mirror of
https://github.com/foambubble/foam.git
synced 2026-01-11 15:08:01 -05:00
Compare commits
11 Commits
cli/fake-i
...
cli/demo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d1ec88717 | ||
|
|
6b1407303e | ||
|
|
528213a0f8 | ||
|
|
ff89948a70 | ||
|
|
734982996a | ||
|
|
265ab19e31 | ||
|
|
3ef95628f5 | ||
|
|
626f64aec0 | ||
|
|
e36c285764 | ||
|
|
43ac90c3c7 | ||
|
|
7f4f90704d |
@@ -11,6 +11,7 @@
|
||||
"@oclif/command": "^1",
|
||||
"@oclif/config": "^1",
|
||||
"@oclif/plugin-help": "^3",
|
||||
"chalk": "^4.1.0",
|
||||
"@types/inquirer": "^6.5.0",
|
||||
"foam-core": "^0.2.0",
|
||||
"inquirer": "^7.3.2",
|
||||
@@ -64,10 +65,9 @@
|
||||
"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"
|
||||
},
|
||||
"types": "lib/index.d.ts"
|
||||
}
|
||||
}
|
||||
83
packages/foam-cli/src/commands/lint.ts
Normal file
83
packages/foam-cli/src/commands/lint.ts
Normal 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!');
|
||||
}
|
||||
}
|
||||
}
|
||||
63
packages/foam-cli/src/commands/publish.ts
Normal file
63
packages/foam-cli/src/commands/publish.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as fs from 'fs'
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -6,9 +7,9 @@ import * as fs from 'fs'
|
||||
* @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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user