noblog 0.2.3 → 0.3.0-beta.2

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.
Files changed (43) hide show
  1. package/README.md +280 -121
  2. package/dist/client-CzKR81Or.js +551 -0
  3. package/dist/client-CzKR81Or.js.map +1 -0
  4. package/dist/config-check.impl-C5FM7uWi.js +14 -0
  5. package/dist/config-check.impl-C5FM7uWi.js.map +1 -0
  6. package/dist/config-csbquMhF.js +21 -0
  7. package/dist/config-csbquMhF.js.map +1 -0
  8. package/dist/config-file-C07nmm45.js +27 -0
  9. package/dist/config-file-C07nmm45.js.map +1 -0
  10. package/dist/defaults-RGOB9tfi.js +10 -0
  11. package/dist/defaults-RGOB9tfi.js.map +1 -0
  12. package/dist/errors-BCRhKbgG.js +73 -0
  13. package/dist/errors-BCRhKbgG.js.map +1 -0
  14. package/dist/frontmatter-fields-BkXq38CO.js +71 -0
  15. package/dist/frontmatter-fields-BkXq38CO.js.map +1 -0
  16. package/dist/generate.d.ts +1 -0
  17. package/dist/generate.impl-ChFrtwtC.js +34 -0
  18. package/dist/generate.impl-ChFrtwtC.js.map +1 -0
  19. package/dist/generate.js +198 -940
  20. package/dist/generate.js.map +1 -1
  21. package/dist/index.d.ts +213 -18
  22. package/dist/index.js +5 -0
  23. package/package.json +32 -17
  24. package/schema/config.json +178 -0
  25. package/skills/noblog-config/SKILL.md +40 -0
  26. package/skills/noblog-config/assets/nobloger.yaml +30 -0
  27. package/skills/noblog-config/assets/thought-lite.yaml +47 -0
  28. package/skills/noblog-config/references/config-schema.md +110 -0
  29. package/skills/noblog-config/references/notion-property-types.md +42 -0
  30. package/skills/noblog-config/references/theme-examples.md +41 -0
  31. package/dist/cmds/generate.d.ts +0 -3
  32. package/dist/cmds/generate.d.ts.map +0 -1
  33. package/dist/generate.cjs +0 -970
  34. package/dist/generate.cjs.map +0 -1
  35. package/dist/index.d.ts.map +0 -1
  36. package/dist/lib/noblog/format.d.ts +0 -24
  37. package/dist/lib/noblog/format.d.ts.map +0 -1
  38. package/dist/lib/noblog/noblog.d.ts +0 -31
  39. package/dist/lib/noblog/noblog.d.ts.map +0 -1
  40. package/dist/lib/noblog/utils.d.ts +0 -36
  41. package/dist/lib/noblog/utils.d.ts.map +0 -1
  42. package/dist/lib/notion/client.d.ts +0 -8
  43. package/dist/lib/notion/client.d.ts.map +0 -1
