diff --git a/bun.lock b/bun.lock index 8a83128..a8f1e27 100644 --- a/bun.lock +++ b/bun.lock @@ -20,6 +20,7 @@ }, "devDependencies": { "@types/bun": "latest", + "arg": "^5.0.2", "hast": "^1.0.0", "prettier": "^3.5.3", "prettier-plugin-astro": "^0.14.1", @@ -325,6 +326,8 @@ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], diff --git a/package.json b/package.json index ad0495e..8455c24 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,12 @@ "scripts": { "dev": "bunx --bun astro dev", "dist": "bunx --bun astro build", - "preview": "bunx --bun astro preview" + "preview": "bunx --bun astro preview", + "posts": "bun run scripts/postman.ts" }, "devDependencies": { "@types/bun": "latest", + "arg": "^5.0.2", "hast": "^1.0.0", "prettier": "^3.5.3", "prettier-plugin-astro": "^0.14.1", diff --git a/scripts/postman.ts b/scripts/postman.ts new file mode 100644 index 0000000..2bcb934 --- /dev/null +++ b/scripts/postman.ts @@ -0,0 +1,105 @@ +import arg from "arg"; +import paths from "node:path"; +import fs from "node:fs/promises"; + +const globalArgs = arg( + { + "--help": Boolean, + }, + { argv: process.argv.slice(2), stopAtPositional: true }, +); + +if (globalArgs["--help"] || globalArgs._.length === 0) { + process.stdout.write(`postman.ts [...options] + +COMMANDS + +new - Create new post + +OPTIONS +--help - Print this message and exit +`); + + process.exit(); +} + +const command = globalArgs._[0]; +let rest = globalArgs._.slice(1); + +function parseNewPostArgsPartial(rest: string[]) { + return arg( + { + "--draft": Boolean, + "--overwrite": Boolean, + "--help": Boolean, + }, + { stopAtPositional: true, argv: rest }, + ); +} + +const postRoot = paths.resolve(import.meta.dir, "..", "src", "posts"); + +if (command === "new") { + const cfg = parseNewPostArgsPartial(rest); + rest = cfg._.slice(1); + const postId: string = cfg._[0]; + + const hasAdditionalArgs = cfg._.length > 0; + + const partialCfg = parseNewPostArgsPartial(rest); + Object.assign(cfg, partialCfg); + + if (cfg["--help"] || !hasAdditionalArgs) { + process.stdout.write(`postman.ts new [...options] + +Create a new post with default content. + +OPTIONS + +--draft - Create the post as a draft +--overwrite - Overwrite existing file +--help - Print this message and exit +`); + + process.exit(); + } + + if (partialCfg._.length > 0) { + throw new TypeError(`unexpected positional argument "${partialCfg._[0]}"`); + } + + const thisPostRoot = paths.join(postRoot, postId); + + await fs.mkdir(thisPostRoot); + + const postContent = `--- +title: ${postId} +${cfg["--draft"] ? "visibility: draft" : ""} +--- +import More from "~/components/More.astro"; + + +`; + + const thisPostFilename = paths.join(thisPostRoot, "index.mdx"); + + const file = await fs.open( + thisPostFilename, + (cfg["--overwrite"] + ? fs.constants.O_TRUNC + : fs.constants.O_CREAT | fs.constants.O_EXCL) | fs.constants.O_WRONLY, + ); + + try { + await fs.writeFile(file, postContent, { + encoding: "utf-8", + }); + } finally { + await file.close(); + } + + const displayedPath = paths.relative(process.cwd(), thisPostFilename); + process.stdout.write(displayedPath); +} else { + throw new TypeError(`unknown command ${command}`); +}