@mzebley/mark-down-cli 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -0
- package/dist/{index.js → index.mjs} +2 -1
- package/dist/index.mjs.map +1 -0
- package/package.json +6 -6
- package/src/index.ts +1 -1
- package/tsup.config.ts +15 -0
package/dist/index.cjs
CHANGED
|
@@ -324,7 +324,7 @@ async function assertExists(target, message) {
|
|
|
324
324
|
|
|
325
325
|
// src/index.ts
|
|
326
326
|
var program = new import_commander.Command();
|
|
327
|
-
program.name("mark-down").description(`${brand} CLI for building snippet manifests`).version("1.2.
|
|
327
|
+
program.name("mark-down").description(`${brand} CLI for building snippet manifests`).version("1.2.1");
|
|
328
328
|
program.command("build").argument("[sourceDir]", "directory containing snippets", "content/snippets").option("-o, --output <path>", "where to write snippets-index.json").action(async (sourceDir, options) => {
|
|
329
329
|
try {
|
|
330
330
|
const result = await buildManifestFile({ sourceDir, outputPath: options.output });
|
|
@@ -370,3 +370,4 @@ function handleError(error) {
|
|
|
370
370
|
});
|
|
371
371
|
process.exit(1);
|
|
372
372
|
}
|
|
373
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/manifest.ts","../src/errors.ts","../src/watch.ts","../src/logger.ts","../src/compile-page.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { buildManifestFile } from \"./manifest.js\";\nimport { watch as watchSnippets } from \"./watch.js\";\nimport { brand, logEvent } from \"./logger.js\";\nimport { DuplicateSlugError } from \"./errors.js\";\nimport { compilePage } from \"./compile-page.js\";\n\nconst program = new Command();\nprogram\n .name(\"mark-down\")\n .description(`${brand} CLI for building snippet manifests`)\n .version(\"1.2.1\");\n\nprogram\n .command(\"build\")\n .argument(\"[sourceDir]\", \"directory containing snippets\", \"content/snippets\")\n .option(\"-o, --output <path>\", \"where to write snippets-index.json\")\n .action(async (sourceDir: string, options: { output?: string }) => {\n try {\n const result = await buildManifestFile({ sourceDir, outputPath: options.output });\n logEvent(\"info\", \"manifest.written\", {\n outputPath: result.outputPath,\n snippetCount: result.manifest.length\n });\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"watch\")\n .argument(\"[sourceDir]\", \"directory containing snippets\", \"content/snippets\")\n .option(\"-o, --output <path>\", \"where to write snippets-index.json\")\n .action(async (sourceDir: string, options: { output?: string }) => {\n try {\n await watchSnippets(sourceDir, options.output);\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"compile-page\")\n .argument(\"<inputHtml>\", \"HTML file containing data-snippet placeholders\")\n .option(\"--manifest <path>\", \"path to snippets-index.json\")\n .option(\"--outDir <path>\", \"output directory for compiled HTML\", \"dist\")\n .option(\"--inPlace\", \"overwrite the input HTML file instead of writing to outDir\")\n .action(async (inputHtml: string, options: { manifest?: string; outDir?: string; inPlace?: boolean }) => {\n try {\n await compilePage(inputHtml, {\n manifest: options.manifest,\n outDir: options.outDir,\n inPlace: options.inPlace\n });\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram.parseAsync(process.argv).catch(handleError);\n\nfunction handleError(error: unknown) {\n const err = error as Error;\n if (err instanceof DuplicateSlugError) {\n logEvent(\"error\", \"manifest.duplicate_slug\", {\n message: err.message,\n slugs: err.duplicates\n });\n process.exit(2);\n }\n logEvent(\"error\", \"cli.error\", {\n message: err.message,\n stack: err.stack\n });\n process.exit(1);\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport fg from \"fast-glob\";\nimport matter from \"gray-matter\";\nimport YAML from \"yaml\";\nimport { normalizeSlug, type SnippetMeta } from \"@mzebley/mark-down\";\nimport { DuplicateSlugError } from \"./errors.js\";\n\nconst MATTER_OPTIONS = {\n engines: {\n yaml: (source: string) => YAML.parse(source) ?? {}\n }\n};\n\nexport interface BuildOptions {\n sourceDir: string;\n outputPath?: string;\n}\n\nexport interface BuildResult {\n manifest: SnippetMeta[];\n outputPath: string;\n}\n\nexport async function buildManifestFile(options: BuildOptions): Promise<BuildResult> {\n const manifest = await buildManifest(options.sourceDir);\n const target = options.outputPath ?? path.join(options.sourceDir, \"snippets-index.json\");\n await fs.writeFile(target, JSON.stringify(manifest, null, 2));\n return { manifest, outputPath: target };\n}\n\nexport async function buildManifest(sourceDir: string): Promise<SnippetMeta[]> {\n const cwd = path.resolve(sourceDir);\n const files = await fg([\"**/*.md\"], { cwd, absolute: true });\n const manifest: SnippetMeta[] = [];\n\n for (const absolutePath of files) {\n const relativePath = path.relative(cwd, absolutePath);\n const normalizedPath = toPosix(relativePath);\n const content = await fs.readFile(absolutePath, \"utf8\");\n const parsed = matter(content, MATTER_OPTIONS);\n const snippet = createSnippet(normalizedPath, parsed.data ?? {});\n if (snippet.draft) {\n continue;\n }\n manifest.push(snippet);\n }\n\n ensureUniqueSlugs(manifest);\n\n manifest.sort((a, b) => {\n const orderA = typeof a.order === \"number\" ? a.order : Number.POSITIVE_INFINITY;\n const orderB = typeof b.order === \"number\" ? b.order : Number.POSITIVE_INFINITY;\n if (orderA !== orderB) {\n return orderA - orderB;\n }\n const titleA = a.title?.toLowerCase() ?? \"\";\n const titleB = b.title?.toLowerCase() ?? \"\";\n return titleA.localeCompare(titleB);\n });\n\n return manifest;\n}\n\nexport function createSnippet(relativePath: string, frontMatter: Record<string, unknown>): SnippetMeta {\n const group = deriveGroup(relativePath);\n const slugSource = typeof frontMatter.slug === \"string\" && frontMatter.slug.trim().length\n ? frontMatter.slug\n : relativePath.replace(/\\.md$/i, \"\");\n const slug = normalizeSlug(slugSource);\n\n const { title, order, type, tags, draft } = normalizeKnownFields(frontMatter);\n const extra = collectExtra(frontMatter);\n\n return {\n slug,\n title,\n order,\n type,\n tags,\n draft,\n path: relativePath,\n group,\n extra\n };\n}\n\nfunction normalizeKnownFields(data: Record<string, unknown>) {\n return {\n title: typeof data.title === \"string\" ? data.title : undefined,\n order: typeof data.order === \"number\"\n ? data.order\n : data.order === null\n ? null\n : undefined,\n type: typeof data.type === \"string\" ? data.type : undefined,\n tags: normalizeTags(data.tags),\n draft: data.draft === true ? true : undefined\n };\n}\n\nfunction collectExtra(data: Record<string, unknown>): Record<string, unknown> | undefined {\n const extra: Record<string, unknown> = {};\n const reserved = new Set([\"slug\", \"title\", \"order\", \"type\", \"tags\", \"draft\"]);\n for (const [key, value] of Object.entries(data)) {\n if (reserved.has(key)) {\n continue;\n }\n extra[key] = value;\n }\n return Object.keys(extra).length ? extra : undefined;\n}\n\nfunction normalizeTags(value: unknown): string[] | undefined {\n if (!value) {\n return undefined;\n }\n if (Array.isArray(value)) {\n return value.map((entry) => String(entry));\n }\n if (typeof value === \"string\") {\n return value\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n }\n return undefined;\n}\n\nfunction deriveGroup(relativePath: string): string {\n const dirname = toPosix(path.dirname(relativePath));\n if (dirname === \".\" || !dirname.length) {\n return \"root\";\n }\n return dirname;\n}\n\nfunction toPosix(value: string): string {\n return value.split(path.sep).join(\"/\");\n}\n\nfunction ensureUniqueSlugs(manifest: SnippetMeta[]) {\n const seen = new Map<string, string>();\n const duplicates = new Set<string>();\n for (const snippet of manifest) {\n if (seen.has(snippet.slug)) {\n duplicates.add(snippet.slug);\n } else {\n seen.set(snippet.slug, snippet.path);\n }\n }\n if (duplicates.size) {\n throw new DuplicateSlugError([...duplicates.values()]);\n }\n}\n","export class DuplicateSlugError extends Error {\n readonly duplicates: string[];\n\n constructor(duplicates: string[]) {\n super(`Duplicate slugs detected: ${duplicates.join(\", \")}`);\n this.name = \"DuplicateSlugError\";\n this.duplicates = duplicates;\n }\n}\n","import path from \"node:path\";\nimport chokidar from \"chokidar\";\nimport { buildManifestFile, type BuildResult } from \"./manifest.js\";\nimport { logEvent } from \"./logger.js\";\n\nexport async function watch(sourceDir: string, outputPath?: string) {\n const cwd = path.resolve(sourceDir);\n logEvent(\"info\", \"watch.start\", {\n directory: cwd,\n outputPath: outputPath ?? path.join(cwd, \"snippets-index.json\")\n });\n await rebuild(cwd, outputPath);\n\n const watcher = chokidar.watch([\"**/*.md\"], {\n cwd,\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 200,\n pollInterval: 50\n }\n });\n\n const schedule = debounce(async () => {\n await rebuild(cwd, outputPath);\n }, 150);\n\n watcher.on(\"all\", (event, filePath) => {\n logEvent(\"info\", \"watch.change\", { event, file: filePath });\n schedule();\n });\n}\n\nasync function rebuild(sourceDir: string, outputPath?: string): Promise<BuildResult | void> {\n try {\n const result = await buildManifestFile({ sourceDir, outputPath });\n logEvent(\"info\", \"manifest.updated\", {\n outputPath: result.outputPath,\n snippetCount: result.manifest.length\n });\n return result;\n } catch (error) {\n const err = error as Error;\n logEvent(\"error\", \"manifest.update_failed\", {\n message: err.message,\n stack: err.stack\n });\n }\n}\n\nfunction debounce<T extends (...args: unknown[]) => Promise<unknown> | void>(\n fn: T,\n delay: number\n) {\n let timer: NodeJS.Timeout | null = null;\n return (...args: Parameters<T>) => {\n if (timer) {\n clearTimeout(timer);\n }\n timer = setTimeout(() => {\n timer = null;\n void fn(...args);\n }, delay);\n };\n}\n","export const brand = \"mark↓\";\n\nexport type LogLevel = \"info\" | \"warn\" | \"error\";\n\nexport interface LogFields {\n message?: string;\n [key: string]: unknown;\n}\n\nexport function logEvent(level: LogLevel, event: string, fields: LogFields = {}) {\n const entry = {\n brand,\n level,\n event,\n timestamp: new Date().toISOString(),\n ...fields\n };\n const output = `${JSON.stringify(entry)}\\n`;\n const stream = level === \"error\" ? process.stderr : process.stdout;\n stream.write(output);\n}\n\nexport function log(message: string, fields?: LogFields) {\n if (fields) {\n logEvent(\"info\", message, fields);\n return;\n }\n logEvent(\"info\", \"message\", { message });\n}\n\nexport function logError(message: string, fields?: LogFields) {\n if (fields) {\n logEvent(\"error\", message, fields);\n return;\n }\n logEvent(\"error\", \"message\", { message });\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { load as loadHtml } from \"cheerio\";\nimport { parseFrontMatter, renderMarkdown, type SnippetMeta } from \"@mzebley/mark-down\";\nimport { logEvent } from \"./logger.js\";\n\nexport interface CompilePageOptions {\n manifest?: string;\n outDir?: string;\n inPlace?: boolean;\n}\n\nconst DEFAULT_OUT_DIR = \"dist\";\n\nexport async function compilePage(inputHtml: string, options: CompilePageOptions = {}): Promise<string> {\n const sourcePath = path.resolve(inputHtml);\n await assertExists(sourcePath, `Input HTML file not found at '${inputHtml}'.`);\n\n const manifestPath = await resolveManifestPath(sourcePath, options.manifest);\n const manifestDir = path.dirname(manifestPath);\n const manifest = await loadManifest(manifestPath);\n\n const rawHtml = await fs.readFile(sourcePath, \"utf8\");\n const doctypeMatch = rawHtml.match(/^(<!doctype[^>]*>\\s*)/i);\n const doctype = doctypeMatch?.[1] ?? \"\";\n const dom = loadHtml(rawHtml, { decodeEntities: false });\n\n const targets = dom(\"[data-snippet]\").toArray();\n for (const node of targets) {\n const element = dom(node);\n const slug = element.attr(\"data-snippet\");\n if (!slug) {\n continue;\n }\n const entry = manifest.find((item) => item.slug === slug);\n if (!entry) {\n console.warn(`mark↓: no snippet found for \"${slug}\"`);\n continue;\n }\n\n const snippetPath = path.resolve(manifestDir, entry.path);\n let raw: string;\n try {\n raw = await fs.readFile(snippetPath, \"utf8\");\n } catch (error) {\n console.warn(`mark↓: failed to read snippet at '${entry.path}'`, error);\n continue;\n }\n\n let body = raw;\n let frontMatterSlug: string | undefined;\n try {\n const frontMatter = parseFrontMatter(raw);\n body = frontMatter.content;\n frontMatterSlug = frontMatter.slug;\n } catch (error) {\n console.warn(`mark↓: failed to parse front matter for '${entry.path}'`, error);\n }\n\n const html = renderMarkdown(body);\n element.html(html);\n\n if (!element.attr(\"id\")) {\n element.attr(\"id\", frontMatterSlug ?? `snippet-${slug}`);\n }\n }\n\n const outputDir = options.inPlace ? path.dirname(sourcePath) : path.resolve(options.outDir ?? DEFAULT_OUT_DIR);\n if (!options.inPlace) {\n await fs.mkdir(outputDir, { recursive: true });\n }\n const outputPath = options.inPlace\n ? sourcePath\n : path.join(outputDir, path.basename(sourcePath));\n\n const outputHtml = `${doctype}${dom.html() ?? \"\"}`;\n await fs.writeFile(outputPath, outputHtml);\n\n logEvent(\"info\", \"compile_page.written\", { outputPath });\n return outputPath;\n}\n\nasync function resolveManifestPath(inputHtml: string, manifestFlag?: string): Promise<string> {\n const manifestPath = manifestFlag\n ? path.resolve(manifestFlag)\n : path.join(path.dirname(path.resolve(inputHtml)), \"snippets-index.json\");\n await assertExists(manifestPath, `Manifest file not found at '${manifestPath}'.`);\n return manifestPath;\n}\n\nasync function loadManifest(manifestPath: string): Promise<SnippetMeta[]> {\n let raw: string;\n try {\n raw = await fs.readFile(manifestPath, \"utf8\");\n } catch (error) {\n throw new Error(`Failed to read manifest at '${manifestPath}': ${String(error)}`);\n }\n\n try {\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n throw new Error(\"Manifest must be a JSON array.\");\n }\n return parsed as SnippetMeta[];\n } catch (error) {\n throw new Error(`Failed to parse manifest at '${manifestPath}': ${String(error)}`);\n }\n}\n\nasync function assertExists(target: string, message: string) {\n try {\n await fs.access(target);\n } catch {\n throw new Error(message);\n }\n}\n"],"mappings":"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","names":["YAML","path","fs","fg","matter","import_node_path","path","chokidar","import_promises","import_node_path","import_mark_down","path","fs","loadHtml"]}
|
|
@@ -301,7 +301,7 @@ async function assertExists(target, message) {
|
|
|
301
301
|
|
|
302
302
|
// src/index.ts
|
|
303
303
|
var program = new Command();
|
|
304
|
-
program.name("mark-down").description(`${brand} CLI for building snippet manifests`).version("1.2.
|
|
304
|
+
program.name("mark-down").description(`${brand} CLI for building snippet manifests`).version("1.2.1");
|
|
305
305
|
program.command("build").argument("[sourceDir]", "directory containing snippets", "content/snippets").option("-o, --output <path>", "where to write snippets-index.json").action(async (sourceDir, options) => {
|
|
306
306
|
try {
|
|
307
307
|
const result = await buildManifestFile({ sourceDir, outputPath: options.output });
|
|
@@ -347,3 +347,4 @@ function handleError(error) {
|
|
|
347
347
|
});
|
|
348
348
|
process.exit(1);
|
|
349
349
|
}
|
|
350
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/manifest.ts","../src/errors.ts","../src/watch.ts","../src/logger.ts","../src/compile-page.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { buildManifestFile } from \"./manifest.js\";\nimport { watch as watchSnippets } from \"./watch.js\";\nimport { brand, logEvent } from \"./logger.js\";\nimport { DuplicateSlugError } from \"./errors.js\";\nimport { compilePage } from \"./compile-page.js\";\n\nconst program = new Command();\nprogram\n .name(\"mark-down\")\n .description(`${brand} CLI for building snippet manifests`)\n .version(\"1.2.1\");\n\nprogram\n .command(\"build\")\n .argument(\"[sourceDir]\", \"directory containing snippets\", \"content/snippets\")\n .option(\"-o, --output <path>\", \"where to write snippets-index.json\")\n .action(async (sourceDir: string, options: { output?: string }) => {\n try {\n const result = await buildManifestFile({ sourceDir, outputPath: options.output });\n logEvent(\"info\", \"manifest.written\", {\n outputPath: result.outputPath,\n snippetCount: result.manifest.length\n });\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"watch\")\n .argument(\"[sourceDir]\", \"directory containing snippets\", \"content/snippets\")\n .option(\"-o, --output <path>\", \"where to write snippets-index.json\")\n .action(async (sourceDir: string, options: { output?: string }) => {\n try {\n await watchSnippets(sourceDir, options.output);\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram\n .command(\"compile-page\")\n .argument(\"<inputHtml>\", \"HTML file containing data-snippet placeholders\")\n .option(\"--manifest <path>\", \"path to snippets-index.json\")\n .option(\"--outDir <path>\", \"output directory for compiled HTML\", \"dist\")\n .option(\"--inPlace\", \"overwrite the input HTML file instead of writing to outDir\")\n .action(async (inputHtml: string, options: { manifest?: string; outDir?: string; inPlace?: boolean }) => {\n try {\n await compilePage(inputHtml, {\n manifest: options.manifest,\n outDir: options.outDir,\n inPlace: options.inPlace\n });\n } catch (error) {\n handleError(error);\n }\n });\n\nprogram.parseAsync(process.argv).catch(handleError);\n\nfunction handleError(error: unknown) {\n const err = error as Error;\n if (err instanceof DuplicateSlugError) {\n logEvent(\"error\", \"manifest.duplicate_slug\", {\n message: err.message,\n slugs: err.duplicates\n });\n process.exit(2);\n }\n logEvent(\"error\", \"cli.error\", {\n message: err.message,\n stack: err.stack\n });\n process.exit(1);\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport fg from \"fast-glob\";\nimport matter from \"gray-matter\";\nimport YAML from \"yaml\";\nimport { normalizeSlug, type SnippetMeta } from \"@mzebley/mark-down\";\nimport { DuplicateSlugError } from \"./errors.js\";\n\nconst MATTER_OPTIONS = {\n engines: {\n yaml: (source: string) => YAML.parse(source) ?? {}\n }\n};\n\nexport interface BuildOptions {\n sourceDir: string;\n outputPath?: string;\n}\n\nexport interface BuildResult {\n manifest: SnippetMeta[];\n outputPath: string;\n}\n\nexport async function buildManifestFile(options: BuildOptions): Promise<BuildResult> {\n const manifest = await buildManifest(options.sourceDir);\n const target = options.outputPath ?? path.join(options.sourceDir, \"snippets-index.json\");\n await fs.writeFile(target, JSON.stringify(manifest, null, 2));\n return { manifest, outputPath: target };\n}\n\nexport async function buildManifest(sourceDir: string): Promise<SnippetMeta[]> {\n const cwd = path.resolve(sourceDir);\n const files = await fg([\"**/*.md\"], { cwd, absolute: true });\n const manifest: SnippetMeta[] = [];\n\n for (const absolutePath of files) {\n const relativePath = path.relative(cwd, absolutePath);\n const normalizedPath = toPosix(relativePath);\n const content = await fs.readFile(absolutePath, \"utf8\");\n const parsed = matter(content, MATTER_OPTIONS);\n const snippet = createSnippet(normalizedPath, parsed.data ?? {});\n if (snippet.draft) {\n continue;\n }\n manifest.push(snippet);\n }\n\n ensureUniqueSlugs(manifest);\n\n manifest.sort((a, b) => {\n const orderA = typeof a.order === \"number\" ? a.order : Number.POSITIVE_INFINITY;\n const orderB = typeof b.order === \"number\" ? b.order : Number.POSITIVE_INFINITY;\n if (orderA !== orderB) {\n return orderA - orderB;\n }\n const titleA = a.title?.toLowerCase() ?? \"\";\n const titleB = b.title?.toLowerCase() ?? \"\";\n return titleA.localeCompare(titleB);\n });\n\n return manifest;\n}\n\nexport function createSnippet(relativePath: string, frontMatter: Record<string, unknown>): SnippetMeta {\n const group = deriveGroup(relativePath);\n const slugSource = typeof frontMatter.slug === \"string\" && frontMatter.slug.trim().length\n ? frontMatter.slug\n : relativePath.replace(/\\.md$/i, \"\");\n const slug = normalizeSlug(slugSource);\n\n const { title, order, type, tags, draft } = normalizeKnownFields(frontMatter);\n const extra = collectExtra(frontMatter);\n\n return {\n slug,\n title,\n order,\n type,\n tags,\n draft,\n path: relativePath,\n group,\n extra\n };\n}\n\nfunction normalizeKnownFields(data: Record<string, unknown>) {\n return {\n title: typeof data.title === \"string\" ? data.title : undefined,\n order: typeof data.order === \"number\"\n ? data.order\n : data.order === null\n ? null\n : undefined,\n type: typeof data.type === \"string\" ? data.type : undefined,\n tags: normalizeTags(data.tags),\n draft: data.draft === true ? true : undefined\n };\n}\n\nfunction collectExtra(data: Record<string, unknown>): Record<string, unknown> | undefined {\n const extra: Record<string, unknown> = {};\n const reserved = new Set([\"slug\", \"title\", \"order\", \"type\", \"tags\", \"draft\"]);\n for (const [key, value] of Object.entries(data)) {\n if (reserved.has(key)) {\n continue;\n }\n extra[key] = value;\n }\n return Object.keys(extra).length ? extra : undefined;\n}\n\nfunction normalizeTags(value: unknown): string[] | undefined {\n if (!value) {\n return undefined;\n }\n if (Array.isArray(value)) {\n return value.map((entry) => String(entry));\n }\n if (typeof value === \"string\") {\n return value\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n }\n return undefined;\n}\n\nfunction deriveGroup(relativePath: string): string {\n const dirname = toPosix(path.dirname(relativePath));\n if (dirname === \".\" || !dirname.length) {\n return \"root\";\n }\n return dirname;\n}\n\nfunction toPosix(value: string): string {\n return value.split(path.sep).join(\"/\");\n}\n\nfunction ensureUniqueSlugs(manifest: SnippetMeta[]) {\n const seen = new Map<string, string>();\n const duplicates = new Set<string>();\n for (const snippet of manifest) {\n if (seen.has(snippet.slug)) {\n duplicates.add(snippet.slug);\n } else {\n seen.set(snippet.slug, snippet.path);\n }\n }\n if (duplicates.size) {\n throw new DuplicateSlugError([...duplicates.values()]);\n }\n}\n","export class DuplicateSlugError extends Error {\n readonly duplicates: string[];\n\n constructor(duplicates: string[]) {\n super(`Duplicate slugs detected: ${duplicates.join(\", \")}`);\n this.name = \"DuplicateSlugError\";\n this.duplicates = duplicates;\n }\n}\n","import path from \"node:path\";\nimport chokidar from \"chokidar\";\nimport { buildManifestFile, type BuildResult } from \"./manifest.js\";\nimport { logEvent } from \"./logger.js\";\n\nexport async function watch(sourceDir: string, outputPath?: string) {\n const cwd = path.resolve(sourceDir);\n logEvent(\"info\", \"watch.start\", {\n directory: cwd,\n outputPath: outputPath ?? path.join(cwd, \"snippets-index.json\")\n });\n await rebuild(cwd, outputPath);\n\n const watcher = chokidar.watch([\"**/*.md\"], {\n cwd,\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 200,\n pollInterval: 50\n }\n });\n\n const schedule = debounce(async () => {\n await rebuild(cwd, outputPath);\n }, 150);\n\n watcher.on(\"all\", (event, filePath) => {\n logEvent(\"info\", \"watch.change\", { event, file: filePath });\n schedule();\n });\n}\n\nasync function rebuild(sourceDir: string, outputPath?: string): Promise<BuildResult | void> {\n try {\n const result = await buildManifestFile({ sourceDir, outputPath });\n logEvent(\"info\", \"manifest.updated\", {\n outputPath: result.outputPath,\n snippetCount: result.manifest.length\n });\n return result;\n } catch (error) {\n const err = error as Error;\n logEvent(\"error\", \"manifest.update_failed\", {\n message: err.message,\n stack: err.stack\n });\n }\n}\n\nfunction debounce<T extends (...args: unknown[]) => Promise<unknown> | void>(\n fn: T,\n delay: number\n) {\n let timer: NodeJS.Timeout | null = null;\n return (...args: Parameters<T>) => {\n if (timer) {\n clearTimeout(timer);\n }\n timer = setTimeout(() => {\n timer = null;\n void fn(...args);\n }, delay);\n };\n}\n","export const brand = \"mark↓\";\n\nexport type LogLevel = \"info\" | \"warn\" | \"error\";\n\nexport interface LogFields {\n message?: string;\n [key: string]: unknown;\n}\n\nexport function logEvent(level: LogLevel, event: string, fields: LogFields = {}) {\n const entry = {\n brand,\n level,\n event,\n timestamp: new Date().toISOString(),\n ...fields\n };\n const output = `${JSON.stringify(entry)}\\n`;\n const stream = level === \"error\" ? process.stderr : process.stdout;\n stream.write(output);\n}\n\nexport function log(message: string, fields?: LogFields) {\n if (fields) {\n logEvent(\"info\", message, fields);\n return;\n }\n logEvent(\"info\", \"message\", { message });\n}\n\nexport function logError(message: string, fields?: LogFields) {\n if (fields) {\n logEvent(\"error\", message, fields);\n return;\n }\n logEvent(\"error\", \"message\", { message });\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { load as loadHtml } from \"cheerio\";\nimport { parseFrontMatter, renderMarkdown, type SnippetMeta } from \"@mzebley/mark-down\";\nimport { logEvent } from \"./logger.js\";\n\nexport interface CompilePageOptions {\n manifest?: string;\n outDir?: string;\n inPlace?: boolean;\n}\n\nconst DEFAULT_OUT_DIR = \"dist\";\n\nexport async function compilePage(inputHtml: string, options: CompilePageOptions = {}): Promise<string> {\n const sourcePath = path.resolve(inputHtml);\n await assertExists(sourcePath, `Input HTML file not found at '${inputHtml}'.`);\n\n const manifestPath = await resolveManifestPath(sourcePath, options.manifest);\n const manifestDir = path.dirname(manifestPath);\n const manifest = await loadManifest(manifestPath);\n\n const rawHtml = await fs.readFile(sourcePath, \"utf8\");\n const doctypeMatch = rawHtml.match(/^(<!doctype[^>]*>\\s*)/i);\n const doctype = doctypeMatch?.[1] ?? \"\";\n const dom = loadHtml(rawHtml, { decodeEntities: false });\n\n const targets = dom(\"[data-snippet]\").toArray();\n for (const node of targets) {\n const element = dom(node);\n const slug = element.attr(\"data-snippet\");\n if (!slug) {\n continue;\n }\n const entry = manifest.find((item) => item.slug === slug);\n if (!entry) {\n console.warn(`mark↓: no snippet found for \"${slug}\"`);\n continue;\n }\n\n const snippetPath = path.resolve(manifestDir, entry.path);\n let raw: string;\n try {\n raw = await fs.readFile(snippetPath, \"utf8\");\n } catch (error) {\n console.warn(`mark↓: failed to read snippet at '${entry.path}'`, error);\n continue;\n }\n\n let body = raw;\n let frontMatterSlug: string | undefined;\n try {\n const frontMatter = parseFrontMatter(raw);\n body = frontMatter.content;\n frontMatterSlug = frontMatter.slug;\n } catch (error) {\n console.warn(`mark↓: failed to parse front matter for '${entry.path}'`, error);\n }\n\n const html = renderMarkdown(body);\n element.html(html);\n\n if (!element.attr(\"id\")) {\n element.attr(\"id\", frontMatterSlug ?? `snippet-${slug}`);\n }\n }\n\n const outputDir = options.inPlace ? path.dirname(sourcePath) : path.resolve(options.outDir ?? DEFAULT_OUT_DIR);\n if (!options.inPlace) {\n await fs.mkdir(outputDir, { recursive: true });\n }\n const outputPath = options.inPlace\n ? sourcePath\n : path.join(outputDir, path.basename(sourcePath));\n\n const outputHtml = `${doctype}${dom.html() ?? \"\"}`;\n await fs.writeFile(outputPath, outputHtml);\n\n logEvent(\"info\", \"compile_page.written\", { outputPath });\n return outputPath;\n}\n\nasync function resolveManifestPath(inputHtml: string, manifestFlag?: string): Promise<string> {\n const manifestPath = manifestFlag\n ? path.resolve(manifestFlag)\n : path.join(path.dirname(path.resolve(inputHtml)), \"snippets-index.json\");\n await assertExists(manifestPath, `Manifest file not found at '${manifestPath}'.`);\n return manifestPath;\n}\n\nasync function loadManifest(manifestPath: string): Promise<SnippetMeta[]> {\n let raw: string;\n try {\n raw = await fs.readFile(manifestPath, \"utf8\");\n } catch (error) {\n throw new Error(`Failed to read manifest at '${manifestPath}': ${String(error)}`);\n }\n\n try {\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n throw new Error(\"Manifest must be a JSON array.\");\n }\n return parsed as SnippetMeta[];\n } catch (error) {\n throw new Error(`Failed to parse manifest at '${manifestPath}': ${String(error)}`);\n }\n}\n\nasync function assertExists(target: string, message: string) {\n try {\n await fs.access(target);\n } catch {\n throw new Error(message);\n }\n}\n"],"mappings":"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","names":["path","path","fs","path","path","fs"]}
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mzebley/mark-down-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "mark↓ CLI for building snippet manifests",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"mark-down": "dist/index.
|
|
7
|
+
"mark-down": "dist/index.mjs"
|
|
8
8
|
},
|
|
9
|
-
"main": "dist/index.
|
|
9
|
+
"main": "dist/index.mjs",
|
|
10
10
|
"module": "dist/index.mjs",
|
|
11
11
|
"types": "dist/index.d.ts",
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "tsup
|
|
17
|
-
"dev": "tsup
|
|
16
|
+
"build": "tsup --config tsup.config.ts",
|
|
17
|
+
"dev": "tsup --config tsup.config.ts --watch"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@mzebley/mark-down": "
|
|
20
|
+
"@mzebley/mark-down": "^1.2.1",
|
|
21
21
|
"cheerio": "^1.0.0",
|
|
22
22
|
"chokidar": "^3.6.0",
|
|
23
23
|
"commander": "^11.1.0",
|
package/src/index.ts
CHANGED
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ["src/index.ts"],
|
|
5
|
+
format: ["esm", "cjs"],
|
|
6
|
+
dts: true,
|
|
7
|
+
sourcemap: true,
|
|
8
|
+
splitting: false,
|
|
9
|
+
clean: true,
|
|
10
|
+
outExtension({ format }) {
|
|
11
|
+
return {
|
|
12
|
+
js: format === "esm" ? ".mjs" : ".cjs"
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
});
|