note
Version:
Create timestamped markdown notes from the command line
117 lines • 3.92 kB
JavaScript
/**
* CLI entry point for the note command.
*
* @since 0.1.0
*/
import { Args, Command } from "@effect/cli";
import { FileSystem, Path } from "@effect/platform";
import { NodeContext, NodeRuntime } from "@effect/platform-node";
import * as Console from "effect/Console";
import * as Data from "effect/Data";
import * as Effect from "effect/Effect";
import * as Match from "effect/Match";
import { makeContent, makeFilename } from "./dist/esm/Note.js";
import { TitleInput } from "./dist/esm/Validate.js";
/**
* Error when an argument matches an existing file in the current directory.
*
* @since 0.1.0
* @category Errors
*/
export class ExistingArgFile extends /*#__PURE__*/Data.TaggedError("ExistingArgFile") {
get message() {
return `"${this.word}" matches an existing file. You may have confused this tool with another command.`;
}
}
/**
* Error when the target note file already exists.
*
* @since 0.1.0
* @category Errors
*/
export class FileAlreadyExists extends /*#__PURE__*/Data.TaggedError("FileAlreadyExists") {
get message() {
return `File "${this.filename}" already exists. Choose a different title or remove the existing file.`;
}
}
/**
* Error when file write fails.
*
* @since 0.1.0
* @category Errors
*/
export class WriteError extends /*#__PURE__*/Data.TaggedError("WriteError") {
get message() {
return `Failed to write "${this.filename}".`;
}
}
/**
* Format a NoteError for display.
*
* @since 0.1.0
* @category Errors
*/
const formatError = error => Match.value(error).pipe(Match.tag("ExistingArgFile", e => `Error: ${e.message}\nUsage: note <title words...>`), Match.tag("FileAlreadyExists", e => `Error: ${e.message}`), Match.tag("WriteError", e => `Error: ${e.message}`), Match.exhaustive);
/**
* Handle NoteError by printing pretty message and exiting.
*
* @since 0.1.0
* @category Errors
*/
const handleNoteError = error => Console.error(formatError(error)).pipe(Effect.andThen(Effect.sync(() => process.exit(1))));
const titleArgs = /*#__PURE__*/Args.text({
name: "title"
}).pipe(/*#__PURE__*/Args.atLeast(1), /*#__PURE__*/Args.withSchema(TitleInput));
const noteCommand = /*#__PURE__*/Command.make("note", {
title: titleArgs
}, ({
title
}) => Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const cwd = yield* Effect.sync(() => process.cwd());
// Check if any arg matches an existing file
for (const word of title.words) {
const filePath = path.join(cwd, word);
const exists = yield* fs.exists(filePath);
if (exists) {
return yield* new ExistingArgFile({
word
});
}
}
// Generate filename and check if it already exists
const now = new Date();
const filename = makeFilename(title.title, now);
const filePath = path.join(cwd, filename);
const targetExists = yield* fs.exists(filePath);
if (targetExists) {
return yield* new FileAlreadyExists({
filename
});
}
// Create the note
const content = makeContent(title.title, now);
yield* fs.writeFileString(filePath, content).pipe(Effect.mapError(cause => new WriteError({
filename,
cause
})));
yield* Console.log(`\u2705 Created: ${filename}`);
}).pipe(Effect.catchTag("ExistingArgFile", handleNoteError), Effect.catchTag("FileAlreadyExists", handleNoteError), Effect.catchTag("WriteError", handleNoteError))).pipe(/*#__PURE__*/Command.withDescription("Create a timestamped markdown note"));
/**
* Run the CLI with the given arguments.
*
* @since 0.1.0
*/
export const run = args => Command.run(noteCommand, {
name: "note",
version: "0.1.0"
})(args);
// Run if executed directly (not when imported as module for testing)
if (process.argv[1]?.includes("bin")) {
run(process.argv).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain({
disableErrorReporting: true
}));
}
//# sourceMappingURL=bin.js.map