Compare commits

..

6 Commits

Author SHA1 Message Date
Jani Eväkallio
265ab19e31 Prototype basic publish command (just pushes all changes to git) 2020-07-16 11:57:47 +01:00
Ankit Tiwari
3ef95628f5 Remove posttest command 2020-07-16 13:33:18 +05:30
Ankit Tiwari
626f64aec0 Propagate the error from writeFileToDisk method 2020-07-16 13:32:30 +05:30
Ankit Tiwari
e36c285764 Refactor renameFile to use path module 2020-07-16 13:30:26 +05:30
Ankit Tiwari
20ca92f451 Make fs tests no blocking by using promises 2020-07-16 12:12:50 +05:30
Ankit Tiwari
4557150378 Use Promise.resolve(null) 2020-07-16 11:50:41 +05:30
10 changed files with 85 additions and 108 deletions

View File

@@ -11,7 +11,6 @@
"@oclif/command": "^1",
"@oclif/config": "^1",
"@oclif/plugin-help": "^3",
"chalk": "^4.1.0",
"foam-core": "^0.2.0",
"ora": "^4.0.4",
"tslib": "^1"
@@ -63,7 +62,6 @@
"scripts": {
"cli": "./bin/run",
"postpack": "rm -f oclif.manifest.json",
"posttest": "eslint . --ext .ts --config .eslintrc",
"prepack": "rm -rf lib && tsc -b && oclif-dev manifest && oclif-dev readme",
"test": "jest",
"version": "oclif-dev readme && git add README.md"

View File

@@ -59,7 +59,7 @@ Successfully generated link references and heading!
return writeFileToDisk(note.path, file);
}
return null;
return Promise.resolve(null);
})
await Promise.all(fileWritePromises);

View File

@@ -1,83 +0,0 @@
import { Command, flags } from '@oclif/command';
import * as ora from 'ora';
import * as chalk from 'chalk';
import { initializeNoteGraph, Note, NoteLink } from 'foam-core';
import * as fs from 'fs'
interface InvalidReference {
note: Note,
link: NoteLink
}
export default class Lint extends Command {
static description = 'Shows lint errors and warnings across all the markdown files in the given workspaces';
// TODO:
static examples = [
`$ foam-cli Lint path-to-foam-workspace
Successfully generated link references and heading!
`,
]
static flags = {
help: flags.help({ char: 'h' }),
}
static args = [{ name: 'workspacePath' }]
async run() {
const spinner = ora('Reading Files').start();
const { args, flags } = this.parse(Lint)
const { workspacePath = './' } = args;
if (fs.existsSync(workspacePath) && fs.lstatSync(workspacePath).isDirectory()) {
const graph = await initializeNoteGraph(workspacePath);
const notes = graph.getNotes();
spinner.text = `${notes.length} files found`;
// TODO: Figure out why there is an undefined note
const orphanedNotes = notes.filter(note => note && graph.getBacklinks(note.id).length === 0);
// Find out invalid references
// ⚠️ Warning: Dirty code ahead. This is just a proof of concept.
// @ts-ignore
const invalidLinks: InvalidReference[] = notes.filter(Boolean).map(note => {
return graph
.getNoteLinks(note.id)
.map(link => {
const target = graph.getNote(link.to);
return !target ? { note: note, link: link } : false;
})
.filter(Boolean)
}).reduce((acc, curr) => ([...acc, ...curr]), []) // flatten the array
const warnings = `${orphanedNotes.map(note => {
return `→ "${note.title}" is an orphan note.`;
}).join('\n')}`;
const errors = `${invalidLinks.map(item => {
return `→ Link "${item.link.to}" in "${item.note.title}" points to a non-existing note [${item.link.position.start.line}, ${item.link.position.start.column}] `;
}).join('\n')}`;
spinner.stop()
this.log(chalk.yellowBright('⚠️ Warnings:'));
this.log(warnings);
this.log(chalk.redBright('❌ Errors:'));
console.log(errors);
}
else {
spinner.fail('Directory does not exist!');
}
}
}

View File

@@ -49,7 +49,7 @@ Successfully generated link references and heading!
if (kebabCasedFileName) {
return renameFile(note.path, kebabCasedFileName);
}
return null;
return Promise.resolve(null);
})
await Promise.all(fileRename);
@@ -80,7 +80,7 @@ Successfully generated link references and heading!
return writeFileToDisk(note.path, file);
}
return null;
return Promise.resolve(null);
}))
await Promise.all(fileWritePromises);

View File