@@ -0,0 +1,14 @@
1
+ import { t as loadNoblogConfig } from "./config-file-C07nmm45.js";
2
+ //#region src/commands/config-check.impl.ts
3
+ async function checkConfig(flags) {
4
+ try {
5
+ await loadNoblogConfig(flags.config);
6
+ this.process.stdout.write(`Configuration valid: ${flags.config}\n`);
7
+ } catch (error) {
8
+ return error instanceof Error ? error : /* @__PURE__ */ new Error("Configuration check failed: an unknown error occurred");
9
+ }
10
+ }
11
+ //#endregion
12
+ export { checkConfig as default };
13
+
14
+ //# sourceMappingURL=config-check.impl-C5FM7uWi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-check.impl-C5FM7uWi.js","names":[],"sources":["../src/commands/config-check.impl.ts"],"sourcesContent":["import { loadNoblogConfig } from \"../config-file.js\";\nimport type { CliContext } from \"../context.js\";\n\ninterface ConfigCheckFlags {\n config: string;\n}\n\nexport default async function checkConfig(\n this: CliContext,\n flags: ConfigCheckFlags,\n): Promise<Error | void> {\n try {\n await loadNoblogConfig(flags.config);\n this.process.stdout.write(`Configuration valid: ${flags.config}\\n`);\n } catch (error) {\n return error instanceof Error\n ? error\n : new Error(\"Configuration check failed: an unknown error occurred\");\n }\n}\n"],"mappings":";;AAOA,eAA8B,YAE5B,OACuB;CACvB,IAAI;EACF,MAAM,iBAAiB,MAAM,MAAM;EACnC,KAAK,QAAQ,OAAO,MAAM,wBAAwB,MAAM,OAAO,GAAG;CACpE,SAAS,OAAO;EACd,OAAO,iBAAiB,QACpB,wBACA,IAAI,MAAM,uDAAuD;CACvE;AACF"}
@@ -0,0 +1,21 @@
1
+ //#region src/config.ts
2
+ var ConfigurationError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "ConfigurationError";
6
+ }
7
+ };
8
+ function loadConfig(env = {}) {
9
+ const notionApiSecret = env["NOTION_API_SECRET"];
10
+ const databaseId = env["NOBLOG_DATABASE_ID"];
11
+ const missing = [["NOTION_API_SECRET", notionApiSecret], ["NOBLOG_DATABASE_ID", databaseId]].filter(([, value]) => !value).map(([name]) => name);
12
+ if (!notionApiSecret || !databaseId) throw new ConfigurationError(`Missing required environment ${missing.length === 1 ? "variable" : "variables"}: ${missing.join(", ")}.\nSet them in your .env file or export them before running noblog.`);
13
+ return {
14
+ notionApiSecret,
15
+ databaseId
16
+ };
17
+ }
18
+ //#endregion
19
+ export { loadConfig as n, ConfigurationError as t };
20
+
21
+ //# sourceMappingURL=config-csbquMhF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-csbquMhF.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["export interface NoblogConfig {\n notionApiSecret: string;\n databaseId: string;\n}\n\nexport class ConfigurationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigurationError\";\n }\n}\n\nexport function loadConfig(\n env: Readonly<Partial<Record<string, string>>> = {},\n): NoblogConfig {\n const notionApiSecret = env[\"NOTION_API_SECRET\"];\n const databaseId = env[\"NOBLOG_DATABASE_ID\"];\n const missing = [\n [\"NOTION_API_SECRET\", notionApiSecret],\n [\"NOBLOG_DATABASE_ID\", databaseId],\n ]\n .filter(([, value]) => !value)\n .map(([name]) => name);\n\n if (!notionApiSecret || !databaseId) {\n throw new ConfigurationError(\n `Missing required environment ${missing.length === 1 ? \"variable\" : \"variables\"}: ${missing.join(\", \")}.\\n` +\n \"Set them in your .env file or export them before running noblog.\",\n );\n }\n\n return { notionApiSecret, databaseId };\n}\n"],"mappings":";AAKA,IAAa,qBAAb,cAAwC,MAAM;CAC5C,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;AAEA,SAAgB,WACd,MAAiD,CAAC,GACpC;CACd,MAAM,kBAAkB,IAAI;CAC5B,MAAM,aAAa,IAAI;CACvB,MAAM,UAAU,CACd,CAAC,qBAAqB,eAAe,GACrC,CAAC,sBAAsB,UAAU,CACnC,CAAC,CACE,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAC7B,KAAK,CAAC,UAAU,IAAI;CAEvB,IAAI,CAAC,mBAAmB,CAAC,YACvB,MAAM,IAAI,mBACR,gCAAgC,QAAQ,WAAW,IAAI,aAAa,YAAY,IAAI,QAAQ,KAAK,IAAI,EAAE,oEAEzG;CAGF,OAAO;EAAE;EAAiB;CAAW;AACvC"}
@@ -0,0 +1,27 @@
1
+ import { t as ConfigurationError } from "./config-csbquMhF.js";
2
+ import { a as noblogConfigSchema, t as getErrorCode } from "./errors-BCRhKbgG.js";
3
+ import { z } from "zod";
4
+ import { parse } from "yaml";
5
+ import { readFile } from "node:fs/promises";
6
+ //#region src/config-file.ts
7
+ async function loadNoblogConfig(path) {
8
+ let source;
9
+ try {
10
+ source = await readFile(path, "utf8");
11
+ } catch (error) {
12
+ throw new ConfigurationError(`Cannot read Noblog config "${path}": ${getErrorCode(error) === "ENOENT" ? "file not found" : error instanceof Error ? error.message : "unknown read error"}`);
13
+ }
14
+ let input;
15
+ try {
16
+ input = parse(source, { prettyErrors: false });
17
+ } catch (error) {
18
+ throw new ConfigurationError(`Invalid YAML in Noblog config "${path}": ${error instanceof Error ? error.message : "unknown parse error"}`);
19
+ }
20
+ const result = noblogConfigSchema.safeParse(input);
21
+ if (!result.success) throw new ConfigurationError(`Invalid Noblog config "${path}":\n${z.prettifyError(result.error)}`);
22
+ return result.data;
23
+ }
24
+ //#endregion
25
+ export { loadNoblogConfig as t };
26
+
27
+ //# sourceMappingURL=config-file-C07nmm45.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file-C07nmm45.js","names":[],"sources":["../src/config-file.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { parse } from \"yaml\";\nimport { z } from \"zod\";\nimport { ConfigurationError } from \"./config.js\";\nimport { getErrorCode } from \"./lib/errors.js\";\nimport {\n noblogConfigSchema,\n type NoblogFileConfig,\n} from \"./lib/noblog/config.js\";\n\nexport async function loadNoblogConfig(\n path: string,\n): Promise<NoblogFileConfig> {\n let source: string;\n try {\n source = await readFile(path, \"utf8\");\n } catch (error) {\n const detail =\n getErrorCode(error) === \"ENOENT\"\n ? \"file not found\"\n : error instanceof Error\n ? error.message\n : \"unknown read error\";\n throw new ConfigurationError(\n `Cannot read Noblog config \"${path}\": ${detail}`,\n );\n }\n\n let input: unknown;\n try {\n input = parse(source, { prettyErrors: false });\n } catch (error) {\n throw new ConfigurationError(\n `Invalid YAML in Noblog config \"${path}\": ${\n error instanceof Error ? error.message : \"unknown parse error\"\n }`,\n );\n }\n\n const result = noblogConfigSchema.safeParse(input);\n if (!result.success) {\n throw new ConfigurationError(\n `Invalid Noblog config \"${path}\":\\n${z.prettifyError(result.error)}`,\n );\n }\n return result.data;\n}\n"],"mappings":";;;;;;AAUA,eAAsB,iBACpB,MAC2B;CAC3B,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,SAAS,MAAM,MAAM;CACtC,SAAS,OAAO;EAOd,MAAM,IAAI,mBACR,8BAA8B,KAAK,KANnC,aAAa,KAAK,MAAM,WACpB,mBACA,iBAAiB,QACf,MAAM,UACN,sBAGR;CACF;CAEA,IAAI;CACJ,IAAI;EACF,QAAQ,MAAM,QAAQ,EAAE,cAAc,MAAM,CAAC;CAC/C,SAAS,OAAO;EACd,MAAM,IAAI,mBACR,kCAAkC,KAAK,KACrC,iBAAiB,QAAQ,MAAM,UAAU,uBAE7C;CACF;CAEA,MAAM,SAAS,mBAAmB,UAAU,KAAK;CACjD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,mBACR,0BAA0B,KAAK,MAAM,EAAE,cAAc,OAAO,KAAK,GACnE;CAEF,OAAO,OAAO;AAChB"}
@@ -0,0 +1,10 @@
1
+ //#region src/lib/noblog/defaults.ts
2
+ const DEFAULT_SAVE_PATH = "./src/pages/posts/";
3
+ const DEFAULT_STORE_PATH = ".noblog/pages/";
4
+ const DEFAULT_LAYOUT_PATH = "../../layouts/MarkdownPostLayout.astro";
5
+ /** U+2E0E EDITORIAL CORONIS, a rare marker for theme child-page links. */
6
+ const DEFAULT_CHILD_PAGE_MARKER = "⸎";
7
+ //#endregion
8
+ export { DEFAULT_STORE_PATH as i, DEFAULT_LAYOUT_PATH as n, DEFAULT_SAVE_PATH as r, DEFAULT_CHILD_PAGE_MARKER as t };
9
+
10
+ //# sourceMappingURL=defaults-RGOB9tfi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults-RGOB9tfi.js","names":[],"sources":["../src/lib/noblog/defaults.ts"],"sourcesContent":["export const DEFAULT_SAVE_PATH = \"./src/pages/posts/\";\nexport const DEFAULT_STORE_PATH = \".noblog/pages/\";\nexport const DEFAULT_LAYOUT_PATH = \"../../layouts/MarkdownPostLayout.astro\";\n\n/** U+2E0E EDITORIAL CORONIS, a rare marker for theme child-page links. */\nexport const DEFAULT_CHILD_PAGE_MARKER = \"⸎\";\n"],"mappings":";AAAA,MAAa,oBAAoB;AACjC,MAAa,qBAAqB;AAClC,MAAa,sBAAsB;;AAGnC,MAAa,4BAA4B"}
@@ -0,0 +1,73 @@
1
+ import { z } from "zod";
2
+ //#region src/lib/noblog/config.ts
3
+ const configValueSchema = z.lazy(() => z.union([
4
+ z.string(),
5
+ z.number().finite(),
6
+ z.boolean(),
7
+ z.null(),
8
+ z.array(configValueSchema),
9
+ z.record(z.string(), configValueSchema)
10
+ ]));
11
+ const fieldModifiers = {
12
+ as: z.enum(["string", "date"]).optional().describe("Convert to a string (quoting date-like strings) or a YAML date."),
13
+ default: configValueSchema.optional().describe("Value used when the source is empty."),
14
+ omitEmpty: z.boolean().optional().describe("Omit undefined, null, empty strings, and empty arrays."),
15
+ required: z.boolean().optional().describe("Fail generation if the value is still empty after defaults.")
16
+ };
17
+ const frontmatterFieldSchema = z.union([z.strictObject({
18
+ ...fieldModifiers,
19
+ from: z.string().min(1).describe("Built-in source or a key declared under notion.properties.")
20
+ }), z.strictObject({
21
+ ...fieldModifiers,
22
+ value: configValueSchema
23
+ })]);
24
+ const frontmatterFieldsSchema = z.record(z.string().min(1), frontmatterFieldSchema);
25
+ const notionPropertiesSchema = z.record(z.string().min(1), z.string().min(1));
26
+ const BUILT_IN_FRONTMATTER_SOURCES = [
27
+ "title",
28
+ "description",
29
+ "tags",
30
+ "publishedAt",
31
+ "archived",
32
+ "layoutPath",
33
+ "pageId",
34
+ "isDatabasePage"
35
+ ];
36
+ const builtInSources = new Set(BUILT_IN_FRONTMATTER_SOURCES);
37
+ const noblogConfigSchema = z.strictObject({
38
+ version: z.literal(1),
39
+ notion: z.strictObject({ properties: notionPropertiesSchema.optional() }).optional(),
40
+ output: z.strictObject({
41
+ savePath: z.string().min(1).optional().describe("Public directory for database-post symlinks."),
42
+ storePath: z.string().min(1).optional().describe("Flat directory containing every canonical Markdown file."),
43
+ layoutPath: z.string().min(1).optional().describe("Layout import path interpreted relative to savePath."),
44
+ recursive: z.boolean().optional().describe("Render child pages recursively (default: true)."),
45
+ childPageMarker: z.string().min(1).optional().describe("Marker prefixed to child-page links (default: ⸎, U+2E0E).")
46
+ }).optional(),
47
+ frontmatter: z.strictObject({ fields: frontmatterFieldsSchema.optional() }).optional()
48
+ }).superRefine((config, context) => {
49
+ const properties = config.notion?.properties ?? {};
50
+ for (const [name, field] of Object.entries(config.frontmatter?.fields ?? {})) {
51
+ if (!("from" in field)) continue;
52
+ if (!builtInSources.has(field.from) && !Object.hasOwn(properties, field.from)) context.addIssue({
53
+ code: "custom",
54
+ message: `Unknown frontmatter source "${field.from}"; declare it under notion.properties`,
55
+ path: [
56
+ "frontmatter",
57
+ "fields",
58
+ name,
59
+ "from"
60
+ ]
61
+ });
62
+ }
63
+ }).describe("Noblog YAML configuration. Notion credentials remain in environment variables or .env files.");
64
+ //#endregion
65
+ //#region src/lib/errors.ts
66
+ function getErrorCode(error) {
67
+ if (typeof error !== "object" || error === null || !("code" in error) || typeof error.code !== "string") return;
68
+ return error.code;
69
+ }
70
+ //#endregion
71
+ export { noblogConfigSchema as a, frontmatterFieldsSchema as i, BUILT_IN_FRONTMATTER_SOURCES as n, notionPropertiesSchema as o, frontmatterFieldSchema as r, getErrorCode as t };
72
+
73
+ //# sourceMappingURL=errors-BCRhKbgG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-BCRhKbgG.js","names":[],"sources":["../src/lib/noblog/config.ts","../src/lib/errors.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport type ConfigValue =\n | string\n | number\n | boolean\n | null\n | ConfigValue[]\n | { [key: string]: ConfigValue };\n\nconst configValueSchema: z.ZodType<ConfigValue> = z.lazy(() =>\n z.union([\n z.string(),\n z.number().finite(),\n z.boolean(),\n z.null(),\n z.array(configValueSchema),\n z.record(z.string(), configValueSchema),\n ]),\n);\n\nconst fieldModifiers = {\n as: z\n .enum([\"string\", \"date\"])\n .optional()\n .describe(\n \"Convert to a string (quoting date-like strings) or a YAML date.\",\n ),\n default: configValueSchema\n .optional()\n .describe(\"Value used when the source is empty.\"),\n omitEmpty: z\n .boolean()\n .optional()\n .describe(\"Omit undefined, null, empty strings, and empty arrays.\"),\n required: z\n .boolean()\n .optional()\n .describe(\"Fail generation if the value is still empty after defaults.\"),\n};\n\nexport const frontmatterFieldSchema = z.union([\n z.strictObject({\n ...fieldModifiers,\n from: z\n .string()\n .min(1)\n .describe(\"Built-in source or a key declared under notion.properties.\"),\n }),\n z.strictObject({\n ...fieldModifiers,\n value: configValueSchema,\n }),\n]);\n\nexport const frontmatterFieldsSchema = z.record(\n z.string().min(1),\n frontmatterFieldSchema,\n);\n\nexport const notionPropertiesSchema = z.record(\n z.string().min(1),\n z.string().min(1),\n);\n\nexport const BUILT_IN_FRONTMATTER_SOURCES = [\n \"title\",\n \"description\",\n \"tags\",\n \"publishedAt\",\n \"archived\",\n \"layoutPath\",\n \"pageId\",\n \"isDatabasePage\",\n] as const;\n\nconst builtInSources: ReadonlySet<string> = new Set(\n BUILT_IN_FRONTMATTER_SOURCES,\n);\n\nexport const noblogConfigSchema = z\n .strictObject({\n version: z.literal(1),\n notion: z\n .strictObject({\n properties: notionPropertiesSchema.optional(),\n })\n .optional(),\n output: z\n .strictObject({\n savePath: z\n .string()\n .min(1)\n .optional()\n .describe(\"Public directory for database-post symlinks.\"),\n storePath: z\n .string()\n .min(1)\n .optional()\n .describe(\"Flat directory containing every canonical Markdown file.\"),\n layoutPath: z\n .string()\n .min(1)\n .optional()\n .describe(\"Layout import path interpreted relative to savePath.\"),\n recursive: z\n .boolean()\n .optional()\n .describe(\"Render child pages recursively (default: true).\"),\n childPageMarker: z\n .string()\n .min(1)\n .optional()\n .describe(\n \"Marker prefixed to child-page links (default: ⸎, U+2E0E).\",\n ),\n })\n .optional(),\n frontmatter: z\n .strictObject({\n fields: frontmatterFieldsSchema.optional(),\n })\n .optional(),\n })\n .superRefine((config, context) => {\n const properties = config.notion?.properties ?? {};\n for (const [name, field] of Object.entries(\n config.frontmatter?.fields ?? {},\n )) {\n if (!(\"from\" in field)) continue;\n if (\n !builtInSources.has(field.from) &&\n !Object.hasOwn(properties, field.from)\n ) {\n context.addIssue({\n code: \"custom\",\n message: `Unknown frontmatter source \"${field.from}\"; declare it under notion.properties`,\n path: [\"frontmatter\", \"fields\", name, \"from\"],\n });\n }\n }\n })\n .describe(\n \"Noblog YAML configuration. Notion credentials remain in environment variables or .env files.\",\n );\n\nexport type FrontmatterField = z.infer<typeof frontmatterFieldSchema>;\nexport type FrontmatterFields = z.infer<typeof frontmatterFieldsSchema>;\nexport type NotionPropertyMap = z.infer<typeof notionPropertiesSchema>;\nexport type NoblogFileConfig = z.infer<typeof noblogConfigSchema>;\n","export function getErrorCode(error: unknown): string | undefined {\n if (\n typeof error !== \"object\" ||\n error === null ||\n !(\"code\" in error) ||\n typeof error.code !== \"string\"\n ) {\n return undefined;\n }\n return error.code;\n}\n"],"mappings":";;AAUA,MAAM,oBAA4C,EAAE,WAClD,EAAE,MAAM;CACN,EAAE,OAAO;CACT,EAAE,OAAO,CAAC,CAAC,OAAO;CAClB,EAAE,QAAQ;CACV,EAAE,KAAK;CACP,EAAE,MAAM,iBAAiB;CACzB,EAAE,OAAO,EAAE,OAAO,GAAG,iBAAiB;AACxC,CAAC,CACH;AAEA,MAAM,iBAAiB;CACrB,IAAI,EACD,KAAK,CAAC,UAAU,MAAM,CAAC,CAAC,CACxB,SAAS,CAAC,CACV,SACC,iEACF;CACF,SAAS,kBACN,SAAS,CAAC,CACV,SAAS,sCAAsC;CAClD,WAAW,EACR,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,wDAAwD;CACpE,UAAU,EACP,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,6DAA6D;AAC3E;AAEA,MAAa,yBAAyB,EAAE,MAAM,CAC5C,EAAE,aAAa;CACb,GAAG;CACH,MAAM,EACH,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,4DAA4D;AAC1E,CAAC,GACD,EAAE,aAAa;CACb,GAAG;CACH,OAAO;AACT,CAAC,CACH,CAAC;AAED,MAAa,0BAA0B,EAAE,OACvC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,GAChB,sBACF;AAEA,MAAa,yBAAyB,EAAE,OACtC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,GAChB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAClB;AAEA,MAAa,+BAA+B;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,iBAAsC,IAAI,IAC9C,4BACF;AAEA,MAAa,qBAAqB,EAC/B,aAAa;CACZ,SAAS,EAAE,QAAQ,CAAC;CACpB,QAAQ,EACL,aAAa,EACZ,YAAY,uBAAuB,SAAS,EAC9C,CAAC,CAAC,CACD,SAAS;CACZ,QAAQ,EACL,aAAa;EACZ,UAAU,EACP,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,8CAA8C;EAC1D,WAAW,EACR,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,0DAA0D;EACtE,YAAY,EACT,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,sDAAsD;EAClE,WAAW,EACR,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,iDAAiD;EAC7D,iBAAiB,EACd,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SACC,2DACF;CACJ,CAAC,CAAC,CACD,SAAS;CACZ,aAAa,EACV,aAAa,EACZ,QAAQ,wBAAwB,SAAS,EAC3C,CAAC,CAAC,CACD,SAAS;AACd,CAAC,CAAC,CACD,aAAa,QAAQ,YAAY;CAChC,MAAM,aAAa,OAAO,QAAQ,cAAc,CAAC;CACjD,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QACjC,OAAO,aAAa,UAAU,CAAC,CACjC,GAAG;EACD,IAAI,EAAE,UAAU,QAAQ;EACxB,IACE,CAAC,eAAe,IAAI,MAAM,IAAI,KAC9B,CAAC,OAAO,OAAO,YAAY,MAAM,IAAI,GAErC,QAAQ,SAAS;GACf,MAAM;GACN,SAAS,+BAA+B,MAAM,KAAK;GACnD,MAAM;IAAC;IAAe;IAAU;IAAM;GAAM;EAC9C,CAAC;CAEL;AACF,CAAC,CAAC,CACD,SACC,8FACF;;;AChJF,SAAgB,aAAa,OAAoC;CAC/D,IACE,OAAO,UAAU,YACjB,UAAU,QACV,EAAE,UAAU,UACZ,OAAO,MAAM,SAAS,UAEtB;CAEF,OAAO,MAAM;AACf"}
@@ -0,0 +1,71 @@
1
+ //#region src/lib/noblog/frontmatter-fields.ts
2
+ const DEFAULT_FRONTMATTER_FIELDS = {
3
+ layout: { from: "layoutPath" },
4
+ title: {
5
+ from: "title",
6
+ default: ""
7
+ },
8
+ description: {
9
+ from: "description",
10
+ default: ""
11
+ },
12
+ tags: {
13
+ from: "tags",
14
+ default: []
15
+ },
16
+ pubDate: {
17
+ from: "publishedAt",
18
+ as: "string",
19
+ default: ""
20
+ },
21
+ archived: {
22
+ from: "archived",
23
+ as: "string",
24
+ default: ""
25
+ }
26
+ };
27
+ function createFrontmatterAdapter(fields) {
28
+ for (const [name, field] of Object.entries(fields)) if ("from" in field === "value" in field) throw new Error(`Frontmatter field "${name}" must have exactly one of "from" or "value"`);
29
+ return ({ page, metadata, layoutPath, isDatabasePage }) => {
30
+ const sources = {
31
+ ...metadata.values,
32
+ layoutPath,
33
+ pageId: page.id,
34
+ isDatabasePage
35
+ };
36
+ const output = {};
37
+ for (const [name, field] of Object.entries(fields)) {
38
+ let value = "value" in field ? field.value : sources[field.from];
39
+ if (isEmpty(value) && "default" in field) value = field.default;
40
+ if (field.required && isEmpty(value)) throw new Error(`Missing required frontmatter field "${name}" for page "${page.id}"`);
41
+ if (field.omitEmpty && isEmpty(value)) continue;
42
+ output[name] = convertValue(value, field, name, page.id);
43
+ }
44
+ return output;
45
+ };
46
+ }
47
+ function convertValue(value, field, name, pageId) {
48
+ if (!field.as || isEmpty(value)) return value;
49
+ if (field.as === "string") {
50
+ if ([
51
+ "string",
52
+ "number",
53
+ "boolean"
54
+ ].includes(typeof value)) return String(value);
55
+ throw conversionError(name, pageId, "a string, number, or boolean");
56
+ }
57
+ if (typeof value !== "string" && typeof value !== "number" || typeof value === "number" && !Number.isFinite(value)) throw conversionError(name, pageId, "a valid date string");
58
+ const date = new Date(value);
59
+ if (Number.isNaN(date.getTime())) throw conversionError(name, pageId, "a valid date string");
60
+ return date;
61
+ }
62
+ function conversionError(name, pageId, expected) {
63
+ return /* @__PURE__ */ new Error(`Cannot convert frontmatter field "${name}" for page "${pageId}"; expected ${expected}`);
64
+ }
65
+ function isEmpty(value) {
66
+ return value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
67
+ }
68
+ //#endregion
69
+ export { createFrontmatterAdapter as n, DEFAULT_FRONTMATTER_FIELDS as t };
70
+
71
+ //# sourceMappingURL=frontmatter-fields-BkXq38CO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter-fields-BkXq38CO.js","names":[],"sources":["../src/lib/noblog/frontmatter-fields.ts"],"sourcesContent":["import type {\n FrontmatterField,\n FrontmatterFields,\n} from \"./config.js\";\nimport type { FrontmatterAdapter, FrontmatterValue } from \"./types.js\";\n\nexport const DEFAULT_FRONTMATTER_FIELDS: FrontmatterFields = {\n layout: { from: \"layoutPath\" },\n title: { from: \"title\", default: \"\" },\n description: { from: \"description\", default: \"\" },\n tags: { from: \"tags\", default: [] },\n pubDate: { from: \"publishedAt\", as: \"string\", default: \"\" },\n archived: { from: \"archived\", as: \"string\", default: \"\" },\n};\n\nexport function createFrontmatterAdapter(\n fields: FrontmatterFields,\n): FrontmatterAdapter {\n for (const [name, field] of Object.entries(fields)) {\n if ((\"from\" in field) === (\"value\" in field)) {\n throw new Error(\n `Frontmatter field \"${name}\" must have exactly one of \"from\" or \"value\"`,\n );\n }\n }\n\n return ({ page, metadata, layoutPath, isDatabasePage }) => {\n const sources: Record<string, FrontmatterValue | undefined> = {\n ...metadata.values,\n layoutPath,\n pageId: page.id,\n isDatabasePage,\n };\n const output: Record<string, FrontmatterValue | undefined> = {};\n\n for (const [name, field] of Object.entries(fields)) {\n let value =\n \"value\" in field\n ? field.value\n : sources[field.from];\n\n if (isEmpty(value) && \"default\" in field) {\n value = field.default;\n }\n if (field.required && isEmpty(value)) {\n throw new Error(\n `Missing required frontmatter field \"${name}\" for page \"${page.id}\"`,\n );\n }\n if (field.omitEmpty && isEmpty(value)) continue;\n\n output[name] = convertValue(value, field, name, page.id);\n }\n\n return output;\n };\n}\n\nfunction convertValue(\n value: FrontmatterValue | undefined,\n field: FrontmatterField,\n name: string,\n pageId: string,\n): FrontmatterValue | undefined {\n if (!field.as || isEmpty(value)) return value;\n\n if (field.as === \"string\") {\n if ([\"string\", \"number\", \"boolean\"].includes(typeof value)) {\n return String(value);\n }\n throw conversionError(name, pageId, \"a string, number, or boolean\");\n }\n\n if (\n (typeof value !== \"string\" && typeof value !== \"number\") ||\n (typeof value === \"number\" && !Number.isFinite(value))\n ) {\n throw conversionError(name, pageId, \"a valid date string\");\n }\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n throw conversionError(name, pageId, \"a valid date string\");\n }\n return date;\n}\n\nfunction conversionError(\n name: string,\n pageId: string,\n expected: string,\n): Error {\n return new Error(\n `Cannot convert frontmatter field \"${name}\" for page \"${pageId}\"; expected ${expected}`,\n );\n}\n\nfunction isEmpty(value: FrontmatterValue | undefined): boolean {\n return (\n value === undefined ||\n value === null ||\n value === \"\" ||\n (Array.isArray(value) && value.length === 0)\n );\n}\n"],"mappings":";AAMA,MAAa,6BAAgD;CAC3D,QAAQ,EAAE,MAAM,aAAa;CAC7B,OAAO;EAAE,MAAM;EAAS,SAAS;CAAG;CACpC,aAAa;EAAE,MAAM;EAAe,SAAS;CAAG;CAChD,MAAM;EAAE,MAAM;EAAQ,SAAS,CAAC;CAAE;CAClC,SAAS;EAAE,MAAM;EAAe,IAAI;EAAU,SAAS;CAAG;CAC1D,UAAU;EAAE,MAAM;EAAY,IAAI;EAAU,SAAS;CAAG;AAC1D;AAEA,SAAgB,yBACd,QACoB;CACpB,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,GAC/C,IAAK,UAAU,UAAY,WAAW,OACpC,MAAM,IAAI,MACR,sBAAsB,KAAK,6CAC7B;CAIJ,QAAQ,EAAE,MAAM,UAAU,YAAY,qBAAqB;EACzD,MAAM,UAAwD;GAC5D,GAAG,SAAS;GACZ;GACA,QAAQ,KAAK;GACb;EACF;EACA,MAAM,SAAuD,CAAC;EAE9D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,GAAG;GAClD,IAAI,QACF,WAAW,QACP,MAAM,QACN,QAAQ,MAAM;GAEpB,IAAI,QAAQ,KAAK,KAAK,aAAa,OACjC,QAAQ,MAAM;GAEhB,IAAI,MAAM,YAAY,QAAQ,KAAK,GACjC,MAAM,IAAI,MACR,uCAAuC,KAAK,cAAc,KAAK,GAAG,EACpE;GAEF,IAAI,MAAM,aAAa,QAAQ,KAAK,GAAG;GAEvC,OAAO,QAAQ,aAAa,OAAO,OAAO,MAAM,KAAK,EAAE;EACzD;EAEA,OAAO;CACT;AACF;AAEA,SAAS,aACP,OACA,OACA,MACA,QAC8B;CAC9B,IAAI,CAAC,MAAM,MAAM,QAAQ,KAAK,GAAG,OAAO;CAExC,IAAI,MAAM,OAAO,UAAU;EACzB,IAAI;GAAC;GAAU;GAAU;EAAS,CAAC,CAAC,SAAS,OAAO,KAAK,GACvD,OAAO,OAAO,KAAK;EAErB,MAAM,gBAAgB,MAAM,QAAQ,8BAA8B;CACpE;CAEA,IACG,OAAO,UAAU,YAAY,OAAO,UAAU,YAC9C,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAEpD,MAAM,gBAAgB,MAAM,QAAQ,qBAAqB;CAE3D,MAAM,OAAO,IAAI,KAAK,KAAK;CAC3B,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAC7B,MAAM,gBAAgB,MAAM,QAAQ,qBAAqB;CAE3D,OAAO;AACT;AAEA,SAAS,gBACP,MACA,QACA,UACO;CACP,uBAAO,IAAI,MACT,qCAAqC,KAAK,cAAc,OAAO,cAAc,UAC/E;AACF;AAEA,SAAS,QAAQ,OAA8C;CAC7D,OACE,UAAU,KAAA,KACV,UAAU,QACV,UAAU,MACT,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;AAE9C"}
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,34 @@
1
+ import { n as loadConfig, t as ConfigurationError } from "./config-csbquMhF.js";
2
+ import "./defaults-RGOB9tfi.js";
3
+ import { n as createFrontmatterAdapter, t as DEFAULT_FRONTMATTER_FIELDS } from "./frontmatter-fields-BkXq38CO.js";
4
+ import { t as loadNoblogConfig } from "./config-file-C07nmm45.js";
5
+ //#region src/commands/generate.impl.ts
6
+ async function generate(flags, savePath, legacySubPath) {
7
+ try {
8
+ const fileConfig = flags.config ? await loadNoblogConfig(flags.config) : void 0;
9
+ const config = loadConfig(this.process.env);
10
+ if (legacySubPath !== void 0) this.process.stderr.write("Warning: SUB_PATH is deprecated and ignored; child pages now live only in the canonical store.\n");
11
+ const propertyMap = fileConfig?.notion?.properties;
12
+ const fields = fileConfig?.frontmatter?.fields;
13
+ const frontmatter = fields || propertyMap ? createFrontmatterAdapter(fields ?? DEFAULT_FRONTMATTER_FIELDS) : void 0;
14
+ await this.generateBlog(config, {
15
+ savePath: savePath ?? fileConfig?.output?.savePath ?? "./src/pages/posts/",
16
+ storePath: flags.store ?? fileConfig?.output?.storePath ?? ".noblog/pages/",
17
+ layoutPath: flags.layout ?? fileConfig?.output?.layoutPath ?? "../../layouts/MarkdownPostLayout.astro",
18
+ recursive: fileConfig?.output?.recursive ?? true,
19
+ childPageMarker: fileConfig?.output?.childPageMarker ?? "⸎",
20
+ ...propertyMap !== void 0 ? { propertyMap } : {},
21
+ ...frontmatter !== void 0 ? { frontmatter } : {},
22
+ verbose: flags.verbose
23
+ }, this.process);
24
+ } catch (error) {
25
+ if (error instanceof ConfigurationError) return error;
26
+ if (!(error instanceof Error)) return /* @__PURE__ */ new Error("Generation failed: an unknown error occurred");
27
+ const message = flags.verbose ? error.stack ?? error.message : error.message;
28
+ return new Error(`Generation failed: ${message}`, { cause: error });
29
+ }
30
+ }
31
+ //#endregion
32
+ export { generate as default };
33
+
34
+ //# sourceMappingURL=generate.impl-ChFrtwtC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.impl-ChFrtwtC.js","names":[],"sources":["../src/commands/generate.impl.ts"],"sourcesContent":["import { ConfigurationError, loadConfig } from \"../config.js\";\nimport { loadNoblogConfig } from \"../config-file.js\";\nimport type { CliContext } from \"../context.js\";\nimport {\n DEFAULT_CHILD_PAGE_MARKER,\n DEFAULT_LAYOUT_PATH,\n DEFAULT_SAVE_PATH,\n DEFAULT_STORE_PATH,\n} from \"../lib/noblog/defaults.js\";\nimport {\n createFrontmatterAdapter,\n DEFAULT_FRONTMATTER_FIELDS,\n} from \"../lib/noblog/frontmatter-fields.js\";\n\ninterface GenerateFlags {\n verbose: boolean;\n config?: string;\n layout?: string;\n store?: string;\n}\n\nexport default async function generate(\n this: CliContext,\n flags: GenerateFlags,\n savePath?: string,\n legacySubPath?: string,\n): Promise<Error | void> {\n try {\n const fileConfig = flags.config\n ? await loadNoblogConfig(flags.config)\n : undefined;\n const config = loadConfig(this.process.env);\n if (legacySubPath !== undefined) {\n this.process.stderr.write(\n \"Warning: SUB_PATH is deprecated and ignored; child pages now live only in the canonical store.\\n\",\n );\n }\n\n const propertyMap = fileConfig?.notion?.properties;\n const fields = fileConfig?.frontmatter?.fields;\n const frontmatter =\n fields || propertyMap\n ? createFrontmatterAdapter(fields ?? DEFAULT_FRONTMATTER_FIELDS)\n : undefined;\n\n await this.generateBlog(\n config,\n {\n savePath:\n savePath ?? fileConfig?.output?.savePath ?? DEFAULT_SAVE_PATH,\n storePath:\n flags.store ?? fileConfig?.output?.storePath ?? DEFAULT_STORE_PATH,\n layoutPath:\n flags.layout ?? fileConfig?.output?.layoutPath ?? DEFAULT_LAYOUT_PATH,\n recursive: fileConfig?.output?.recursive ?? true,\n childPageMarker:\n fileConfig?.output?.childPageMarker ?? DEFAULT_CHILD_PAGE_MARKER,\n ...(propertyMap !== undefined ? { propertyMap } : {}),\n ...(frontmatter !== undefined ? { frontmatter } : {}),\n verbose: flags.verbose,\n },\n this.process,\n );\n } catch (error) {\n if (error instanceof ConfigurationError) {\n return error;\n }\n if (!(error instanceof Error)) {\n return new Error(\"Generation failed: an unknown error occurred\");\n }\n\n const message = flags.verbose\n ? (error.stack ?? error.message)\n : error.message;\n return new Error(`Generation failed: ${message}`, { cause: error });\n }\n}\n"],"mappings":";;;;;AAqBA,eAA8B,SAE5B,OACA,UACA,eACuB;CACvB,IAAI;EACF,MAAM,aAAa,MAAM,SACrB,MAAM,iBAAiB,MAAM,MAAM,IACnC,KAAA;EACJ,MAAM,SAAS,WAAW,KAAK,QAAQ,GAAG;EAC1C,IAAI,kBAAkB,KAAA,GACpB,KAAK,QAAQ,OAAO,MAClB,kGACF;EAGF,MAAM,cAAc,YAAY,QAAQ;EACxC,MAAM,SAAS,YAAY,aAAa;EACxC,MAAM,cACJ,UAAU,cACN,yBAAyB,UAAU,0BAA0B,IAC7D,KAAA;EAEN,MAAM,KAAK,aACT,QACA;GACE,UACE,YAAY,YAAY,QAAQ,YAAA;GAClC,WACE,MAAM,SAAS,YAAY,QAAQ,aAAA;GACrC,YACE,MAAM,UAAU,YAAY,QAAQ,cAAA;GACtC,WAAW,YAAY,QAAQ,aAAa;GAC5C,iBACE,YAAY,QAAQ,mBAAA;GACtB,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;GACnD,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;GACnD,SAAS,MAAM;EACjB,GACA,KAAK,OACP;CACF,SAAS,OAAO;EACd,IAAI,iBAAiB,oBACnB,OAAO;EAET,IAAI,EAAE,iBAAiB,QACrB,uBAAO,IAAI,MAAM,8CAA8C;EAGjE,MAAM,UAAU,MAAM,UACjB,MAAM,SAAS,MAAM,UACtB,MAAM;EACV,OAAO,IAAI,MAAM,sBAAsB,WAAW,EAAE,OAAO,MAAM,CAAC;CACpE;AACF"}