Compare commits

...

2 Commits

Author SHA1 Message Date
Ankit Tiwari
43ac90c3c7 Merge branch 'cli/apply-text-edit' into cli/lint 2020-07-16 11:15:04 +05:30
Ankit Tiwari
7f4f90704d Implement basic lint command 2020-07-15 21:07:07 +05:30
3 changed files with 92 additions and 1 deletions

View File

@@ -11,6 +11,7 @@
"@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"
@@ -68,4 +69,4 @@
"version": "oclif-dev readme && git add README.md"
},
"types": "lib/index.d.ts"
}
}

View File

@@ -0,0 +1,83 @@
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

@@ -85,6 +85,13 @@ 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)