@@ -0,0 +1,63 @@
import {Command, flags} from '@oclif/command'
import { execSync } from 'child_process';
import * as ora from 'ora';
export default class Publish extends Command {
static description = 'Push all changes to git repository';
static examples = [
`$ foam publish -m "Optional log message"`,
]
static flags = {
message: flags.string({
char: 'm',
description: "optional message"
}),
remote: flags.string({
char: 'r',
description: "remote"
}),
branch: flags.string({
char: 'b',
description: "branch"
})
}
async execWithSpinner(command: string, message: string) {
const spinner = ora(message).start();
// @todo handle errors
const response = execSync(command).toString();
spinner.succeed(`${message} Done!`);
return response;
}
async printPublishInfo(remote: string) {
// @todo actually get this data from GH API
const [, remotePath] = execSync(`git remote get-url ${remote}`).toString().trim().split(':');
const [repo, org] = remotePath.split('/').reverse();
console.log('');
console.log(`🎉 Your changes will be available shortly at https://${org}.github.io/${repo.replace('.git', '')}`);
console.log('');
}
async run() {
const {flags} = this.parse(Publish);
// @todo improve
const message = flags.message || 'foam publish';
const remote = flags.remote || 'origin';
const branch = flags.branch || 'master';
await this.execWithSpinner(`git add -A`, 'Staging changes...');
await this.execWithSpinner(`git commit -m "${message}"`, 'Creating a commit...');
await this.execWithSpinner(`git push ${remote} ${branch}`, "Publishing...");
await this.printPublishInfo(remote);
}
}

View File

@@ -1,9 +1,15 @@
import * as fs from 'fs'
import * as fs from 'fs';
import * as path from 'path';
/**
*
* @param fileUri absolute path for the file that needs to renamed
* @param newFileName "new file name" without the extension
*/
export const renameFile = async (fileUri: string, newFileName: string) => {
const fileName = fileUri.split('/').pop();
const extension = fileName?.split('.').pop();
const newFileUri = fileUri.replace(`${fileName}`, `${newFileName}.${extension}`);
const dirName = path.dirname(fileUri);
const extension = path.extname(fileUri);
const newFileUri = path.join(dirName, `${newFileName}${extension}`);
return fs.promises.rename(fileUri, newFileUri);
}

View File

@@ -1,8 +1,5 @@
import * as fs from 'fs';
export const writeFileToDisk = async (fileUri: string, data: string): Promise<Boolean> => {
return fs.promises.writeFile(fileUri, data).then(() => true).catch(err => {
console.log('error while writing to file: ', err)
return false;
})
export const writeFileToDisk = async (fileUri: string, data: string) => {
return fs.promises.writeFile(fileUri, data);
}

View File

@@ -3,6 +3,8 @@ import { renameFile } from '../src/utils/rename-file'
import * as fs from 'fs';
import mockFS from 'mock-fs';
const doesFileExist = (path) => fs.promises.access(path).then(() => true).catch(() => false);
describe('renameFile', () => {
const fileUri = './test/oldFileName.md';
@@ -16,10 +18,11 @@ describe('renameFile', () => {
});
it('should rename existing file', async () => {
expect(fs.existsSync(fileUri)).toBe(true);
expect(await doesFileExist(fileUri)).toBe(true);
renameFile(fileUri, 'new-file-name');
expect(fs.existsSync(fileUri)).toBe(false);
expect(fs.existsSync('./test/new-file-name.md')).toBe(true);
expect(await doesFileExist(fileUri)).toBe(false);
expect(await doesFileExist('./test/new-file-name.md')).toBe(true);
});
});

View File

@@ -18,7 +18,7 @@ describe('writeFileToDisk', () => {
it('should overrwrite existing file in the disk with the new data', async () => {
const expected = `content in the new file`;
await writeFileToDisk(fileUri, expected);
const actual = fs.readFileSync(fileUri, { encoding: 'utf8' });
const actual = await fs.promises.readFile(fileUri, { encoding: 'utf8' });
expect(actual).toBe(expected);
});
})

View File

@@ -85,13 +85,6 @@ export class NoteGraph {
throw new Error(`Note with ID [${noteId}] not found`);
}
// Note: This is temporary until we figure out how to put
// position inside Link (needed for linting)
public getNoteLinks(noteId: ID): NoteLink[] {
const note = this.getNote(noteId);
return note ? note.links : [];
}
public getAllLinks(noteId: ID): Link[] {
return (this.graph.nodeEdges(noteId) || []).map(edge =>
convertEdgeToLink(edge, this.graph)