noblog 0.3.0-beta.3 → 0.3.0-beta.5
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/README.md +13 -0
- package/dist/{client-BIBJQzBg.js → client-CJ_0duRz.js} +57 -5
- package/dist/client-CJ_0duRz.js.map +1 -0
- package/dist/{config-check.impl-NO--hbh0.js → config-check.impl-CkV2SIkF.js} +2 -2
- package/dist/{config-check.impl-NO--hbh0.js.map → config-check.impl-CkV2SIkF.js.map} +1 -1
- package/dist/{config-file-CZX2WJnz.js → config-file-Ti3lB3Et.js} +2 -2
- package/dist/{config-file-CZX2WJnz.js.map → config-file-Ti3lB3Et.js.map} +1 -1
- package/dist/{defaults-RGOB9tfi.js → defaults-DdsgY0uy.js} +3 -2
- package/dist/defaults-DdsgY0uy.js.map +1 -0
- package/dist/{errors-Bm4iVQVp.js → errors-v1sSdZ3l.js} +3 -2
- package/dist/errors-v1sSdZ3l.js.map +1 -0
- package/dist/{generate.impl-zbCKiANA.js → generate.impl-uVcUsW_1.js} +5 -3
- package/dist/generate.impl-uVcUsW_1.js.map +1 -0
- package/dist/generate.js +26 -7
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/schema/config.json +5 -0
- package/skills/noblog-config/SKILL.md +1 -1
- package/skills/noblog-config/references/config-schema.md +1 -0
- package/skills/noblog-config/references/theme-examples.md +2 -0
- package/dist/client-BIBJQzBg.js.map +0 -1
- package/dist/defaults-RGOB9tfi.js.map +0 -1
- package/dist/errors-Bm4iVQVp.js.map +0 -1
- package/dist/generate.impl-zbCKiANA.js.map +0 -1
package/README.md
CHANGED
|
@@ -95,6 +95,9 @@ noblog --store content/.noblog-pages
|
|
|
95
95
|
# Generate using YAML configuration
|
|
96
96
|
noblog --config ./noblog.yaml
|
|
97
97
|
|
|
98
|
+
# Also render the page titled "humbornjo" to public/selfie.md
|
|
99
|
+
noblog --selfie humbornjo
|
|
100
|
+
|
|
98
101
|
# Validate configuration without credentials or Notion access
|
|
99
102
|
noblog config check --config ./noblog.yaml
|
|
100
103
|
|
|
@@ -165,6 +168,16 @@ output:
|
|
|
165
168
|
|
|
166
169
|
When configured, the wrapper overrides `output.childPageMarker`. Both strings must be non-empty and single-line. The wrapper is emitted as raw content, so use it with an HTML-capable Markdown renderer.
|
|
167
170
|
|
|
171
|
+
### Selfie page
|
|
172
|
+
|
|
173
|
+
`--selfie PAGE_TITLE` renders the database page with that exact title to `output.selfiePath` (default `.noblog/selfie.md`, next to the canonical store) in addition to the normal output. The written frontmatter always carries `author: <page title>`, so a consumer site can render the author (for example in a footer) from the page itself. This suits builds that pull a bio page from a personal Notion database:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
noblog --selfie humbornjo
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The page is matched by its rendered title, honoring `notion.properties` mappings such as `title: Name`. When `--selfie` is omitted or empty, or no page title matches, the selfie file is left untouched — commit a sample `.noblog/selfie.md` and everyone without a matching page falls back to it. References work in both directions: other pages link to the bio page through its canonical store file like any sibling page, and child-page links inside the selfie file are rewritten (`./pages/<page-id>.md`) so they keep resolving from `.noblog/`.
|
|
180
|
+
|
|
168
181
|
The keys under `notion.properties` are logical source names; their values must exactly match Notion property names. Custom sources such as `series` must be declared there before frontmatter can reference them. Built-in sources need no declaration.
|
|
169
182
|
|
|
170
183
|
Validate a file without Notion credentials, a Notion client, or network access:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { t as getErrorCode } from "./errors-
|
|
2
|
-
import { n as DEFAULT_LAYOUT_PATH } from "./defaults-
|
|
1
|
+
import { t as getErrorCode } from "./errors-v1sSdZ3l.js";
|
|
2
|
+
import { i as DEFAULT_SELFIE_PATH, n as DEFAULT_LAYOUT_PATH } from "./defaults-DdsgY0uy.js";
|
|
3
3
|
import { stringify } from "yaml";
|
|
4
4
|
import { markdownTable } from "markdown-table";
|
|
5
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
5
6
|
import { randomUUID } from "node:crypto";
|
|
6
7
|
import { lstat, mkdir, readlink, rename, rm, symlink, writeFile } from "node:fs/promises";
|
|
7
|
-
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
8
8
|
import { Client, isFullBlock, isFullDatabase, isFullPage, isNotionClientError } from "@notionhq/client";
|
|
9
9
|
//#region src/lib/noblog/metadata.ts
|
|
10
10
|
const DEFAULT_PROPERTIES = {
|
|
@@ -429,6 +429,52 @@ async function renderSinglePage(pageId, context, post) {
|
|
|
429
429
|
};
|
|
430
430
|
}
|
|
431
431
|
//#endregion
|
|
432
|
+
//#region src/lib/noblog/selfie.ts
|
|
433
|
+
/**
|
|
434
|
+
* Find the database post whose title exactly matches the requested selfie
|
|
435
|
+
* title. Titles are compared trimmed on both sides.
|
|
436
|
+
*/
|
|
437
|
+
function findSelfiePage(posts, title, propertyMap) {
|
|
438
|
+
for (const post of posts) {
|
|
439
|
+
const metadata = extractPageMetadata(post, propertyMap);
|
|
440
|
+
if (metadata.title.trim() === title) return {
|
|
441
|
+
page: post,
|
|
442
|
+
title: metadata.title
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Return the Markdown document with its frontmatter `author` set to the given
|
|
448
|
+
* value. An existing top-level `author` key is replaced so the result always
|
|
449
|
+
* reflects the provided author; the document body is left untouched.
|
|
450
|
+
*/
|
|
451
|
+
function upsertFrontmatterAuthor(content, author) {
|
|
452
|
+
const authorLine = `author: ${JSON.stringify(author)}`;
|
|
453
|
+
const lines = content.split("\n");
|
|
454
|
+
if (lines[0] !== "---") return `---\n${authorLine}\n---\n${content}`;
|
|
455
|
+
const closing = lines.indexOf("---", 1);
|
|
456
|
+
if (closing === -1) return `---\n${authorLine}\n---\n${content}`;
|
|
457
|
+
return [
|
|
458
|
+
"---",
|
|
459
|
+
authorLine,
|
|
460
|
+
...lines.slice(1, closing).filter((line) => !/^author\s*:/u.test(line)),
|
|
461
|
+
...lines.slice(closing)
|
|
462
|
+
].join("\n");
|
|
463
|
+
}
|
|
464
|
+
const STORE_CHILD_LINK = /\]\(\.\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.md\)/g;
|
|
465
|
+
/**
|
|
466
|
+
* Rewrite `./<page-id>.md` child-page links so they resolve from the selfie
|
|
467
|
+
* file's directory instead of the canonical store directory. Links are left
|
|
468
|
+
* unchanged when the selfie file already sits next to the store files, and
|
|
469
|
+
* non-store relative links (for example user asset links) are never touched.
|
|
470
|
+
*/
|
|
471
|
+
function relativizeStoreLinks(content, selfiePath, storePath) {
|
|
472
|
+
const storeDirectory = relative(dirname(resolve(selfiePath)), resolve(storePath));
|
|
473
|
+
const prefix = storeDirectory === "" ? "" : `${storeDirectory.split(sep).join("/")}/`;
|
|
474
|
+
if (prefix === "") return content;
|
|
475
|
+
return content.replace(STORE_CHILD_LINK, (_, pageId) => `](./${prefix}${pageId}.md)`);
|
|
476
|
+
}
|
|
477
|
+
//#endregion
|
|
432
478
|
//#region src/lib/noblog/writer.ts
|
|
433
479
|
async function writePages(pages, options = {}) {
|
|
434
480
|
const savePath = options.savePath ?? "./src/pages/posts/";
|
|
@@ -448,6 +494,12 @@ async function writePages(pages, options = {}) {
|
|
|
448
494
|
}
|
|
449
495
|
return writtenPaths;
|
|
450
496
|
}
|
|
497
|
+
async function writeSelfie(content, selfiePath = DEFAULT_SELFIE_PATH) {
|
|
498
|
+
const directory = dirname(selfiePath);
|
|
499
|
+
if (directory !== ".") await mkdir(directory, { recursive: true });
|
|
500
|
+
await writeFile(selfiePath, content, "utf8");
|
|
501
|
+
return selfiePath;
|
|
502
|
+
}
|
|
451
503
|
function resolveStoreLayoutPath(options = {}) {
|
|
452
504
|
const savePath = options.savePath ?? "./src/pages/posts/";
|
|
453
505
|
const storePath = options.storePath ?? ".noblog/pages/";
|
|
@@ -551,6 +603,6 @@ function delay(milliseconds) {
|
|
|
551
603
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
552
604
|
}
|
|
553
605
|
//#endregion
|
|
554
|
-
export {
|
|
606
|
+
export { todo as A, inlineCode as C, quote as D, link as E, underline as M, renderFrontmatter as N, strikethrough as O, extractPageMetadata as P, image as S, italic as T, equation as _, findSelfiePage as a, heading3 as b, renderPage as c, renderRichText as d, bold as f, divider as g, codeBlock as h, writeSelfie as i, toggle as j, table as k, renderPages as l, callout as m, resolveStoreLayoutPath as n, relativizeStoreLinks as o, bullet as p, writePages as r, upsertFrontmatterAuthor as s, createNotionSource as t, renderPlainText as u, heading1 as v, inlineEquation as w, heading4 as x, heading2 as y };
|
|
555
607
|
|
|
556
|
-
//# sourceMappingURL=client-
|
|
608
|
+
//# sourceMappingURL=client-CJ_0duRz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-CJ_0duRz.js","names":["md.toggle","md.callout","md.bullet","md.image","md.divider","md.equation","md.link","md.heading2","md.codeBlock","md.heading1","md.heading3","md.heading4","md.quote","md.todo","link","md.table"],"sources":["../src/lib/noblog/metadata.ts","../src/lib/noblog/frontmatter.ts","../src/lib/noblog/markdown.ts","../src/lib/noblog/rich-text.ts","../src/lib/noblog/blocks.ts","../src/lib/noblog/pages.ts","../src/lib/noblog/selfie.ts","../src/lib/noblog/writer.ts","../src/lib/notion/client.ts"],"sourcesContent":["import type { PageObjectResponse } from \"@notionhq/client\";\nimport type { NotionPropertyMap } from \"./config.js\";\n\ntype NotionProperty = PageObjectResponse[\"properties\"][string];\n\nexport type MetadataValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined;\n\nexport interface PageMetadata {\n readonly title: string;\n readonly description?: string | undefined;\n readonly tags: readonly string[];\n readonly publishedAt?: string | undefined;\n readonly archived?: boolean | undefined;\n readonly values: Readonly<Record<string, MetadataValue>>;\n}\n\nconst DEFAULT_PROPERTIES = {\n description: \"description\",\n tags: \"tags\",\n publishedAt: \"date\",\n archived: \"archived\",\n} as const;\n\nexport function extractPageMetadata(\n page: PageObjectResponse,\n properties: NotionPropertyMap = {},\n): PageMetadata {\n const propertyNames = new Map<string, string>(Object.entries(properties));\n for (const [key, name] of Object.entries(DEFAULT_PROPERTIES)) {\n if (!propertyNames.has(key)) propertyNames.set(key, name);\n }\n\n const values: Record<string, MetadataValue> = {};\n if (!propertyNames.has(\"title\")) {\n const titleProperty = Object.values(page.properties).findLast(\n (property) => property.type === \"title\",\n );\n values[\"title\"] = normalizeProperty(titleProperty);\n }\n for (const [key, name] of propertyNames) {\n values[key] = normalizeProperty(page.properties[name]);\n }\n\n return {\n title: asString(values[\"title\"]) ?? \"\",\n description: asString(values[\"description\"]),\n tags: asStringArray(values[\"tags\"]) ?? [],\n publishedAt: asString(values[\"publishedAt\"]),\n archived: asBoolean(values[\"archived\"]),\n values,\n };\n}\n\nfunction normalizeProperty(property?: NotionProperty): MetadataValue {\n if (!property) return undefined;\n\n switch (property.type) {\n case \"title\":\n return plainText(property.title);\n case \"rich_text\":\n return plainText(property.rich_text);\n case \"multi_select\":\n return property.multi_select.map(({ name }) => name);\n case \"select\":\n return property.select?.name;\n case \"status\":\n return property.status?.name;\n case \"checkbox\":\n return property.checkbox;\n case \"number\":\n return property.number ?? undefined;\n case \"date\":\n return property.date?.start;\n case \"url\":\n return property.url ?? undefined;\n case \"email\":\n return property.email ?? undefined;\n case \"phone_number\":\n return property.phone_number ?? undefined;\n case \"people\":\n return property.people.map(entityName);\n case \"relation\":\n return property.relation.map(({ id }) => id);\n case \"files\":\n return property.files.map((file) =>\n \"file\" in file ? file.file.url : file.external.url,\n );\n case \"formula\":\n return normalizeFormula(property.formula);\n case \"created_time\":\n return property.created_time;\n case \"last_edited_time\":\n return property.last_edited_time;\n case \"created_by\":\n return entityName(property.created_by);\n case \"last_edited_by\":\n return entityName(property.last_edited_by);\n case \"unique_id\":\n return property.unique_id.prefix\n ? `${property.unique_id.prefix}-${property.unique_id.number}`\n : String(property.unique_id.number);\n default:\n return undefined;\n }\n}\n\nfunction normalizeFormula(\n formula: Extract<NotionProperty, { type: \"formula\" }>[\"formula\"],\n): MetadataValue {\n switch (formula.type) {\n case \"string\":\n return formula.string ?? undefined;\n case \"number\":\n return formula.number ?? undefined;\n case \"boolean\":\n return formula.boolean ?? undefined;\n case \"date\":\n return formula.date?.start;\n }\n}\n\nfunction plainText(\n richText: Extract<NotionProperty, { type: \"title\" }>[\"title\"],\n): string {\n return richText.map((item) => item.plain_text).join(\"\");\n}\n\nfunction entityName(entity: {\n readonly id: string;\n readonly name?: string | null;\n}): string {\n return entity.name ?? entity.id;\n}\n\nfunction asString(value: MetadataValue): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction asBoolean(value: MetadataValue): boolean | undefined {\n return typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction asStringArray(value: MetadataValue): readonly string[] | undefined {\n if (typeof value === \"string\") return [value];\n return Array.isArray(value) ? value : undefined;\n}\n","import type { PageObjectResponse } from \"@notionhq/client\";\nimport { stringify } from \"yaml\";\nimport type { NotionPropertyMap } from \"./config.js\";\nimport { DEFAULT_LAYOUT_PATH } from \"./defaults.js\";\nimport { extractPageMetadata } from \"./metadata.js\";\nimport type { FrontmatterAdapter, FrontmatterRecord } from \"./types.js\";\n\nexport interface FrontmatterOptions {\n readonly layoutPath?: string;\n readonly propertyMap?: NotionPropertyMap;\n readonly frontmatter?: FrontmatterAdapter;\n readonly isDatabasePage?: boolean;\n}\n\nexport function renderFrontmatter(\n page: PageObjectResponse,\n options: FrontmatterOptions = {},\n): string {\n const { layoutPath = DEFAULT_LAYOUT_PATH } = options;\n if (options.frontmatter) {\n const metadata = extractPageMetadata(page, options.propertyMap);\n const values = options.frontmatter({\n page,\n metadata,\n layoutPath,\n isDatabasePage: options.isDatabasePage ?? true,\n });\n return serializeFrontmatter(values);\n }\n\n let title = \"\";\n let description = \"\";\n let tags: string[] = [];\n let pubDate = \"\";\n let archived = \"\";\n\n for (const [name, property] of Object.entries(page.properties)) {\n switch (property.type) {\n case \"title\":\n title = property.title.map((item) => item.plain_text).join(\"\");\n break;\n case \"multi_select\":\n if (name === \"tags\") {\n tags = property.multi_select.map((tag) => tag.name);\n }\n break;\n case \"date\":\n if (name === \"date\") {\n pubDate =\n property.date?.start ?? new Date().toISOString().slice(0, 10);\n }\n break;\n }\n\n if (name === \"archived\" && property.type === \"checkbox\") {\n archived = property.checkbox ? \"true\" : \"false\";\n }\n if (name === \"description\" && property.type === \"rich_text\") {\n description = property.rich_text\n .map((item) => item.plain_text)\n .join(\"\");\n }\n }\n\n return (\n \"---\\n\" +\n `layout: ${layoutPath}\\n` +\n `title: ${JSON.stringify(title)}\\n` +\n `description: ${JSON.stringify(description)}\\n` +\n `tags: ${JSON.stringify(tags)}\\n` +\n `pubDate: ${JSON.stringify(pubDate)}\\n` +\n `archived: ${JSON.stringify(archived)}\\n` +\n \"---\\n\"\n );\n}\n\nfunction serializeFrontmatter(values: FrontmatterRecord): string {\n if (\n values === null ||\n typeof values !== \"object\" ||\n Array.isArray(values) ||\n (Object.getPrototypeOf(values) !== Object.prototype &&\n Object.getPrototypeOf(values) !== null)\n ) {\n throw new Error(\"Frontmatter adapter must return a plain object\");\n }\n\n const entries = Object.entries(values).filter(\n ([, value]) => value !== undefined,\n );\n if (entries.length === 0) return \"---\\n---\\n\";\n\n const yaml = stringify(Object.fromEntries(entries), {\n aliasDuplicateObjects: false,\n schema: \"yaml-1.1\",\n });\n return `---\\n${yaml}---\\n`;\n}\n","import { markdownTable } from \"markdown-table\";\nimport type { CalloutIcon } from \"./types.js\";\n\nexport const inlineCode = (text: string) => `\\`${text}\\``;\nexport const inlineEquation = (text: string) => `$${text}$`;\nexport const bold = (text: string) => `**${text}**`;\nexport const italic = (text: string) => `_${text}_`;\nexport const strikethrough = (text: string) => `~~${text}~~`;\nexport const underline = (text: string) => `<u>${text}</u>`;\nexport const link = (text: string, href: string) => `[${text}](${href})`;\n\nexport function codeBlock(text: string, language = \"\"): string {\n const normalizedLanguage = language === \"plain text\" ? \"text\" : language;\n return `\\`\\`\\`${normalizedLanguage}\\n${text}\\n\\`\\`\\``;\n}\n\nexport const equation = (text: string) => `$$\\n${text}\\n$$`;\nexport const heading1 = (text: string) => `# ${text}`;\nexport const heading2 = (text: string) => `## ${text}`;\nexport const heading3 = (text: string) => `### ${text}`;\nexport const heading4 = (text: string) => `#### ${text}`;\nexport const quote = (text: string) => `> ${text.replace(/\\n/g, \" \\n> \")}`;\n\nexport function callout(text: string, icon?: CalloutIcon): string {\n const emoji = icon?.type === \"emoji\" ? `${icon.emoji} ` : \"\";\n return `> ${emoji}${text.replace(/\\n/g, \" \\n> \")}`;\n}\n\nexport function bullet(text: string, count?: number): string {\n const rendered = text.trim();\n return count === undefined ? `- ${rendered}` : `${count}. ${rendered}`;\n}\n\nexport const todo = (text: string, checked: boolean) =>\n checked ? `- [x] ${text}` : `- [ ] ${text}`;\n\nexport async function image(\n alt: string,\n href: string,\n convertToBase64 = false,\n): Promise<string> {\n if (!convertToBase64 || href.startsWith(\"data:\")) {\n if (href.startsWith(\"data:\")) {\n const base64 = href.split(\",\").pop();\n return ``;\n }\n return ``;\n }\n\n const response = await fetch(href);\n if (!response.ok) {\n throw new Error(\n `Failed to download image: ${response.status} ${response.statusText}`,\n );\n }\n const buffer = await response.arrayBuffer();\n return `.toString(\"base64\")})`;\n}\n\nexport const divider = () => \"---\";\n\nexport function toggle(summary?: string, children?: string): string {\n if (!summary) return children ?? \"\";\n return `<details>\\n<summary>${summary}</summary>\\n${children ?? \"\"}\\n</details>\\n\\n`;\n}\n\nexport function table(cells: string[][], hasColumnHeader = true): string {\n if (hasColumnHeader || cells.length === 0) return markdownTable(cells);\n\n const width = Math.max(...cells.map((row) => row.length));\n if (width === 0) return \"\";\n const emptyHeader = Array.from({ length: width }, () => \"\");\n return markdownTable([emptyHeader, ...cells]);\n}\n","import type { RichTextItemResponse } from \"@notionhq/client\";\nimport { inlineEquation, link } from \"./markdown.js\";\n\ntype AnnotationName = Exclude<\n keyof RichTextItemResponse[\"annotations\"],\n \"color\"\n>;\n\ninterface AnnotationFormat {\n readonly name: AnnotationName;\n readonly open: string;\n readonly close: string;\n}\n\n// Ordered from the outermost marker to the innermost marker. Stable sorting\n// keeps this order when annotations end on the same rich-text segment.\nconst ANNOTATION_FORMATS: readonly AnnotationFormat[] = [\n { name: \"strikethrough\", open: \"~~\", close: \"~~\" },\n { name: \"underline\", open: \"<u>\", close: \"</u>\" },\n { name: \"italic\", open: \"_\", close: \"_\" },\n { name: \"bold\", open: \"**\", close: \"**\" },\n { name: \"code\", open: \"`\", close: \"`\" },\n];\n\nexport function renderPlainText(\n richText: readonly RichTextItemResponse[],\n): string {\n return richText.map((item) => item.plain_text).join(\"\");\n}\n\nexport function renderRichText(\n richText: readonly RichTextItemResponse[],\n): string {\n let rendered = \"\";\n let active: AnnotationFormat[] = [];\n\n for (const [index, item] of richText.entries()) {\n const desired = getAnnotationFormats(richText, index, item);\n let shared = 0;\n while (\n shared < active.length &&\n shared < desired.length &&\n active[shared]?.name === desired[shared]?.name\n ) {\n shared++;\n }\n\n for (const format of active.slice(shared).toReversed()) {\n rendered += format.close;\n }\n for (const format of desired.slice(shared)) {\n rendered += format.open;\n }\n\n rendered += renderContent(item);\n active = desired;\n }\n\n for (const format of active.toReversed()) {\n rendered += format.close;\n }\n return rendered;\n}\n\nfunction getAnnotationFormats(\n richText: readonly RichTextItemResponse[],\n index: number,\n item: RichTextItemResponse,\n): AnnotationFormat[] {\n if (item.type === \"equation\") return [];\n\n return ANNOTATION_FORMATS.filter((format) => item.annotations[format.name])\n .map((format) => ({\n format,\n runEnd: getAnnotationRunEnd(richText, index, format.name),\n }))\n .sort((left, right) => right.runEnd - left.runEnd)\n .map(({ format }) => format);\n}\n\nfunction getAnnotationRunEnd(\n richText: readonly RichTextItemResponse[],\n start: number,\n name: AnnotationName,\n): number {\n let end = start;\n for (const [offset, next] of richText.slice(start + 1).entries()) {\n if (next.type === \"equation\" || !next.annotations[name]) break;\n end = start + offset + 1;\n }\n return end;\n}\n\nfunction renderContent(item: RichTextItemResponse): string {\n if (item.type === \"equation\") {\n return inlineEquation(item.equation.expression);\n }\n return item.href ? link(item.plain_text, item.href) : item.plain_text;\n}\n","import type { BlockObjectResponse } from \"@notionhq/client\";\nimport { DEFAULT_CHILD_PAGE_MARKER } from \"./defaults.js\";\nimport * as md from \"./markdown.js\";\nimport { renderPlainText, renderRichText } from \"./rich-text.js\";\nimport type {\n BlockTransformerContext,\n BlockTransformers,\n ChildPageWrapper,\n NoblogBlock,\n NotionReader,\n} from \"./types.js\";\n\nexport interface BlockNode {\n readonly block: NoblogBlock;\n readonly children: BlockNode[];\n}\n\nexport interface BlockRenderContext extends BlockTransformerContext {\n readonly childPageMarker?: string;\n readonly childPageWrapper?: ChildPageWrapper;\n readonly transformers?: BlockTransformers;\n}\n\nexport interface RenderedBlocks {\n content: string;\n childPageIds: string[];\n}\n\nexport async function loadBlockTree(\n blockId: string,\n reader: NotionReader,\n ancestors: ReadonlySet<string> = new Set(),\n): Promise<BlockNode[]> {\n if (ancestors.has(blockId)) return [];\n\n const blocks = await reader.getBlockChildren(blockId);\n const childAncestors = new Set(ancestors).add(blockId);\n const nodes: BlockNode[] = [];\n\n for (const block of blocks) {\n if (block.type === \"unsupported\") continue;\n\n const childSourceId = getChildSourceId(block);\n const children =\n childSourceId && !childAncestors.has(childSourceId)\n ? await loadBlockTree(childSourceId, reader, childAncestors)\n : [];\n nodes.push({ block, children });\n }\n\n return nodes;\n}\n\nfunction getChildSourceId(block: NoblogBlock): string | undefined {\n if (block.type === \"child_page\" || block.type === \"child_database\") {\n return undefined;\n }\n if (block.type === \"synced_block\") {\n return (\n block.synced_block.synced_from?.block_id ??\n (block.has_children ? block.id : undefined)\n );\n }\n return block.has_children ? block.id : undefined;\n}\n\nexport async function renderBlockTree(\n nodes: readonly BlockNode[],\n context: BlockRenderContext,\n): Promise<RenderedBlocks> {\n const result: RenderedBlocks = { content: \"\", childPageIds: [] };\n let numberedListIndex = 0;\n\n for (const node of nodes) {\n const { block } = node;\n numberedListIndex =\n block.type === \"numbered_list_item\" ? numberedListIndex + 1 : 0;\n\n const children =\n block.type === \"table\"\n ? { content: \"\", childPageIds: [] }\n : await renderBlockTree(node.children, context);\n const content = await renderBlockNode(\n node,\n children.content,\n numberedListIndex || undefined,\n context,\n );\n\n if (block.type === \"toggle\") {\n result.content += content;\n } else {\n const lineWrap = isListBlock(block) ? \"\" : \"\\n\";\n result.content += `${lineWrap}${content}\\n${lineWrap}`;\n if (!containsOwnChildren(block)) {\n result.content += children.content;\n }\n }\n\n if (block.type === \"child_page\") {\n result.childPageIds.push(block.id);\n } else {\n result.childPageIds.push(...children.childPageIds);\n }\n }\n\n return result;\n}\n\nfunction isListBlock(block: NoblogBlock): boolean {\n return (\n block.type === \"to_do\" ||\n block.type === \"bulleted_list_item\" ||\n block.type === \"numbered_list_item\"\n );\n}\n\nfunction containsOwnChildren(block: NoblogBlock): boolean {\n return (\n block.type === \"child_page\" ||\n block.type === \"callout\" ||\n block.type === \"bulleted_list_item\" ||\n block.type === \"numbered_list_item\" ||\n block.type === \"table\"\n );\n}\n\nfunction transformBlock<K extends NoblogBlock[\"type\"]>(\n type: K,\n block: Extract<NoblogBlock, { type: NoInfer<K> }>,\n context: BlockRenderContext,\n): string | undefined | Promise<string | undefined> {\n const transformer = context.transformers?.[type];\n return transformer?.(block, context);\n}\n\nasync function renderBlockNode(\n node: BlockNode,\n children: string,\n listNumber: number | undefined,\n context: BlockRenderContext,\n): Promise<string> {\n const { block } = node;\n const transformed = await transformBlock(block.type, block, context);\n const content =\n transformed ??\n (await renderDefaultBlock(\n node,\n context.childPageMarker ?? DEFAULT_CHILD_PAGE_MARKER,\n context.childPageWrapper,\n ));\n\n switch (block.type) {\n case \"toggle\":\n return md.toggle(content, children);\n case \"callout\":\n return md.callout(\n children ? `${content}\\n${children}`.trim() : content,\n block.callout.icon,\n );\n case \"bulleted_list_item\":\n return md.bullet(withIndentedChildren(content, children, 2));\n case \"numbered_list_item\":\n return md.bullet(\n withIndentedChildren(\n content,\n children,\n `${listNumber ?? 1}. `.length,\n ),\n listNumber,\n );\n default:\n return content;\n }\n}\n\nfunction withIndentedChildren(\n content: string,\n children: string,\n markerWidth: number,\n): string {\n const nested = children.trim();\n if (!nested) return content;\n\n const indentation = \" \".repeat(markerWidth);\n const indented = nested\n .split(\"\\n\")\n .map((line) => (line ? `${indentation}${line}` : \"\"))\n .join(\"\\n\");\n return `${content}\\n${indented}`;\n}\n\nasync function renderDefaultBlock(\n node: BlockNode,\n childPageMarker: string,\n childPageWrapper: ChildPageWrapper | undefined,\n): Promise<string> {\n const { block } = node;\n\n switch (block.type) {\n case \"image\": {\n const url = getFileUrl(block.image);\n const caption = renderPlainText(block.image.caption);\n const alt = caption.trim() || getFileName(url) || \"image\";\n return md.image(alt, url);\n }\n case \"divider\":\n return md.divider();\n case \"equation\":\n return md.equation(block.equation.expression);\n case \"pdf\":\n return renderFileLink(block.pdf, block.type);\n case \"file\":\n return renderFileLink(block.file, block.type);\n case \"video\":\n return renderFileLink(block.video, block.type);\n case \"audio\":\n return renderFileLink(block.audio, block.type);\n case \"embed\":\n return md.link(block.type, block.embed.url);\n case \"bookmark\":\n return md.link(block.type, block.bookmark.url);\n case \"link_preview\":\n return md.link(block.type, block.link_preview.url);\n case \"link_to_page\": {\n const target = block.link_to_page;\n if (target.type !== \"page_id\" && target.type !== \"database_id\") {\n return \"\";\n }\n const id = target.type === \"page_id\" ? target.page_id : target.database_id;\n return md.link(block.type, `https://www.notion.so/${id}`);\n }\n case \"child_page\":\n return renderChildPageLink(block, childPageMarker, childPageWrapper);\n case \"child_database\":\n return md.heading2(block.child_database.title || \"child_database\");\n case \"table\":\n return renderTable(node.children, block.table.has_column_header);\n case \"code\":\n return md.codeBlock(\n renderRichText(block.code.rich_text),\n block.code.language,\n );\n case \"heading_1\":\n return md.heading1(renderRichText(block.heading_1.rich_text));\n case \"heading_2\":\n return md.heading2(renderRichText(block.heading_2.rich_text));\n case \"heading_3\":\n return md.heading3(renderRichText(block.heading_3.rich_text));\n case \"heading_4\":\n return md.heading4(renderRichText(block.heading_4.rich_text));\n case \"quote\":\n return md.quote(renderRichText(block.quote.rich_text));\n case \"to_do\":\n return md.todo(\n renderRichText(block.to_do.rich_text),\n block.to_do.checked,\n );\n case \"paragraph\":\n return renderRichText(block.paragraph.rich_text);\n case \"toggle\":\n return renderRichText(block.toggle.rich_text);\n case \"callout\":\n return renderRichText(block.callout.rich_text);\n case \"bulleted_list_item\":\n return renderRichText(block.bulleted_list_item.rich_text);\n case \"numbered_list_item\":\n return renderRichText(block.numbered_list_item.rich_text);\n case \"template\":\n return renderRichText(block.template.rich_text);\n default:\n return \"\";\n }\n}\n\ntype FileContent =\n | Extract<BlockObjectResponse, { type: \"image\" }>[\"image\"]\n | Extract<BlockObjectResponse, { type: \"pdf\" }>[\"pdf\"]\n | Extract<BlockObjectResponse, { type: \"file\" }>[\"file\"]\n | Extract<BlockObjectResponse, { type: \"video\" }>[\"video\"]\n | Extract<BlockObjectResponse, { type: \"audio\" }>[\"audio\"];\n\nfunction getFileUrl(content: FileContent): string {\n if (content.type === \"external\") return content.external.url;\n return content.file.url;\n}\n\nfunction getFileName(url: string): string | undefined {\n return url.match(/[^\\/\\\\&?]+\\.\\w{3,4}(?=([?&].*$|$))/)?.[0];\n}\n\nfunction renderFileLink(content: FileContent, fallbackTitle: string): string {\n const url = getFileUrl(content);\n const caption = renderPlainText(content.caption);\n const title = caption.trim() || getFileName(url) || fallbackTitle;\n return md.link(title, url);\n}\n\nfunction renderChildPageLink(\n block: Extract<BlockObjectResponse, { type: \"child_page\" }>,\n marker: string,\n wrapper: ChildPageWrapper | undefined,\n): string {\n // The theme consumes the marker or wrapper to render subpage links.\n const link = md.link(block.child_page.title, `./${block.id}.md`);\n return wrapper === undefined\n ? `${marker} ${link}`\n : `${wrapper.head}${link}${wrapper.tail}`;\n}\n\nfunction renderTable(\n nodes: readonly BlockNode[],\n hasColumnHeader: boolean,\n): string {\n const rows = nodes.flatMap(({ block }) => {\n if (block.type !== \"table_row\") return [];\n return [\n block.table_row.cells.map((cell) => renderRichText(cell)),\n ];\n });\n return md.table(rows, hasColumnHeader);\n}\n","import type { PageObjectResponse } from \"@notionhq/client\";\nimport { loadBlockTree, renderBlockTree } from \"./blocks.js\";\nimport {\n DEFAULT_CHILD_PAGE_MARKER,\n DEFAULT_LAYOUT_PATH,\n} from \"./defaults.js\";\nimport { renderFrontmatter } from \"./frontmatter.js\";\nimport type { NotionPropertyMap } from \"./config.js\";\nimport type {\n BlockTransformers,\n ChildPageWrapper,\n FrontmatterAdapter,\n NotionReader,\n RenderedPage,\n RenderOptions,\n} from \"./types.js\";\n\ninterface PageRenderContext {\n readonly reader: NotionReader;\n readonly databasePageIds: ReadonlySet<string>;\n readonly layoutPath: string;\n readonly propertyMap: NotionPropertyMap | undefined;\n readonly frontmatter: FrontmatterAdapter | undefined;\n readonly recursive: boolean;\n readonly childPageMarker: string;\n readonly childPageWrapper: ChildPageWrapper | undefined;\n readonly transformers: BlockTransformers | undefined;\n readonly renderedPages: Map<string, Promise<RenderedPage>>;\n}\n\nexport async function renderPage(\n pageId: string,\n reader: NotionReader,\n options: RenderOptions = {},\n): Promise<RenderedPage> {\n const context = createContext([pageId], reader, options);\n return collectPage(pageId, context);\n}\n\nexport async function renderPages(\n posts: readonly PageObjectResponse[],\n reader: NotionReader,\n options: RenderOptions = {},\n): Promise<RenderedPage[]> {\n const context = createContext(\n posts.map((post) => post.id),\n reader,\n options,\n );\n\n for (const post of posts) {\n await collectPage(post.id, context, post);\n }\n\n return Promise.all([...context.renderedPages.values()]);\n}\n\nfunction createContext(\n databasePageIds: readonly string[],\n reader: NotionReader,\n options: RenderOptions,\n): PageRenderContext {\n return {\n reader,\n databasePageIds: new Set(databasePageIds),\n layoutPath: options.layoutPath ?? DEFAULT_LAYOUT_PATH,\n propertyMap: options.propertyMap,\n frontmatter: options.frontmatter,\n recursive: options.recursive ?? true,\n childPageMarker: options.childPageMarker ?? DEFAULT_CHILD_PAGE_MARKER,\n childPageWrapper: options.childPageWrapper,\n transformers: options.transformers,\n renderedPages: new Map(),\n };\n}\n\nasync function collectPage(\n pageId: string,\n context: PageRenderContext,\n post?: PageObjectResponse,\n): Promise<RenderedPage> {\n const cached = context.renderedPages.get(pageId);\n if (cached) return cached;\n\n const rendering = renderSinglePage(pageId, context, post);\n context.renderedPages.set(pageId, rendering);\n const rendered = await rendering;\n\n if (context.recursive) {\n for (const childPageId of rendered.childPageIds) {\n await collectPage(childPageId, context);\n }\n }\n\n return rendered;\n}\n\nasync function renderSinglePage(\n pageId: string,\n context: PageRenderContext,\n post?: PageObjectResponse,\n): Promise<RenderedPage> {\n const [page, blockTree] = await Promise.all([\n post ? Promise.resolve(post) : context.reader.getPage(pageId),\n loadBlockTree(pageId, context.reader),\n ]);\n const rendered = await renderBlockTree(blockTree, {\n pageId,\n childPageMarker: context.childPageMarker,\n ...(context.childPageWrapper !== undefined\n ? { childPageWrapper: context.childPageWrapper }\n : {}),\n ...(context.transformers !== undefined\n ? { transformers: context.transformers }\n : {}),\n });\n const frontmatter = renderFrontmatter(page, {\n layoutPath: context.layoutPath,\n ...(context.propertyMap !== undefined\n ? { propertyMap: context.propertyMap }\n : {}),\n ...(context.frontmatter !== undefined\n ? { frontmatter: context.frontmatter }\n : {}),\n isDatabasePage: context.databasePageIds.has(pageId),\n });\n\n return {\n id: pageId,\n content: frontmatter + rendered.content,\n childPageIds: [...new Set(rendered.childPageIds)],\n isDatabasePage: context.databasePageIds.has(pageId),\n };\n}\n","import type { PageObjectResponse } from \"@notionhq/client\";\nimport { dirname, relative, resolve, sep } from \"node:path\";\nimport type { NotionPropertyMap } from \"./config.js\";\nimport { extractPageMetadata } from \"./metadata.js\";\n\nexport interface SelfieMatch {\n readonly page: PageObjectResponse;\n readonly title: string;\n}\n\n/**\n * Find the database post whose title exactly matches the requested selfie\n * title. Titles are compared trimmed on both sides.\n */\nexport function findSelfiePage(\n posts: readonly PageObjectResponse[],\n title: string,\n propertyMap?: NotionPropertyMap,\n): SelfieMatch | undefined {\n for (const post of posts) {\n const metadata = extractPageMetadata(post, propertyMap);\n if (metadata.title.trim() === title) {\n return { page: post, title: metadata.title };\n }\n }\n return undefined;\n}\n\n/**\n * Return the Markdown document with its frontmatter `author` set to the given\n * value. An existing top-level `author` key is replaced so the result always\n * reflects the provided author; the document body is left untouched.\n */\nexport function upsertFrontmatterAuthor(\n content: string,\n author: string,\n): string {\n const authorLine = `author: ${JSON.stringify(author)}`;\n const lines = content.split(\"\\n\");\n if (lines[0] !== \"---\") {\n return `---\\n${authorLine}\\n---\\n${content}`;\n }\n\n const closing = lines.indexOf(\"---\", 1);\n if (closing === -1) {\n return `---\\n${authorLine}\\n---\\n${content}`;\n }\n\n const frontmatterLines = lines\n .slice(1, closing)\n .filter((line) => !/^author\\s*:/u.test(line));\n return [\"---\", authorLine, ...frontmatterLines, ...lines.slice(closing)].join(\n \"\\n\",\n );\n}\n\n// Child-page links emitted by the renderer target `./<page-id>.md`, which\n// only resolves for files sitting in the canonical store directory.\nconst STORE_CHILD_LINK = /\\]\\(\\.\\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\.md\\)/g;\n\n/**\n * Rewrite `./<page-id>.md` child-page links so they resolve from the selfie\n * file's directory instead of the canonical store directory. Links are left\n * unchanged when the selfie file already sits next to the store files, and\n * non-store relative links (for example user asset links) are never touched.\n */\nexport function relativizeStoreLinks(\n content: string,\n selfiePath: string,\n storePath: string,\n): string {\n const storeDirectory = relative(\n dirname(resolve(selfiePath)),\n resolve(storePath),\n );\n const prefix =\n storeDirectory === \"\"\n ? \"\"\n : `${storeDirectory.split(sep).join(\"/\")}/`;\n if (prefix === \"\") return content;\n\n return content.replace(\n STORE_CHILD_LINK,\n (_, pageId: string) => `](./${prefix}${pageId}.md)`,\n );\n}\n","import { randomUUID } from \"node:crypto\";\nimport {\n lstat,\n mkdir,\n readlink,\n rename,\n rm,\n symlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { getErrorCode } from \"../errors.js\";\nimport {\n DEFAULT_LAYOUT_PATH,\n DEFAULT_SAVE_PATH,\n DEFAULT_SELFIE_PATH,\n DEFAULT_STORE_PATH,\n} from \"./defaults.js\";\nimport type {\n RenderedPage,\n StoreLayoutOptions,\n WritePagesOptions,\n} from \"./types.js\";\n\nexport async function writePages(\n pages: readonly RenderedPage[],\n options: WritePagesOptions = {},\n): Promise<string[]> {\n const savePath = options.savePath ?? DEFAULT_SAVE_PATH;\n const storePath = options.storePath ?? DEFAULT_STORE_PATH;\n const writtenPaths: string[] = [];\n await mkdir(storePath, { recursive: true });\n\n for (const page of pages) {\n const storedPath = join(storePath, `${page.id}.md`);\n const storedStats = await getStats(storedPath);\n if (storedStats && !storedStats.isFile()) {\n throw new Error(`Page store path is not a regular file: ${storedPath}`);\n }\n await writeFile(storedPath, page.content, \"utf8\");\n\n if (!page.isDatabasePage) continue;\n\n const viewPath = join(savePath, `${page.id}.md`);\n await mkdir(savePath, { recursive: true });\n await linkStoredPage(storedPath, viewPath);\n writtenPaths.push(viewPath);\n }\n\n return writtenPaths;\n}\n\nexport async function writeSelfie(\n content: string,\n selfiePath: string = DEFAULT_SELFIE_PATH,\n): Promise<string> {\n const directory = dirname(selfiePath);\n if (directory !== \".\") {\n await mkdir(directory, { recursive: true });\n }\n await writeFile(selfiePath, content, \"utf8\");\n return selfiePath;\n}\n\nexport function resolveStoreLayoutPath(\n options: StoreLayoutOptions = {},\n): string {\n const savePath = options.savePath ?? DEFAULT_SAVE_PATH;\n const storePath = options.storePath ?? DEFAULT_STORE_PATH;\n const layoutPath = options.layoutPath ?? DEFAULT_LAYOUT_PATH;\n const layoutTarget = resolve(savePath, layoutPath);\n return relative(resolve(storePath), layoutTarget).split(sep).join(\"/\");\n}\n\nasync function linkStoredPage(\n storedPath: string,\n viewPath: string,\n): Promise<void> {\n if (resolve(storedPath) === resolve(viewPath)) return;\n\n const viewStats = await getStats(viewPath);\n if (viewStats && !viewStats.isFile() && !viewStats.isSymbolicLink()) {\n throw new Error(`Refusing to replace non-file output: ${viewPath}`);\n }\n\n const target = relative(dirname(viewPath), storedPath);\n if (viewStats?.isSymbolicLink() && (await readlink(viewPath)) === target) {\n return;\n }\n\n const temporaryPath = `${viewPath}.noblog-${randomUUID()}.tmp`;\n try {\n await symlink(target, temporaryPath, \"file\");\n await rename(temporaryPath, viewPath);\n } catch (error) {\n await rm(temporaryPath, { force: true }).catch(() => {});\n throw error;\n }\n}\n\nasync function getStats(path: string) {\n try {\n return await lstat(path);\n } catch (error: unknown) {\n if (getErrorCode(error) === \"ENOENT\") return undefined;\n throw error;\n }\n}\n","import {\n Client,\n isFullBlock,\n isFullDatabase,\n isFullPage,\n isNotionClientError,\n} from \"@notionhq/client\";\nimport type {\n BlockObjectResponse,\n PageObjectResponse,\n} from \"@notionhq/client\";\nimport type { NotionReader } from \"../noblog/types.js\";\n\nexport interface NotionSourceConfig {\n readonly notionApiSecret: string;\n readonly databaseId: string;\n}\n\nexport interface NotionSource extends NotionReader {\n listPages(): Promise<PageObjectResponse[]>;\n}\n\nexport function createNotionSource(\n config: NotionSourceConfig,\n): NotionSource {\n const client = new Client({ auth: config.notionApiSecret });\n return {\n listPages: () => listPages(client, config.databaseId),\n getPage: (pageId) => getPage(client, pageId),\n getBlockChildren: (blockId) => getBlockChildren(client, blockId),\n };\n}\n\nasync function listPages(\n client: Client,\n databaseId: string,\n): Promise<PageObjectResponse[]> {\n const database = await withRetry(() =>\n client.databases.retrieve({ database_id: databaseId }),\n );\n if (!isFullDatabase(database)) {\n throw new Error(`Notion database ${databaseId} is not a full database`);\n }\n\n const pages: PageObjectResponse[] = [];\n for (const source of database.data_sources) {\n let startCursor: string | undefined;\n do {\n const response = await withRetry(() =>\n client.dataSources.query({\n data_source_id: source.id,\n filter: {\n property: \"publish\",\n checkbox: { equals: true },\n },\n sorts: [{ property: \"date\", direction: \"descending\" }],\n page_size: 100,\n ...(startCursor !== undefined\n ? { start_cursor: startCursor }\n : {}),\n }),\n );\n pages.push(...response.results.filter(isFullPage));\n startCursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (startCursor);\n }\n\n return pages;\n}\n\nasync function getPage(\n client: Client,\n pageId: string,\n): Promise<PageObjectResponse> {\n const page = await withRetry(() => client.pages.retrieve({ page_id: pageId }));\n if (!isFullPage(page)) {\n throw new Error(`Notion page ${pageId} is not a full page`);\n }\n return page;\n}\n\nasync function getBlockChildren(\n client: Client,\n blockId: string,\n): Promise<BlockObjectResponse[]> {\n const blocks: BlockObjectResponse[] = [];\n let startCursor: string | undefined;\n\n do {\n const response = await withRetry(() =>\n client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(startCursor !== undefined ? { start_cursor: startCursor } : {}),\n }),\n );\n blocks.push(...response.results.filter(isFullBlock));\n startCursor = response.next_cursor ?? undefined;\n } while (startCursor);\n\n return blocks;\n}\n\nconst MAX_ATTEMPTS = 3;\nconst INITIAL_RETRY_DELAY_MS = 500;\n\nasync function withRetry<T>(operation: () => Promise<T>): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await operation();\n } catch (error: unknown) {\n if (attempt === MAX_ATTEMPTS - 1 || !isRetryable(error)) throw error;\n await delay(INITIAL_RETRY_DELAY_MS * 2 ** attempt);\n }\n }\n}\n\nfunction isRetryable(error: unknown): boolean {\n if (!isNotionClientError(error) || !(\"status\" in error)) return true;\n return error.status === 408 || error.status === 429 || error.status >= 500;\n}\n\nfunction delay(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n"],"mappings":";;;;;;;;;AAqBA,MAAM,qBAAqB;CACzB,aAAa;CACb,MAAM;CACN,aAAa;CACb,UAAU;AACZ;AAEA,SAAgB,oBACd,MACA,aAAgC,CAAC,GACnB;CACd,MAAM,gBAAgB,IAAI,IAAoB,OAAO,QAAQ,UAAU,CAAC;CACxE,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,kBAAkB,GACzD,IAAI,CAAC,cAAc,IAAI,GAAG,GAAG,cAAc,IAAI,KAAK,IAAI;CAG1D,MAAM,SAAwC,CAAC;CAC/C,IAAI,CAAC,cAAc,IAAI,OAAO,GAI5B,OAAO,WAAW,kBAHI,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,UAClD,aAAa,SAAS,SAAS,OAEc,CAAC;CAEnD,KAAK,MAAM,CAAC,KAAK,SAAS,eACxB,OAAO,OAAO,kBAAkB,KAAK,WAAW,KAAK;CAGvD,OAAO;EACL,OAAO,SAAS,OAAO,QAAQ,KAAK;EACpC,aAAa,SAAS,OAAO,cAAc;EAC3C,MAAM,cAAc,OAAO,OAAO,KAAK,CAAC;EACxC,aAAa,SAAS,OAAO,cAAc;EAC3C,UAAU,UAAU,OAAO,WAAW;EACtC;CACF;AACF;AAEA,SAAS,kBAAkB,UAA0C;CACnE,IAAI,CAAC,UAAU,OAAO,KAAA;CAEtB,QAAQ,SAAS,MAAjB;EACE,KAAK,SACH,OAAO,UAAU,SAAS,KAAK;EACjC,KAAK,aACH,OAAO,UAAU,SAAS,SAAS;EACrC,KAAK,gBACH,OAAO,SAAS,aAAa,KAAK,EAAE,WAAW,IAAI;EACrD,KAAK,UACH,OAAO,SAAS,QAAQ;EAC1B,KAAK,UACH,OAAO,SAAS,QAAQ;EAC1B,KAAK,YACH,OAAO,SAAS;EAClB,KAAK,UACH,OAAO,SAAS,UAAU,KAAA;EAC5B,KAAK,QACH,OAAO,SAAS,MAAM;EACxB,KAAK,OACH,OAAO,SAAS,OAAO,KAAA;EACzB,KAAK,SACH,OAAO,SAAS,SAAS,KAAA;EAC3B,KAAK,gBACH,OAAO,SAAS,gBAAgB,KAAA;EAClC,KAAK,UACH,OAAO,SAAS,OAAO,IAAI,UAAU;EACvC,KAAK,YACH,OAAO,SAAS,SAAS,KAAK,EAAE,SAAS,EAAE;EAC7C,KAAK,SACH,OAAO,SAAS,MAAM,KAAK,SACzB,UAAU,OAAO,KAAK,KAAK,MAAM,KAAK,SAAS,GACjD;EACF,KAAK,WACH,OAAO,iBAAiB,SAAS,OAAO;EAC1C,KAAK,gBACH,OAAO,SAAS;EAClB,KAAK,oBACH,OAAO,SAAS;EAClB,KAAK,cACH,OAAO,WAAW,SAAS,UAAU;EACvC,KAAK,kBACH,OAAO,WAAW,SAAS,cAAc;EAC3C,KAAK,aACH,OAAO,SAAS,UAAU,SACtB,GAAG,SAAS,UAAU,OAAO,GAAG,SAAS,UAAU,WACnD,OAAO,SAAS,UAAU,MAAM;EACtC,SACE;CACJ;AACF;AAEA,SAAS,iBACP,SACe;CACf,QAAQ,QAAQ,MAAhB;EACE,KAAK,UACH,OAAO,QAAQ,UAAU,KAAA;EAC3B,KAAK,UACH,OAAO,QAAQ,UAAU,KAAA;EAC3B,KAAK,WACH,OAAO,QAAQ,WAAW,KAAA;EAC5B,KAAK,QACH,OAAO,QAAQ,MAAM;CACzB;AACF;AAEA,SAAS,UACP,UACQ;CACR,OAAO,SAAS,KAAK,SAAS,KAAK,UAAU,CAAC,CAAC,KAAK,EAAE;AACxD;AAEA,SAAS,WAAW,QAGT;CACT,OAAO,OAAO,QAAQ,OAAO;AAC/B;AAEA,SAAS,SAAS,OAA0C;CAC1D,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAS,UAAU,OAA2C;CAC5D,OAAO,OAAO,UAAU,YAAY,QAAQ,KAAA;AAC9C;AAEA,SAAS,cAAc,OAAqD;CAC1E,IAAI,OAAO,UAAU,UAAU,OAAO,CAAC,KAAK;CAC5C,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAA;AACxC;;;ACxIA,SAAgB,kBACd,MACA,UAA8B,CAAC,GACvB;CACR,MAAM,EAAE,aAAa,wBAAwB;CAC7C,IAAI,QAAQ,aAAa;EACvB,MAAM,WAAW,oBAAoB,MAAM,QAAQ,WAAW;EAO9D,OAAO,qBANQ,QAAQ,YAAY;GACjC;GACA;GACA;GACA,gBAAgB,QAAQ,kBAAkB;EAC5C,CACiC,CAAC;CACpC;CAEA,IAAI,QAAQ;CACZ,IAAI,cAAc;CAClB,IAAI,OAAiB,CAAC;CACtB,IAAI,UAAU;CACd,IAAI,WAAW;CAEf,KAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,KAAK,UAAU,GAAG;EAC9D,QAAQ,SAAS,MAAjB;GACE,KAAK;IACH,QAAQ,SAAS,MAAM,KAAK,SAAS,KAAK,UAAU,CAAC,CAAC,KAAK,EAAE;IAC7D;GACF,KAAK;IACH,IAAI,SAAS,QACX,OAAO,SAAS,aAAa,KAAK,QAAQ,IAAI,IAAI;IAEpD;GACF,KAAK;IACH,IAAI,SAAS,QACX,UACE,SAAS,MAAM,0BAAS,IAAI,KAAK,EAAA,CAAE,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;IAEhE;EACJ;EAEA,IAAI,SAAS,cAAc,SAAS,SAAS,YAC3C,WAAW,SAAS,WAAW,SAAS;EAE1C,IAAI,SAAS,iBAAiB,SAAS,SAAS,aAC9C,cAAc,SAAS,UACpB,KAAK,SAAS,KAAK,UAAU,CAAC,CAC9B,KAAK,EAAE;CAEd;CAEA,OACE;UACW,WAAW,WACZ,KAAK,UAAU,KAAK,EAAE,iBAChB,KAAK,UAAU,WAAW,EAAE,UACnC,KAAK,UAAU,IAAI,EAAE,aAClB,KAAK,UAAU,OAAO,EAAE,cACvB,KAAK,UAAU,QAAQ,EAAE;;AAG1C;AAEA,SAAS,qBAAqB,QAAmC;CAC/D,IACE,WAAW,QACX,OAAO,WAAW,YAClB,MAAM,QAAQ,MAAM,KACnB,OAAO,eAAe,MAAM,MAAM,OAAO,aACxC,OAAO,eAAe,MAAM,MAAM,MAEpC,MAAM,IAAI,MAAM,gDAAgD;CAGlE,MAAM,UAAU,OAAO,QAAQ,MAAM,CAAC,CAAC,QACpC,GAAG,WAAW,UAAU,KAAA,CAC3B;CACA,IAAI,QAAQ,WAAW,GAAG,OAAO;CAMjC,OAAO,QAJM,UAAU,OAAO,YAAY,OAAO,GAAG;EAClD,uBAAuB;EACvB,QAAQ;CACV,CACkB,EAAE;AACtB;;;AC9FA,MAAa,cAAc,SAAiB,KAAK,KAAK;AACtD,MAAa,kBAAkB,SAAiB,IAAI,KAAK;AACzD,MAAa,QAAQ,SAAiB,KAAK,KAAK;AAChD,MAAa,UAAU,SAAiB,IAAI,KAAK;AACjD,MAAa,iBAAiB,SAAiB,KAAK,KAAK;AACzD,MAAa,aAAa,SAAiB,MAAM,KAAK;AACtD,MAAa,QAAQ,MAAc,SAAiB,IAAI,KAAK,IAAI,KAAK;AAEtE,SAAgB,UAAU,MAAc,WAAW,IAAY;CAE7D,OAAO,SADoB,aAAa,eAAe,SAAS,SAC7B,IAAI,KAAK;AAC9C;AAEA,MAAa,YAAY,SAAiB,OAAO,KAAK;AACtD,MAAa,YAAY,SAAiB,KAAK;AAC/C,MAAa,YAAY,SAAiB,MAAM;AAChD,MAAa,YAAY,SAAiB,OAAO;AACjD,MAAa,YAAY,SAAiB,QAAQ;AAClD,MAAa,SAAS,SAAiB,KAAK,KAAK,QAAQ,OAAO,QAAQ;AAExE,SAAgB,QAAQ,MAAc,MAA4B;CAEhE,OAAO,KADO,MAAM,SAAS,UAAU,GAAG,KAAK,MAAM,KAAK,KACtC,KAAK,QAAQ,OAAO,QAAQ;AAClD;AAEA,SAAgB,OAAO,MAAc,OAAwB;CAC3D,MAAM,WAAW,KAAK,KAAK;CAC3B,OAAO,UAAU,KAAA,IAAY,KAAK,aAAa,GAAG,MAAM,IAAI;AAC9D;AAEA,MAAa,QAAQ,MAAc,YACjC,UAAU,SAAS,SAAS,SAAS;AAEvC,eAAsB,MACpB,KACA,MACA,kBAAkB,OACD;CACjB,IAAI,CAAC,mBAAmB,KAAK,WAAW,OAAO,GAAG;EAChD,IAAI,KAAK,WAAW,OAAO,GAEzB,OAAO,KAAK,IAAI,0BADD,KAAK,MAAM,GAAG,CAAC,CAAC,IACgB,EAAE;EAEnD,OAAO,KAAK,IAAI,IAAI,KAAK;CAC3B;CAEA,MAAM,WAAW,MAAM,MAAM,IAAI;CACjC,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MACR,6BAA6B,SAAS,OAAO,GAAG,SAAS,YAC3D;CAEF,MAAM,SAAS,MAAM,SAAS,YAAY;CAC1C,OAAO,KAAK,IAAI,0BAA0B,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ,EAAE;AACnF;AAEA,MAAa,gBAAgB;AAE7B,SAAgB,OAAO,SAAkB,UAA2B;CAClE,IAAI,CAAC,SAAS,OAAO,YAAY;CACjC,OAAO,uBAAuB,QAAQ,cAAc,YAAY,GAAG;AACrE;AAEA,SAAgB,MAAM,OAAmB,kBAAkB,MAAc;CACvE,IAAI,mBAAmB,MAAM,WAAW,GAAG,OAAO,cAAc,KAAK;CAErE,MAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC;CACxD,IAAI,UAAU,GAAG,OAAO;CAExB,OAAO,cAAc,CADD,MAAM,KAAK,EAAE,QAAQ,MAAM,SAAS,EACxB,GAAG,GAAG,KAAK,CAAC;AAC9C;;;ACzDA,MAAM,qBAAkD;CACtD;EAAE,MAAM;EAAiB,MAAM;EAAM,OAAO;CAAK;CACjD;EAAE,MAAM;EAAa,MAAM;EAAO,OAAO;CAAO;CAChD;EAAE,MAAM;EAAU,MAAM;EAAK,OAAO;CAAI;CACxC;EAAE,MAAM;EAAQ,MAAM;EAAM,OAAO;CAAK;CACxC;EAAE,MAAM;EAAQ,MAAM;EAAK,OAAO;CAAI;AACxC;AAEA,SAAgB,gBACd,UACQ;CACR,OAAO,SAAS,KAAK,SAAS,KAAK,UAAU,CAAC,CAAC,KAAK,EAAE;AACxD;AAEA,SAAgB,eACd,UACQ;CACR,IAAI,WAAW;CACf,IAAI,SAA6B,CAAC;CAElC,KAAK,MAAM,CAAC,OAAO,SAAS,SAAS,QAAQ,GAAG;EAC9C,MAAM,UAAU,qBAAqB,UAAU,OAAO,IAAI;EAC1D,IAAI,SAAS;EACb,OACE,SAAS,OAAO,UAChB,SAAS,QAAQ,UACjB,OAAO,OAAO,EAAE,SAAS,QAAQ,OAAO,EAAE,MAE1C;EAGF,KAAK,MAAM,UAAU,OAAO,MAAM,MAAM,CAAC,CAAC,WAAW,GACnD,YAAY,OAAO;EAErB,KAAK,MAAM,UAAU,QAAQ,MAAM,MAAM,GACvC,YAAY,OAAO;EAGrB,YAAY,cAAc,IAAI;EAC9B,SAAS;CACX;CAEA,KAAK,MAAM,UAAU,OAAO,WAAW,GACrC,YAAY,OAAO;CAErB,OAAO;AACT;AAEA,SAAS,qBACP,UACA,OACA,MACoB;CACpB,IAAI,KAAK,SAAS,YAAY,OAAO,CAAC;CAEtC,OAAO,mBAAmB,QAAQ,WAAW,KAAK,YAAY,OAAO,KAAK,CAAC,CACxE,KAAK,YAAY;EAChB;EACA,QAAQ,oBAAoB,UAAU,OAAO,OAAO,IAAI;CAC1D,EAAE,CAAC,CACF,MAAM,MAAM,UAAU,MAAM,SAAS,KAAK,MAAM,CAAC,CACjD,KAAK,EAAE,aAAa,MAAM;AAC/B;AAEA,SAAS,oBACP,UACA,OACA,MACQ;CACR,IAAI,MAAM;CACV,KAAK,MAAM,CAAC,QAAQ,SAAS,SAAS,MAAM,QAAQ,CAAC,CAAC,CAAC,QAAQ,GAAG;EAChE,IAAI,KAAK,SAAS,cAAc,CAAC,KAAK,YAAY,OAAO;EACzD,MAAM,QAAQ,SAAS;CACzB;CACA,OAAO;AACT;AAEA,SAAS,cAAc,MAAoC;CACzD,IAAI,KAAK,SAAS,YAChB,OAAO,eAAe,KAAK,SAAS,UAAU;CAEhD,OAAO,KAAK,OAAO,KAAK,KAAK,YAAY,KAAK,IAAI,IAAI,KAAK;AAC7D;;;ACtEA,eAAsB,cACpB,SACA,QACA,4BAAiC,IAAI,IAAI,GACnB;CACtB,IAAI,UAAU,IAAI,OAAO,GAAG,OAAO,CAAC;CAEpC,MAAM,SAAS,MAAM,OAAO,iBAAiB,OAAO;CACpD,MAAM,iBAAiB,IAAI,IAAI,SAAS,CAAC,CAAC,IAAI,OAAO;CACrD,MAAM,QAAqB,CAAC;CAE5B,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,SAAS,eAAe;EAElC,MAAM,gBAAgB,iBAAiB,KAAK;EAC5C,MAAM,WACJ,iBAAiB,CAAC,eAAe,IAAI,aAAa,IAC9C,MAAM,cAAc,eAAe,QAAQ,cAAc,IACzD,CAAC;EACP,MAAM,KAAK;GAAE;GAAO;EAAS,CAAC;CAChC;CAEA,OAAO;AACT;AAEA,SAAS,iBAAiB,OAAwC;CAChE,IAAI,MAAM,SAAS,gBAAgB,MAAM,SAAS,kBAChD;CAEF,IAAI,MAAM,SAAS,gBACjB,OACE,MAAM,aAAa,aAAa,aAC/B,MAAM,eAAe,MAAM,KAAK,KAAA;CAGrC,OAAO,MAAM,eAAe,MAAM,KAAK,KAAA;AACzC;AAEA,eAAsB,gBACpB,OACA,SACyB;CACzB,MAAM,SAAyB;EAAE,SAAS;EAAI,cAAc,CAAC;CAAE;CAC/D,IAAI,oBAAoB;CAExB,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,EAAE,UAAU;EAClB,oBACE,MAAM,SAAS,uBAAuB,oBAAoB,IAAI;EAEhE,MAAM,WACJ,MAAM,SAAS,UACX;GAAE,SAAS;GAAI,cAAc,CAAC;EAAE,IAChC,MAAM,gBAAgB,KAAK,UAAU,OAAO;EAClD,MAAM,UAAU,MAAM,gBACpB,MACA,SAAS,SACT,qBAAqB,KAAA,GACrB,OACF;EAEA,IAAI,MAAM,SAAS,UACjB,OAAO,WAAW;OACb;GACL,MAAM,WAAW,YAAY,KAAK,IAAI,KAAK;GAC3C,OAAO,WAAW,GAAG,WAAW,QAAQ,IAAI;GAC5C,IAAI,CAAC,oBAAoB,KAAK,GAC5B,OAAO,WAAW,SAAS;EAE/B;EAEA,IAAI,MAAM,SAAS,cACjB,OAAO,aAAa,KAAK,MAAM,EAAE;OAEjC,OAAO,aAAa,KAAK,GAAG,SAAS,YAAY;CAErD;CAEA,OAAO;AACT;AAEA,SAAS,YAAY,OAA6B;CAChD,OACE,MAAM,SAAS,WACf,MAAM,SAAS,wBACf,MAAM,SAAS;AAEnB;AAEA,SAAS,oBAAoB,OAA6B;CACxD,OACE,MAAM,SAAS,gBACf,MAAM,SAAS,aACf,MAAM,SAAS,wBACf,MAAM,SAAS,wBACf,MAAM,SAAS;AAEnB;AAEA,SAAS,eACP,MACA,OACA,SACkD;CAClD,MAAM,cAAc,QAAQ,eAAe;CAC3C,OAAO,cAAc,OAAO,OAAO;AACrC;AAEA,eAAe,gBACb,MACA,UACA,YACA,SACiB;CACjB,MAAM,EAAE,UAAU;CAElB,MAAM,UACJ,MAFwB,eAAe,MAAM,MAAM,OAAO,OAAO,KAGhE,MAAM,mBACL,MACA,QAAQ,mBAAA,KACR,QAAQ,gBACV;CAEF,QAAQ,MAAM,MAAd;EACE,KAAK,UACH,OAAOA,OAAU,SAAS,QAAQ;EACpC,KAAK,WACH,OAAOC,QACL,WAAW,GAAG,QAAQ,IAAI,WAAW,KAAK,IAAI,SAC9C,MAAM,QAAQ,IAChB;EACF,KAAK,sBACH,OAAOC,OAAU,qBAAqB,SAAS,UAAU,CAAC,CAAC;EAC7D,KAAK,sBACH,OAAOA,OACL,qBACE,SACA,UACA,GAAG,cAAc,EAAE,IAAI,MACzB,GACA,UACF;EACF,SACE,OAAO;CACX;AACF;AAEA,SAAS,qBACP,SACA,UACA,aACQ;CACR,MAAM,SAAS,SAAS,KAAK;CAC7B,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,cAAc,IAAI,OAAO,WAAW;CAK1C,OAAO,GAAG,QAAQ,IAJD,OACd,MAAM,IAAI,CAAC,CACX,KAAK,SAAU,OAAO,GAAG,cAAc,SAAS,EAAG,CAAC,CACpD,KAAK,IACqB;AAC/B;AAEA,eAAe,mBACb,MACA,iBACA,kBACiB;CACjB,MAAM,EAAE,UAAU;CAElB,QAAQ,MAAM,MAAd;EACE,KAAK,SAAS;GACZ,MAAM,MAAM,WAAW,MAAM,KAAK;GAGlC,OAAOC,MAFS,gBAAgB,MAAM,MAAM,OAC1B,CAAC,CAAC,KAAK,KAAK,YAAY,GAAG,KAAK,SAC7B,GAAG;EAC1B;EACA,KAAK,WACH,OAAOC,QAAW;EACpB,KAAK,YACH,OAAOC,SAAY,MAAM,SAAS,UAAU;EAC9C,KAAK,OACH,OAAO,eAAe,MAAM,KAAK,MAAM,IAAI;EAC7C,KAAK,QACH,OAAO,eAAe,MAAM,MAAM,MAAM,IAAI;EAC9C,KAAK,SACH,OAAO,eAAe,MAAM,OAAO,MAAM,IAAI;EAC/C,KAAK,SACH,OAAO,eAAe,MAAM,OAAO,MAAM,IAAI;EAC/C,KAAK,SACH,OAAOC,KAAQ,MAAM,MAAM,MAAM,MAAM,GAAG;EAC5C,KAAK,YACH,OAAOA,KAAQ,MAAM,MAAM,MAAM,SAAS,GAAG;EAC/C,KAAK,gBACH,OAAOA,KAAQ,MAAM,MAAM,MAAM,aAAa,GAAG;EACnD,KAAK,gBAAgB;GACnB,MAAM,SAAS,MAAM;GACrB,IAAI,OAAO,SAAS,aAAa,OAAO,SAAS,eAC/C,OAAO;GAET,MAAM,KAAK,OAAO,SAAS,YAAY,OAAO,UAAU,OAAO;GAC/D,OAAOA,KAAQ,MAAM,MAAM,yBAAyB,IAAI;EAC1D;EACA,KAAK,cACH,OAAO,oBAAoB,OAAO,iBAAiB,gBAAgB;EACrE,KAAK,kBACH,OAAOC,SAAY,MAAM,eAAe,SAAS,gBAAgB;EACnE,KAAK,SACH,OAAO,YAAY,KAAK,UAAU,MAAM,MAAM,iBAAiB;EACjE,KAAK,QACH,OAAOC,UACL,eAAe,MAAM,KAAK,SAAS,GACnC,MAAM,KAAK,QACb;EACF,KAAK,aACH,OAAOC,SAAY,eAAe,MAAM,UAAU,SAAS,CAAC;EAC9D,KAAK,aACH,OAAOF,SAAY,eAAe,MAAM,UAAU,SAAS,CAAC;EAC9D,KAAK,aACH,OAAOG,SAAY,eAAe,MAAM,UAAU,SAAS,CAAC;EAC9D,KAAK,aACH,OAAOC,SAAY,eAAe,MAAM,UAAU,SAAS,CAAC;EAC9D,KAAK,SACH,OAAOC,MAAS,eAAe,MAAM,MAAM,SAAS,CAAC;EACvD,KAAK,SACH,OAAOC,KACL,eAAe,MAAM,MAAM,SAAS,GACpC,MAAM,MAAM,OACd;EACF,KAAK,aACH,OAAO,eAAe,MAAM,UAAU,SAAS;EACjD,KAAK,UACH,OAAO,eAAe,MAAM,OAAO,SAAS;EAC9C,KAAK,WACH,OAAO,eAAe,MAAM,QAAQ,SAAS;EAC/C,KAAK,sBACH,OAAO,eAAe,MAAM,mBAAmB,SAAS;EAC1D,KAAK,sBACH,OAAO,eAAe,MAAM,mBAAmB,SAAS;EAC1D,KAAK,YACH,OAAO,eAAe,MAAM,SAAS,SAAS;EAChD,SACE,OAAO;CACX;AACF;AASA,SAAS,WAAW,SAA8B;CAChD,IAAI,QAAQ,SAAS,YAAY,OAAO,QAAQ,SAAS;CACzD,OAAO,QAAQ,KAAK;AACtB;AAEA,SAAS,YAAY,KAAiC;CACpD,OAAO,IAAI,MAAM,oCAAoC,CAAC,GAAG;AAC3D;AAEA,SAAS,eAAe,SAAsB,eAA+B;CAC3E,MAAM,MAAM,WAAW,OAAO;CAE9B,MAAM,QADU,gBAAgB,QAAQ,OACpB,CAAC,CAAC,KAAK,KAAK,YAAY,GAAG,KAAK;CACpD,OAAOP,KAAQ,OAAO,GAAG;AAC3B;AAEA,SAAS,oBACP,OACA,QACA,SACQ;CAER,MAAMQ,SAAOR,KAAQ,MAAM,WAAW,OAAO,KAAK,MAAM,GAAG,IAAI;CAC/D,OAAO,YAAY,KAAA,IACf,GAAG,OAAO,GAAGQ,WACb,GAAG,QAAQ,OAAOA,SAAO,QAAQ;AACvC;AAEA,SAAS,YACP,OACA,iBACQ;CAOR,OAAOC,MANM,MAAM,SAAS,EAAE,YAAY;EACxC,IAAI,MAAM,SAAS,aAAa,OAAO,CAAC;EACxC,OAAO,CACL,MAAM,UAAU,MAAM,KAAK,SAAS,eAAe,IAAI,CAAC,CAC1D;CACF,CACgB,GAAM,eAAe;AACvC;;;ACnSA,eAAsB,WACpB,QACA,QACA,UAAyB,CAAC,GACH;CAEvB,OAAO,YAAY,QADH,cAAc,CAAC,MAAM,GAAG,QAAQ,OACf,CAAC;AACpC;AAEA,eAAsB,YACpB,OACA,QACA,UAAyB,CAAC,GACD;CACzB,MAAM,UAAU,cACd,MAAM,KAAK,SAAS,KAAK,EAAE,GAC3B,QACA,OACF;CAEA,KAAK,MAAM,QAAQ,OACjB,MAAM,YAAY,KAAK,IAAI,SAAS,IAAI;CAG1C,OAAO,QAAQ,IAAI,CAAC,GAAG,QAAQ,cAAc,OAAO,CAAC,CAAC;AACxD;AAEA,SAAS,cACP,iBACA,QACA,SACmB;CACnB,OAAO;EACL;EACA,iBAAiB,IAAI,IAAI,eAAe;EACxC,YAAY,QAAQ,cAAA;EACpB,aAAa,QAAQ;EACrB,aAAa,QAAQ;EACrB,WAAW,QAAQ,aAAa;EAChC,iBAAiB,QAAQ,mBAAA;EACzB,kBAAkB,QAAQ;EAC1B,cAAc,QAAQ;EACtB,+BAAe,IAAI,IAAI;CACzB;AACF;AAEA,eAAe,YACb,QACA,SACA,MACuB;CACvB,MAAM,SAAS,QAAQ,cAAc,IAAI,MAAM;CAC/C,IAAI,QAAQ,OAAO;CAEnB,MAAM,YAAY,iBAAiB,QAAQ,SAAS,IAAI;CACxD,QAAQ,cAAc,IAAI,QAAQ,SAAS;CAC3C,MAAM,WAAW,MAAM;CAEvB,IAAI,QAAQ,WACV,KAAK,MAAM,eAAe,SAAS,cACjC,MAAM,YAAY,aAAa,OAAO;CAI1C,OAAO;AACT;AAEA,eAAe,iBACb,QACA,SACA,MACuB;CACvB,MAAM,CAAC,MAAM,aAAa,MAAM,QAAQ,IAAI,CAC1C,OAAO,QAAQ,QAAQ,IAAI,IAAI,QAAQ,OAAO,QAAQ,MAAM,GAC5D,cAAc,QAAQ,QAAQ,MAAM,CACtC,CAAC;CACD,MAAM,WAAW,MAAM,gBAAgB,WAAW;EAChD;EACA,iBAAiB,QAAQ;EACzB,GAAI,QAAQ,qBAAqB,KAAA,IAC7B,EAAE,kBAAkB,QAAQ,iBAAiB,IAC7C,CAAC;EACL,GAAI,QAAQ,iBAAiB,KAAA,IACzB,EAAE,cAAc,QAAQ,aAAa,IACrC,CAAC;CACP,CAAC;CAYD,OAAO;EACL,IAAI;EACJ,SAbkB,kBAAkB,MAAM;GAC1C,YAAY,QAAQ;GACpB,GAAI,QAAQ,gBAAgB,KAAA,IACxB,EAAE,aAAa,QAAQ,YAAY,IACnC,CAAC;GACL,GAAI,QAAQ,gBAAgB,KAAA,IACxB,EAAE,aAAa,QAAQ,YAAY,IACnC,CAAC;GACL,gBAAgB,QAAQ,gBAAgB,IAAI,MAAM;EACpD,CAIqB,IAAI,SAAS;EAChC,cAAc,CAAC,GAAG,IAAI,IAAI,SAAS,YAAY,CAAC;EAChD,gBAAgB,QAAQ,gBAAgB,IAAI,MAAM;CACpD;AACF;;;;;;;ACvHA,SAAgB,eACd,OACA,OACA,aACyB;CACzB,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,WAAW,oBAAoB,MAAM,WAAW;EACtD,IAAI,SAAS,MAAM,KAAK,MAAM,OAC5B,OAAO;GAAE,MAAM;GAAM,OAAO,SAAS;EAAM;CAE/C;AAEF;;;;;;AAOA,SAAgB,wBACd,SACA,QACQ;CACR,MAAM,aAAa,WAAW,KAAK,UAAU,MAAM;CACnD,MAAM,QAAQ,QAAQ,MAAM,IAAI;CAChC,IAAI,MAAM,OAAO,OACf,OAAO,QAAQ,WAAW,SAAS;CAGrC,MAAM,UAAU,MAAM,QAAQ,OAAO,CAAC;CACtC,IAAI,YAAY,IACd,OAAO,QAAQ,WAAW,SAAS;CAMrC,OAAO;EAAC;EAAO;EAAY,GAHF,MACtB,MAAM,GAAG,OAAO,CAAC,CACjB,QAAQ,SAAS,CAAC,eAAe,KAAK,IAAI,CACA;EAAG,GAAG,MAAM,MAAM,OAAO;CAAC,CAAC,CAAC,KACvE,IACF;AACF;AAIA,MAAM,mBAAmB;;;;;;;AAQzB,SAAgB,qBACd,SACA,YACA,WACQ;CACR,MAAM,iBAAiB,SACrB,QAAQ,QAAQ,UAAU,CAAC,GAC3B,QAAQ,SAAS,CACnB;CACA,MAAM,SACJ,mBAAmB,KACf,KACA,GAAG,eAAe,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE;CAC7C,IAAI,WAAW,IAAI,OAAO;CAE1B,OAAO,QAAQ,QACb,mBACC,GAAG,WAAmB,OAAO,SAAS,OAAO,KAChD;AACF;;;AC7DA,eAAsB,WACpB,OACA,UAA6B,CAAC,GACX;CACnB,MAAM,WAAW,QAAQ,YAAA;CACzB,MAAM,YAAY,QAAQ,aAAA;CAC1B,MAAM,eAAyB,CAAC;CAChC,MAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;CAE1C,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,aAAa,KAAK,WAAW,GAAG,KAAK,GAAG,IAAI;EAClD,MAAM,cAAc,MAAM,SAAS,UAAU;EAC7C,IAAI,eAAe,CAAC,YAAY,OAAO,GACrC,MAAM,IAAI,MAAM,0CAA0C,YAAY;EAExE,MAAM,UAAU,YAAY,KAAK,SAAS,MAAM;EAEhD,IAAI,CAAC,KAAK,gBAAgB;EAE1B,MAAM,WAAW,KAAK,UAAU,GAAG,KAAK,GAAG,IAAI;EAC/C,MAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;EACzC,MAAM,eAAe,YAAY,QAAQ;EACzC,aAAa,KAAK,QAAQ;CAC5B;CAEA,OAAO;AACT;AAEA,eAAsB,YACpB,SACA,aAAqB,qBACJ;CACjB,MAAM,YAAY,QAAQ,UAAU;CACpC,IAAI,cAAc,KAChB,MAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;CAE5C,MAAM,UAAU,YAAY,SAAS,MAAM;CAC3C,OAAO;AACT;AAEA,SAAgB,uBACd,UAA8B,CAAC,GACvB;CACR,MAAM,WAAW,QAAQ,YAAA;CACzB,MAAM,YAAY,QAAQ,aAAA;CAE1B,MAAM,eAAe,QAAQ,UADV,QAAQ,cAAA,wCACsB;CACjD,OAAO,SAAS,QAAQ,SAAS,GAAG,YAAY,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;AACvE;AAEA,eAAe,eACb,YACA,UACe;CACf,IAAI,QAAQ,UAAU,MAAM,QAAQ,QAAQ,GAAG;CAE/C,MAAM,YAAY,MAAM,SAAS,QAAQ;CACzC,IAAI,aAAa,CAAC,UAAU,OAAO,KAAK,CAAC,UAAU,eAAe,GAChE,MAAM,IAAI,MAAM,wCAAwC,UAAU;CAGpE,MAAM,SAAS,SAAS,QAAQ,QAAQ,GAAG,UAAU;CACrD,IAAI,WAAW,eAAe,KAAM,MAAM,SAAS,QAAQ,MAAO,QAChE;CAGF,MAAM,gBAAgB,GAAG,SAAS,UAAU,WAAW,EAAE;CACzD,IAAI;EACF,MAAM,QAAQ,QAAQ,eAAe,MAAM;EAC3C,MAAM,OAAO,eAAe,QAAQ;CACtC,SAAS,OAAO;EACd,MAAM,GAAG,eAAe,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;EACvD,MAAM;CACR;AACF;AAEA,eAAe,SAAS,MAAc;CACpC,IAAI;EACF,OAAO,MAAM,MAAM,IAAI;CACzB,SAAS,OAAgB;EACvB,IAAI,aAAa,KAAK,MAAM,UAAU,OAAO,KAAA;EAC7C,MAAM;CACR;AACF;;;ACrFA,SAAgB,mBACd,QACc;CACd,MAAM,SAAS,IAAI,OAAO,EAAE,MAAM,OAAO,gBAAgB,CAAC;CAC1D,OAAO;EACL,iBAAiB,UAAU,QAAQ,OAAO,UAAU;EACpD,UAAU,WAAW,QAAQ,QAAQ,MAAM;EAC3C,mBAAmB,YAAY,iBAAiB,QAAQ,OAAO;CACjE;AACF;AAEA,eAAe,UACb,QACA,YAC+B;CAC/B,MAAM,WAAW,MAAM,gBACrB,OAAO,UAAU,SAAS,EAAE,aAAa,WAAW,CAAC,CACvD;CACA,IAAI,CAAC,eAAe,QAAQ,GAC1B,MAAM,IAAI,MAAM,mBAAmB,WAAW,wBAAwB;CAGxE,MAAM,QAA8B,CAAC;CACrC,KAAK,MAAM,UAAU,SAAS,cAAc;EAC1C,IAAI;EACJ,GAAG;GACD,MAAM,WAAW,MAAM,gBACrB,OAAO,YAAY,MAAM;IACvB,gBAAgB,OAAO;IACvB,QAAQ;KACN,UAAU;KACV,UAAU,EAAE,QAAQ,KAAK;IAC3B;IACA,OAAO,CAAC;KAAE,UAAU;KAAQ,WAAW;IAAa,CAAC;IACrD,WAAW;IACX,GAAI,gBAAgB,KAAA,IAChB,EAAE,cAAc,YAAY,IAC5B,CAAC;GACP,CAAC,CACH;GACA,MAAM,KAAK,GAAG,SAAS,QAAQ,OAAO,UAAU,CAAC;GACjD,cAAc,SAAS,eAAe,KAAA;GACtC,IAAI,CAAC,SAAS,UAAU;EAC1B,SAAS;CACX;CAEA,OAAO;AACT;AAEA,eAAe,QACb,QACA,QAC6B;CAC7B,MAAM,OAAO,MAAM,gBAAgB,OAAO,MAAM,SAAS,EAAE,SAAS,OAAO,CAAC,CAAC;CAC7E,IAAI,CAAC,WAAW,IAAI,GAClB,MAAM,IAAI,MAAM,eAAe,OAAO,oBAAoB;CAE5D,OAAO;AACT;AAEA,eAAe,iBACb,QACA,SACgC;CAChC,MAAM,SAAgC,CAAC;CACvC,IAAI;CAEJ,GAAG;EACD,MAAM,WAAW,MAAM,gBACrB,OAAO,OAAO,SAAS,KAAK;GAC1B,UAAU;GACV,WAAW;GACX,GAAI,gBAAgB,KAAA,IAAY,EAAE,cAAc,YAAY,IAAI,CAAC;EACnE,CAAC,CACH;EACA,OAAO,KAAK,GAAG,SAAS,QAAQ,OAAO,WAAW,CAAC;EACnD,cAAc,SAAS,eAAe,KAAA;CACxC,SAAS;CAET,OAAO;AACT;AAEA,MAAM,eAAe;AACrB,MAAM,yBAAyB;AAE/B,eAAe,UAAa,WAAyC;CACnE,KAAK,IAAI,UAAU,IAAK,WACtB,IAAI;EACF,OAAO,MAAM,UAAU;CACzB,SAAS,OAAgB;EACvB,IAAI,YAAY,eAAe,KAAK,CAAC,YAAY,KAAK,GAAG,MAAM;EAC/D,MAAM,MAAM,yBAAyB,KAAK,OAAO;CACnD;AAEJ;AAEA,SAAS,YAAY,OAAyB;CAC5C,IAAI,CAAC,oBAAoB,KAAK,KAAK,EAAE,YAAY,QAAQ,OAAO;CAChE,OAAO,MAAM,WAAW,OAAO,MAAM,WAAW,OAAO,MAAM,UAAU;AACzE;AAEA,SAAS,MAAM,cAAqC;CAClD,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,YAAY,CAAC;AACnE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as loadNoblogConfig } from "./config-file-
|
|
1
|
+
import { t as loadNoblogConfig } from "./config-file-Ti3lB3Et.js";
|
|
2
2
|
//#region src/commands/config-check.impl.ts
|
|
3
3
|
async function checkConfig(flags) {
|
|
4
4
|
try {
|
|
@@ -11,4 +11,4 @@ async function checkConfig(flags) {
|
|
|
11
11
|
//#endregion
|
|
12
12
|
export { checkConfig as default };
|
|
13
13
|
|
|
14
|
-
//# sourceMappingURL=config-check.impl-
|
|
14
|
+
//# sourceMappingURL=config-check.impl-CkV2SIkF.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-check.impl-
|
|
1
|
+
{"version":3,"file":"config-check.impl-CkV2SIkF.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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as ConfigurationError } from "./config-csbquMhF.js";
|
|
2
|
-
import { a as noblogConfigSchema, t as getErrorCode } from "./errors-
|
|
2
|
+
import { a as noblogConfigSchema, t as getErrorCode } from "./errors-v1sSdZ3l.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { parse } from "yaml";
|
|
5
5
|
import { readFile } from "node:fs/promises";
|
|
@@ -24,4 +24,4 @@ async function loadNoblogConfig(path) {
|
|
|
24
24
|
//#endregion
|
|
25
25
|
export { loadNoblogConfig as t };
|
|
26
26
|
|
|
27
|
-
//# sourceMappingURL=config-file-
|
|
27
|
+
//# sourceMappingURL=config-file-Ti3lB3Et.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-file-
|
|
1
|
+
{"version":3,"file":"config-file-Ti3lB3Et.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"}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
const DEFAULT_SAVE_PATH = "./src/pages/posts/";
|
|
3
3
|
const DEFAULT_STORE_PATH = ".noblog/pages/";
|
|
4
4
|
const DEFAULT_LAYOUT_PATH = "../../layouts/MarkdownPostLayout.astro";
|
|
5
|
+
const DEFAULT_SELFIE_PATH = ".noblog/selfie.md";
|
|
5
6
|
/** U+2E0E EDITORIAL CORONIS, a rare marker for theme child-page links. */
|
|
6
7
|
const DEFAULT_CHILD_PAGE_MARKER = "⸎";
|
|
7
8
|
//#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
|
+
export { DEFAULT_STORE_PATH as a, DEFAULT_SELFIE_PATH as i, DEFAULT_LAYOUT_PATH as n, DEFAULT_SAVE_PATH as r, DEFAULT_CHILD_PAGE_MARKER as t };
|
|
9
10
|
|
|
10
|
-
//# sourceMappingURL=defaults-
|
|
11
|
+
//# sourceMappingURL=defaults-DdsgY0uy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults-DdsgY0uy.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\";\nexport const DEFAULT_SELFIE_PATH = \".noblog/selfie.md\";\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;AACnC,MAAa,sBAAsB;;AAGnC,MAAa,4BAA4B"}
|
|
@@ -47,7 +47,8 @@ const noblogConfigSchema = z.strictObject({
|
|
|
47
47
|
layoutPath: z.string().min(1).optional().describe("Layout import path interpreted relative to savePath."),
|
|
48
48
|
recursive: z.boolean().optional().describe("Render child pages recursively (default: true)."),
|
|
49
49
|
childPageMarker: z.string().min(1).optional().describe("Marker prefixed to child-page links (default: ⸎, U+2E0E)."),
|
|
50
|
-
childPageWrapper: childPageWrapperSchema.optional().describe("Optional wrapper around child-page Markdown links; overrides childPageMarker.")
|
|
50
|
+
childPageWrapper: childPageWrapperSchema.optional().describe("Optional wrapper around child-page Markdown links; overrides childPageMarker."),
|
|
51
|
+
selfiePath: z.string().min(1).optional().describe("Output file for the page named by --selfie (default: .noblog/selfie.md).")
|
|
51
52
|
}).optional(),
|
|
52
53
|
frontmatter: z.strictObject({ fields: frontmatterFieldsSchema.optional() }).optional()
|
|
53
54
|
}).superRefine((config, context) => {
|
|
@@ -75,4 +76,4 @@ function getErrorCode(error) {
|
|
|
75
76
|
//#endregion
|
|
76
77
|
export { noblogConfigSchema as a, frontmatterFieldsSchema as i, BUILT_IN_FRONTMATTER_SOURCES as n, notionPropertiesSchema as o, frontmatterFieldSchema as r, getErrorCode as t };
|
|
77
78
|
|
|
78
|
-
//# sourceMappingURL=errors-
|
|
79
|
+
//# sourceMappingURL=errors-v1sSdZ3l.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors-v1sSdZ3l.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\nconst childPageWrapperSchema = z.strictObject({\n head: z\n .string()\n .min(1)\n .regex(/^[^\\r\\n]+$/, \"must be a single line\")\n .describe(\"Opening wrapper emitted before the child-page Markdown link.\"),\n tail: z\n .string()\n .min(1)\n .regex(/^[^\\r\\n]+$/, \"must be a single line\")\n .describe(\"Closing wrapper emitted after the child-page Markdown link.\"),\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 childPageWrapper: childPageWrapperSchema\n .optional()\n .describe(\n \"Optional wrapper around child-page Markdown links; overrides childPageMarker.\",\n ),\n selfiePath: z\n .string()\n .min(1)\n .optional()\n .describe(\n \"Output file for the page named by --selfie (default: .noblog/selfie.md).\",\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":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as loadConfig, t as ConfigurationError } from "./config-csbquMhF.js";
|
|
2
|
-
import "./defaults-
|
|
2
|
+
import "./defaults-DdsgY0uy.js";
|
|
3
3
|
import { n as createFrontmatterAdapter, t as DEFAULT_FRONTMATTER_FIELDS } from "./frontmatter-fields-BkXq38CO.js";
|
|
4
|
-
import { t as loadNoblogConfig } from "./config-file-
|
|
4
|
+
import { t as loadNoblogConfig } from "./config-file-Ti3lB3Et.js";
|
|
5
5
|
//#region src/commands/generate.impl.ts
|
|
6
6
|
async function generate(flags, savePath, legacySubPath) {
|
|
7
7
|
try {
|
|
@@ -18,9 +18,11 @@ async function generate(flags, savePath, legacySubPath) {
|
|
|
18
18
|
layoutPath: flags.layout ?? fileConfig?.output?.layoutPath ?? "../../layouts/MarkdownPostLayout.astro",
|
|
19
19
|
recursive: fileConfig?.output?.recursive ?? true,
|
|
20
20
|
childPageMarker: fileConfig?.output?.childPageMarker ?? "⸎",
|
|
21
|
+
selfiePath: fileConfig?.output?.selfiePath ?? ".noblog/selfie.md",
|
|
21
22
|
...childPageWrapper !== void 0 ? { childPageWrapper } : {},
|
|
22
23
|
...propertyMap !== void 0 ? { propertyMap } : {},
|
|
23
24
|
...frontmatter !== void 0 ? { frontmatter } : {},
|
|
25
|
+
...flags.selfie !== void 0 ? { selfie: flags.selfie } : {},
|
|
24
26
|
verbose: flags.verbose
|
|
25
27
|
}, this.process);
|
|
26
28
|
} catch (error) {
|
|
@@ -33,4 +35,4 @@ async function generate(flags, savePath, legacySubPath) {
|
|
|
33
35
|
//#endregion
|
|
34
36
|
export { generate as default };
|
|
35
37
|
|
|
36
|
-
//# sourceMappingURL=generate.impl-
|
|
38
|
+
//# sourceMappingURL=generate.impl-uVcUsW_1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.impl-uVcUsW_1.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_SELFIE_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 selfie?: 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 childPageWrapper = fileConfig?.output?.childPageWrapper;\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 selfiePath: fileConfig?.output?.selfiePath ?? DEFAULT_SELFIE_PATH,\n ...(childPageWrapper !== undefined ? { childPageWrapper } : {}),\n ...(propertyMap !== undefined ? { propertyMap } : {}),\n ...(frontmatter !== undefined ? { frontmatter } : {}),\n ...(flags.selfie !== undefined ? { selfie: flags.selfie } : {}),\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":";;;;;AAuBA,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,mBAAmB,YAAY,QAAQ;EAC7C,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,YAAY,YAAY,QAAQ,cAAA;GAChC,GAAI,qBAAqB,KAAA,IAAY,EAAE,iBAAiB,IAAI,CAAC;GAC7D,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;GACnD,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;GACnD,GAAI,MAAM,WAAW,KAAA,IAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;GAC7D,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"}
|
package/dist/generate.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as ConfigurationError } from "./config-csbquMhF.js";
|
|
3
|
-
import
|
|
3
|
+
import "./defaults-DdsgY0uy.js";
|
|
4
|
+
import { a as findSelfiePage, i as writeSelfie, l as renderPages, n as resolveStoreLayoutPath, o as relativizeStoreLinks, r as writePages, s as upsertFrontmatterAuthor, t as createNotionSource } from "./client-CJ_0duRz.js";
|
|
4
5
|
import { config } from "dotenv";
|
|
5
6
|
import { ExitCode, buildApplication, buildCommand, buildRouteMap, help, run, version } from "@stricli/core";
|
|
6
7
|
const app = buildApplication(buildRouteMap({
|
|
7
8
|
routes: {
|
|
8
9
|
config: buildRouteMap({
|
|
9
10
|
routes: { check: buildCommand({
|
|
10
|
-
loader: async () => import("./config-check.impl-
|
|
11
|
+
loader: async () => import("./config-check.impl-CkV2SIkF.js"),
|
|
11
12
|
parameters: {
|
|
12
13
|
flags: { config: {
|
|
13
14
|
kind: "parsed",
|
|
@@ -25,7 +26,7 @@ const app = buildApplication(buildRouteMap({
|
|
|
25
26
|
docs: { brief: "Inspect Noblog configuration" }
|
|
26
27
|
}),
|
|
27
28
|
generate: buildCommand({
|
|
28
|
-
loader: async () => import("./generate.impl-
|
|
29
|
+
loader: async () => import("./generate.impl-uVcUsW_1.js"),
|
|
29
30
|
parameters: {
|
|
30
31
|
flags: {
|
|
31
32
|
verbose: {
|
|
@@ -54,6 +55,13 @@ const app = buildApplication(buildRouteMap({
|
|
|
54
55
|
placeholder: "PATH",
|
|
55
56
|
parse: (input) => input,
|
|
56
57
|
optional: true
|
|
58
|
+
},
|
|
59
|
+
selfie: {
|
|
60
|
+
kind: "parsed",
|
|
61
|
+
brief: "Render the database page with this title to output.selfiePath",
|
|
62
|
+
placeholder: "PAGE_TITLE",
|
|
63
|
+
parse: (input) => input,
|
|
64
|
+
optional: true
|
|
57
65
|
}
|
|
58
66
|
},
|
|
59
67
|
aliases: { v: "verbose" },
|
|
@@ -74,7 +82,7 @@ const app = buildApplication(buildRouteMap({
|
|
|
74
82
|
},
|
|
75
83
|
docs: {
|
|
76
84
|
brief: "Generate blog content from Notion",
|
|
77
|
-
fullDescription: "Fetch published pages from Notion and write Astro-compatible Markdown files. Only database posts receive public symlinks; child pages live in the canonical store. The legacy SUB_PATH argument is accepted but ignored."
|
|
85
|
+
fullDescription: "Fetch published pages from Notion and write Astro-compatible Markdown files. Only database posts receive public symlinks; child pages live in the canonical store. The legacy SUB_PATH argument is accepted but ignored. With --selfie PAGE_TITLE, the matching database page is also rendered to output.selfiePath (default public/selfie.md) with frontmatter author set to the page title; when the title is empty or matches no page, the selfie file is left untouched."
|
|
78
86
|
}
|
|
79
87
|
})
|
|
80
88
|
},
|
|
@@ -98,7 +106,7 @@ const app = buildApplication(buildRouteMap({
|
|
|
98
106
|
}),
|
|
99
107
|
version: version({
|
|
100
108
|
brief: "Show version information",
|
|
101
|
-
info: { currentVersion: `noblog v0.3.0-beta.
|
|
109
|
+
info: { currentVersion: `noblog v0.3.0-beta.5` },
|
|
102
110
|
alias: false
|
|
103
111
|
})
|
|
104
112
|
});
|
|
@@ -158,17 +166,28 @@ async function generateBlog(config, options, output) {
|
|
|
158
166
|
if (options.verbose) output.stderr.write("Fetching posts from Notion...\n");
|
|
159
167
|
const posts = await notion.listPages();
|
|
160
168
|
if (options.verbose) output.stderr.write(`Found ${posts.length} pages\nSave path: ${options.savePath}\nStore path: ${options.storePath}\nLayout path: ${options.layoutPath}\n`);
|
|
161
|
-
|
|
169
|
+
const pages = await renderPages(posts, notion, {
|
|
162
170
|
layoutPath: storeLayoutPath,
|
|
163
171
|
recursive: options.recursive,
|
|
164
172
|
childPageMarker: options.childPageMarker,
|
|
165
173
|
...options.childPageWrapper !== void 0 ? { childPageWrapper: options.childPageWrapper } : {},
|
|
166
174
|
...options.propertyMap !== void 0 ? { propertyMap: options.propertyMap } : {},
|
|
167
175
|
...options.frontmatter !== void 0 ? { frontmatter: options.frontmatter } : {}
|
|
168
|
-
})
|
|
176
|
+
});
|
|
177
|
+
await writePages(pages, {
|
|
169
178
|
savePath: options.savePath,
|
|
170
179
|
storePath: options.storePath
|
|
171
180
|
});
|
|
181
|
+
const selfieTitle = options.selfie?.trim() ?? "";
|
|
182
|
+
if (selfieTitle !== "") {
|
|
183
|
+
const selfiePath = options.selfiePath ?? ".noblog/selfie.md";
|
|
184
|
+
const match = findSelfiePage(posts, selfieTitle, options.propertyMap);
|
|
185
|
+
const rendered = pages.find((page) => page.id === match?.page.id);
|
|
186
|
+
if (match && rendered) {
|
|
187
|
+
await writeSelfie(upsertFrontmatterAuthor(relativizeStoreLinks(rendered.content, selfiePath, options.storePath), match.title), selfiePath);
|
|
188
|
+
if (options.verbose) output.stderr.write(`Wrote selfie page "${match.title}" to ${selfiePath}\n`);
|
|
189
|
+
} else if (options.verbose) output.stderr.write(`Selfie page "${selfieTitle}" not found in the database; leaving ${selfiePath} untouched\n`);
|
|
190
|
+
}
|
|
172
191
|
output.stdout.write("✅ Successfully generated blog content\n");
|
|
173
192
|
}
|
|
174
193
|
//#endregion
|
package/dist/generate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.js","names":["packageJson.version"],"sources":["../package.json","../src/commands/config-check.ts","../src/commands/config.ts","../src/commands/generate.ts","../src/app.ts","../src/workflows/generate.ts","../src/main.ts","../src/cli.ts"],"sourcesContent":["","import { buildCommand } from \"@stricli/core\";\n\nexport const configCheckCommand = buildCommand({\n loader: async () => import(\"./config-check.impl.js\"),\n parameters: {\n flags: {\n config: {\n kind: \"parsed\",\n brief: \"YAML configuration file\",\n placeholder: \"PATH\",\n parse: (input) => input,\n },\n },\n positional: {\n kind: \"tuple\",\n parameters: [],\n },\n },\n docs: {\n brief: \"Validate a YAML configuration without network access\",\n },\n});\n","import { buildRouteMap } from \"@stricli/core\";\nimport { configCheckCommand } from \"./config-check.js\";\n\nexport const configCommand = buildRouteMap({\n routes: {\n check: configCheckCommand,\n },\n docs: {\n brief: \"Inspect Noblog configuration\",\n },\n});\n","import { buildCommand } from \"@stricli/core\";\n\nexport const generateCommand = buildCommand({\n loader: async () => import(\"./generate.impl.js\"),\n parameters: {\n flags: {\n verbose: {\n kind: \"boolean\",\n brief: \"Print diagnostic information\",\n default: false,\n withNegated: false,\n },\n config: {\n kind: \"parsed\",\n brief: \"YAML configuration file\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n layout: {\n kind: \"parsed\",\n brief: \"Layout path relative to SAVE_PATH\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n store: {\n kind: \"parsed\",\n brief: \"Directory for canonical Markdown files\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n },\n aliases: {\n v: \"verbose\",\n },\n positional: {\n kind: \"tuple\",\n parameters: [\n {\n brief: \"Public view directory for database posts\",\n placeholder: \"SAVE_PATH\",\n parse: (input) => input,\n optional: true,\n },\n {\n brief: \"Deprecated compatibility argument; ignored\",\n placeholder: \"SUB_PATH\",\n parse: (input) => input,\n optional: true,\n },\n ],\n },\n },\n docs: {\n brief: \"Generate blog content from Notion\",\n fullDescription:\n \"Fetch published pages from Notion and write Astro-compatible Markdown files. \" +\n \"Only database posts receive public symlinks; child pages live in the canonical store. \" +\n \"The legacy SUB_PATH argument is accepted but ignored.\",\n },\n});\n","import {\n buildApplication,\n buildRouteMap,\n ExitCode,\n help,\n run,\n version,\n} from \"@stricli/core\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { configCommand } from \"./commands/config.js\";\nimport { generateCommand } from \"./commands/generate.js\";\nimport { ConfigurationError } from \"./config.js\";\nimport type { CliContext } from \"./context.js\";\n\nconst root = buildRouteMap({\n routes: {\n config: configCommand,\n generate: generateCommand,\n },\n docs: {\n brief: \"Generate static blog content from Notion\",\n fullDescription:\n \"Running noblog without a subcommand is equivalent to 'noblog generate'.\\n\" +\n \"The legacy 'help' and 'version' commands are also supported.\\n\\n\" +\n \"Generation requires environment variables:\\n\" +\n \" NOTION_API_SECRET Your Notion API secret\\n\" +\n \" NOBLOG_DATABASE_ID Your Notion database ID\",\n },\n});\n\nconst helpFormatting = {\n caseStyle: \"original\",\n useAliasInUsageLine: false,\n onlyRequiredInUsageLine: false,\n} as const;\n\nexport const app = buildApplication(\n root,\n {\n name: \"noblog\",\n scanner: {\n allowArgumentEscapeSequence: true,\n },\n determineExitCode: (error) =>\n error instanceof ConfigurationError ? 2 : ExitCode.CommandRunError,\n },\n {\n help: help({\n brief: \"Show help information\",\n defaultForRouteMap: true,\n formatting: helpFormatting,\n }),\n version: version({\n brief: \"Show version information\",\n info: { currentVersion: `noblog v${packageJson.version}` },\n alias: false,\n }),\n },\n);\n\nconst PATH_FLAGS = new Set([\"--config\", \"--layout\", \"--store\"]);\n\nexport async function runCli(\n inputs: readonly string[],\n context: CliContext,\n): Promise<void> {\n const escapeIndex = inputs.indexOf(\"--\");\n const commandInputs =\n escapeIndex === -1 ? inputs : inputs.slice(0, escapeIndex);\n const escapedInputs = escapeIndex === -1 ? [] : inputs.slice(escapeIndex);\n\n let normalizedInputs: readonly string[];\n if (hasLegacyControl(commandInputs, \"help\")) {\n normalizedInputs = [\"--help\"];\n } else if (\n hasLegacyControl(commandInputs, \"version\") ||\n hasLegacyControl(commandInputs, \"--version\")\n ) {\n normalizedInputs = [\"--version\"];\n } else {\n // Legacy control words were global. Keep the first explicit command and\n // skip later ones; escaped words and flag values remain positional data.\n const remainingInputs: string[] = [];\n for (let index = 0; index < commandInputs.length; index++) {\n const input = commandInputs[index];\n if (input === undefined) continue;\n if (input === \"generate\" && index !== 0) continue;\n\n remainingInputs.push(input);\n const flagValue = getFlagValue(commandInputs, index);\n if (PATH_FLAGS.has(input) && flagValue !== undefined) {\n remainingInputs.push(flagValue);\n index++;\n }\n }\n normalizedInputs = [...remainingInputs, ...escapedInputs];\n\n if (\n normalizedInputs[0] !== \"generate\" &&\n normalizedInputs[0] !== \"config\" &&\n normalizedInputs[0] !== \"-h\" &&\n normalizedInputs[0] !== \"--help\" &&\n normalizedInputs[0] !== \"--version\"\n ) {\n normalizedInputs = [\"generate\", ...normalizedInputs];\n }\n }\n\n await run(app, normalizedInputs, context);\n\n const exitCode = context.process.exitCode;\n if (typeof exitCode === \"number\" && exitCode < 0) {\n context.process.exitCode =\n exitCode === ExitCode.InvalidArgument ||\n exitCode === ExitCode.UnknownCommand\n ? 2\n : 1;\n }\n}\n\nfunction hasLegacyControl(\n inputs: readonly string[],\n control: \"help\" | \"version\" | \"--version\",\n): boolean {\n for (let index = 0; index < inputs.length; index++) {\n const input = inputs[index];\n if (input === undefined) continue;\n if (\n PATH_FLAGS.has(input) &&\n getFlagValue(inputs, index) !== undefined\n ) {\n index++;\n continue;\n }\n if (input === control) return true;\n }\n return false;\n}\n\nfunction getFlagValue(\n inputs: readonly string[],\n flagIndex: number,\n): string | undefined {\n const value = inputs[flagIndex + 1];\n return value !== undefined && !value.startsWith(\"-\") ? value : undefined;\n}\n","import type { NoblogConfig } from \"../config.js\";\nimport type { NotionPropertyMap } from \"../lib/noblog/config.js\";\nimport {\n renderPages,\n resolveStoreLayoutPath,\n writePages,\n} from \"../lib/noblog/index.js\";\nimport type {\n ChildPageWrapper,\n FrontmatterAdapter,\n} from \"../lib/noblog/types.js\";\nimport { createNotionSource } from \"../lib/notion/client.js\";\n\nexport interface GenerateOptions {\n savePath: string;\n storePath: string;\n layoutPath: string;\n recursive: boolean;\n childPageMarker: string;\n childPageWrapper?: ChildPageWrapper;\n propertyMap?: NotionPropertyMap;\n frontmatter?: FrontmatterAdapter;\n verbose: boolean;\n}\n\nexport interface GenerateOutput {\n stdout: { write(chunk: string): void };\n stderr: { write(chunk: string): void };\n}\n\nexport async function generateBlog(\n config: NoblogConfig,\n options: GenerateOptions,\n output: GenerateOutput,\n): Promise<void> {\n const notion = createNotionSource(config);\n const storeLayoutPath = resolveStoreLayoutPath(options);\n\n if (options.verbose) {\n output.stderr.write(\"Fetching posts from Notion...\\n\");\n }\n\n const posts = await notion.listPages();\n\n if (options.verbose) {\n output.stderr.write(\n `Found ${posts.length} pages\\n` +\n `Save path: ${options.savePath}\\n` +\n `Store path: ${options.storePath}\\n` +\n `Layout path: ${options.layoutPath}\\n`,\n );\n }\n\n const pages = await renderPages(posts, notion, {\n layoutPath: storeLayoutPath,\n recursive: options.recursive,\n childPageMarker: options.childPageMarker,\n ...(options.childPageWrapper !== undefined\n ? { childPageWrapper: options.childPageWrapper }\n : {}),\n ...(options.propertyMap !== undefined\n ? { propertyMap: options.propertyMap }\n : {}),\n ...(options.frontmatter !== undefined\n ? { frontmatter: options.frontmatter }\n : {}),\n });\n await writePages(pages, {\n savePath: options.savePath,\n storePath: options.storePath,\n });\n\n output.stdout.write(\"✅ Successfully generated blog content\\n\");\n}\n\nexport type GenerateBlog = typeof generateBlog;\n","import { config as loadDotenv } from \"dotenv\";\nimport type { DotenvPopulateInput } from \"dotenv\";\nimport { runCli } from \"./app.js\";\nimport type { CliContext } from \"./context.js\";\nimport { generateBlog } from \"./workflows/generate.js\";\n\nconst nodeProcess: CliContext[\"process\"] = {\n env: process.env,\n stdout: process.stdout,\n stderr: process.stderr,\n get exitCode() {\n const exitCode = process.exitCode;\n return typeof exitCode === \"number\" ? exitCode : null;\n },\n set exitCode(value: number | null) {\n process.exitCode = value ?? undefined;\n },\n};\n\nexport async function main(\n inputs: readonly string[] = process.argv.slice(2),\n context: CliContext = { process: nodeProcess, generateBlog },\n): Promise<void> {\n const environment = context.process.env ?? {};\n const processEnv: DotenvPopulateInput = Object.fromEntries(\n Object.entries(environment).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n );\n loadDotenv({ processEnv, quiet: true });\n Object.assign(environment, processEnv);\n await runCli(inputs, context);\n}\n","#!/usr/bin/env node\n\nimport { main } from \"./main.js\";\n\nawait main();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generate.js","names":["packageJson.version"],"sources":["../package.json","../src/commands/config-check.ts","../src/commands/config.ts","../src/commands/generate.ts","../src/app.ts","../src/workflows/generate.ts","../src/main.ts","../src/cli.ts"],"sourcesContent":["","import { buildCommand } from \"@stricli/core\";\n\nexport const configCheckCommand = buildCommand({\n loader: async () => import(\"./config-check.impl.js\"),\n parameters: {\n flags: {\n config: {\n kind: \"parsed\",\n brief: \"YAML configuration file\",\n placeholder: \"PATH\",\n parse: (input) => input,\n },\n },\n positional: {\n kind: \"tuple\",\n parameters: [],\n },\n },\n docs: {\n brief: \"Validate a YAML configuration without network access\",\n },\n});\n","import { buildRouteMap } from \"@stricli/core\";\nimport { configCheckCommand } from \"./config-check.js\";\n\nexport const configCommand = buildRouteMap({\n routes: {\n check: configCheckCommand,\n },\n docs: {\n brief: \"Inspect Noblog configuration\",\n },\n});\n","import { buildCommand } from \"@stricli/core\";\n\nexport const generateCommand = buildCommand({\n loader: async () => import(\"./generate.impl.js\"),\n parameters: {\n flags: {\n verbose: {\n kind: \"boolean\",\n brief: \"Print diagnostic information\",\n default: false,\n withNegated: false,\n },\n config: {\n kind: \"parsed\",\n brief: \"YAML configuration file\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n layout: {\n kind: \"parsed\",\n brief: \"Layout path relative to SAVE_PATH\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n store: {\n kind: \"parsed\",\n brief: \"Directory for canonical Markdown files\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n selfie: {\n kind: \"parsed\",\n brief: \"Render the database page with this title to output.selfiePath\",\n placeholder: \"PAGE_TITLE\",\n parse: (input) => input,\n optional: true,\n },\n },\n aliases: {\n v: \"verbose\",\n },\n positional: {\n kind: \"tuple\",\n parameters: [\n {\n brief: \"Public view directory for database posts\",\n placeholder: \"SAVE_PATH\",\n parse: (input) => input,\n optional: true,\n },\n {\n brief: \"Deprecated compatibility argument; ignored\",\n placeholder: \"SUB_PATH\",\n parse: (input) => input,\n optional: true,\n },\n ],\n },\n },\n docs: {\n brief: \"Generate blog content from Notion\",\n fullDescription:\n \"Fetch published pages from Notion and write Astro-compatible Markdown files. \" +\n \"Only database posts receive public symlinks; child pages live in the canonical store. \" +\n \"The legacy SUB_PATH argument is accepted but ignored. \" +\n \"With --selfie PAGE_TITLE, the matching database page is also rendered to output.selfiePath \" +\n \"(default public/selfie.md) with frontmatter author set to the page title; \" +\n \"when the title is empty or matches no page, the selfie file is left untouched.\",\n },\n});\n","import {\n buildApplication,\n buildRouteMap,\n ExitCode,\n help,\n run,\n version,\n} from \"@stricli/core\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { configCommand } from \"./commands/config.js\";\nimport { generateCommand } from \"./commands/generate.js\";\nimport { ConfigurationError } from \"./config.js\";\nimport type { CliContext } from \"./context.js\";\n\nconst root = buildRouteMap({\n routes: {\n config: configCommand,\n generate: generateCommand,\n },\n docs: {\n brief: \"Generate static blog content from Notion\",\n fullDescription:\n \"Running noblog without a subcommand is equivalent to 'noblog generate'.\\n\" +\n \"The legacy 'help' and 'version' commands are also supported.\\n\\n\" +\n \"Generation requires environment variables:\\n\" +\n \" NOTION_API_SECRET Your Notion API secret\\n\" +\n \" NOBLOG_DATABASE_ID Your Notion database ID\",\n },\n});\n\nconst helpFormatting = {\n caseStyle: \"original\",\n useAliasInUsageLine: false,\n onlyRequiredInUsageLine: false,\n} as const;\n\nexport const app = buildApplication(\n root,\n {\n name: \"noblog\",\n scanner: {\n allowArgumentEscapeSequence: true,\n },\n determineExitCode: (error) =>\n error instanceof ConfigurationError ? 2 : ExitCode.CommandRunError,\n },\n {\n help: help({\n brief: \"Show help information\",\n defaultForRouteMap: true,\n formatting: helpFormatting,\n }),\n version: version({\n brief: \"Show version information\",\n info: { currentVersion: `noblog v${packageJson.version}` },\n alias: false,\n }),\n },\n);\n\nconst PATH_FLAGS = new Set([\"--config\", \"--layout\", \"--store\"]);\n\nexport async function runCli(\n inputs: readonly string[],\n context: CliContext,\n): Promise<void> {\n const escapeIndex = inputs.indexOf(\"--\");\n const commandInputs =\n escapeIndex === -1 ? inputs : inputs.slice(0, escapeIndex);\n const escapedInputs = escapeIndex === -1 ? [] : inputs.slice(escapeIndex);\n\n let normalizedInputs: readonly string[];\n if (hasLegacyControl(commandInputs, \"help\")) {\n normalizedInputs = [\"--help\"];\n } else if (\n hasLegacyControl(commandInputs, \"version\") ||\n hasLegacyControl(commandInputs, \"--version\")\n ) {\n normalizedInputs = [\"--version\"];\n } else {\n // Legacy control words were global. Keep the first explicit command and\n // skip later ones; escaped words and flag values remain positional data.\n const remainingInputs: string[] = [];\n for (let index = 0; index < commandInputs.length; index++) {\n const input = commandInputs[index];\n if (input === undefined) continue;\n if (input === \"generate\" && index !== 0) continue;\n\n remainingInputs.push(input);\n const flagValue = getFlagValue(commandInputs, index);\n if (PATH_FLAGS.has(input) && flagValue !== undefined) {\n remainingInputs.push(flagValue);\n index++;\n }\n }\n normalizedInputs = [...remainingInputs, ...escapedInputs];\n\n if (\n normalizedInputs[0] !== \"generate\" &&\n normalizedInputs[0] !== \"config\" &&\n normalizedInputs[0] !== \"-h\" &&\n normalizedInputs[0] !== \"--help\" &&\n normalizedInputs[0] !== \"--version\"\n ) {\n normalizedInputs = [\"generate\", ...normalizedInputs];\n }\n }\n\n await run(app, normalizedInputs, context);\n\n const exitCode = context.process.exitCode;\n if (typeof exitCode === \"number\" && exitCode < 0) {\n context.process.exitCode =\n exitCode === ExitCode.InvalidArgument ||\n exitCode === ExitCode.UnknownCommand\n ? 2\n : 1;\n }\n}\n\nfunction hasLegacyControl(\n inputs: readonly string[],\n control: \"help\" | \"version\" | \"--version\",\n): boolean {\n for (let index = 0; index < inputs.length; index++) {\n const input = inputs[index];\n if (input === undefined) continue;\n if (\n PATH_FLAGS.has(input) &&\n getFlagValue(inputs, index) !== undefined\n ) {\n index++;\n continue;\n }\n if (input === control) return true;\n }\n return false;\n}\n\nfunction getFlagValue(\n inputs: readonly string[],\n flagIndex: number,\n): string | undefined {\n const value = inputs[flagIndex + 1];\n return value !== undefined && !value.startsWith(\"-\") ? value : undefined;\n}\n","import type { NoblogConfig } from \"../config.js\";\nimport type { NotionPropertyMap } from \"../lib/noblog/config.js\";\nimport {\n DEFAULT_SELFIE_PATH,\n findSelfiePage,\n relativizeStoreLinks,\n renderPages,\n resolveStoreLayoutPath,\n upsertFrontmatterAuthor,\n writePages,\n writeSelfie,\n} from \"../lib/noblog/index.js\";\nimport type {\n ChildPageWrapper,\n FrontmatterAdapter,\n} from \"../lib/noblog/types.js\";\nimport { createNotionSource } from \"../lib/notion/client.js\";\n\nexport interface GenerateOptions {\n savePath: string;\n storePath: string;\n layoutPath: string;\n recursive: boolean;\n childPageMarker: string;\n childPageWrapper?: ChildPageWrapper;\n propertyMap?: NotionPropertyMap;\n frontmatter?: FrontmatterAdapter;\n selfie?: string;\n selfiePath?: string;\n verbose: boolean;\n}\n\nexport interface GenerateOutput {\n stdout: { write(chunk: string): void };\n stderr: { write(chunk: string): void };\n}\n\nexport async function generateBlog(\n config: NoblogConfig,\n options: GenerateOptions,\n output: GenerateOutput,\n): Promise<void> {\n const notion = createNotionSource(config);\n const storeLayoutPath = resolveStoreLayoutPath(options);\n\n if (options.verbose) {\n output.stderr.write(\"Fetching posts from Notion...\\n\");\n }\n\n const posts = await notion.listPages();\n\n if (options.verbose) {\n output.stderr.write(\n `Found ${posts.length} pages\\n` +\n `Save path: ${options.savePath}\\n` +\n `Store path: ${options.storePath}\\n` +\n `Layout path: ${options.layoutPath}\\n`,\n );\n }\n\n const pages = await renderPages(posts, notion, {\n layoutPath: storeLayoutPath,\n recursive: options.recursive,\n childPageMarker: options.childPageMarker,\n ...(options.childPageWrapper !== undefined\n ? { childPageWrapper: options.childPageWrapper }\n : {}),\n ...(options.propertyMap !== undefined\n ? { propertyMap: options.propertyMap }\n : {}),\n ...(options.frontmatter !== undefined\n ? { frontmatter: options.frontmatter }\n : {}),\n });\n await writePages(pages, {\n savePath: options.savePath,\n storePath: options.storePath,\n });\n\n const selfieTitle = options.selfie?.trim() ?? \"\";\n if (selfieTitle !== \"\") {\n const selfiePath = options.selfiePath ?? DEFAULT_SELFIE_PATH;\n const match = findSelfiePage(posts, selfieTitle, options.propertyMap);\n const rendered = pages.find((page) => page.id === match?.page.id);\n if (match && rendered) {\n const content = upsertFrontmatterAuthor(\n relativizeStoreLinks(rendered.content, selfiePath, options.storePath),\n match.title,\n );\n await writeSelfie(content, selfiePath);\n if (options.verbose) {\n output.stderr.write(\n `Wrote selfie page \"${match.title}\" to ${selfiePath}\\n`,\n );\n }\n } else if (options.verbose) {\n output.stderr.write(\n `Selfie page \"${selfieTitle}\" not found in the database; ` +\n `leaving ${selfiePath} untouched\\n`,\n );\n }\n }\n\n output.stdout.write(\"✅ Successfully generated blog content\\n\");\n}\n\nexport type GenerateBlog = typeof generateBlog;\n","import { config as loadDotenv } from \"dotenv\";\nimport type { DotenvPopulateInput } from \"dotenv\";\nimport { runCli } from \"./app.js\";\nimport type { CliContext } from \"./context.js\";\nimport { generateBlog } from \"./workflows/generate.js\";\n\nconst nodeProcess: CliContext[\"process\"] = {\n env: process.env,\n stdout: process.stdout,\n stderr: process.stderr,\n get exitCode() {\n const exitCode = process.exitCode;\n return typeof exitCode === \"number\" ? exitCode : null;\n },\n set exitCode(value: number | null) {\n process.exitCode = value ?? undefined;\n },\n};\n\nexport async function main(\n inputs: readonly string[] = process.argv.slice(2),\n context: CliContext = { process: nodeProcess, generateBlog },\n): Promise<void> {\n const environment = context.process.env ?? {};\n const processEnv: DotenvPopulateInput = Object.fromEntries(\n Object.entries(environment).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n );\n loadDotenv({ processEnv, quiet: true });\n Object.assign(environment, processEnv);\n await runCli(inputs, context);\n}\n","#!/usr/bin/env node\n\nimport { main } from \"./main.js\";\n\nawait main();\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ declare const noblogConfigSchema: z.ZodObject<{
|
|
|
60
60
|
head: z.ZodString;
|
|
61
61
|
tail: z.ZodString;
|
|
62
62
|
}, z.core.$strict>>;
|
|
63
|
+
selfiePath: z.ZodOptional<z.ZodString>;
|
|
63
64
|
}, z.core.$strict>>;
|
|
64
65
|
frontmatter: z.ZodOptional<z.ZodObject<{
|
|
65
66
|
fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
|
|
@@ -92,6 +93,7 @@ type NoblogFileConfig = z.infer<typeof noblogConfigSchema>;
|
|
|
92
93
|
declare const DEFAULT_SAVE_PATH = "./src/pages/posts/";
|
|
93
94
|
declare const DEFAULT_STORE_PATH = ".noblog/pages/";
|
|
94
95
|
declare const DEFAULT_LAYOUT_PATH = "../../layouts/MarkdownPostLayout.astro";
|
|
96
|
+
declare const DEFAULT_SELFIE_PATH = ".noblog/selfie.md";
|
|
95
97
|
/** U+2E0E EDITORIAL CORONIS, a rare marker for theme child-page links. */
|
|
96
98
|
declare const DEFAULT_CHILD_PAGE_MARKER = "\u2E0E";
|
|
97
99
|
//#endregion
|
|
@@ -214,8 +216,33 @@ declare function renderPages(posts: readonly PageObjectResponse[], reader: Notio
|
|
|
214
216
|
declare function renderPlainText(richText: readonly RichTextItemResponse[]): string;
|
|
215
217
|
declare function renderRichText(richText: readonly RichTextItemResponse[]): string;
|
|
216
218
|
//#endregion
|
|
219
|
+
//#region src/lib/noblog/selfie.d.ts
|
|
220
|
+
interface SelfieMatch {
|
|
221
|
+
readonly page: PageObjectResponse;
|
|
222
|
+
readonly title: string;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Find the database post whose title exactly matches the requested selfie
|
|
226
|
+
* title. Titles are compared trimmed on both sides.
|
|
227
|
+
*/
|
|
228
|
+
declare function findSelfiePage(posts: readonly PageObjectResponse[], title: string, propertyMap?: NotionPropertyMap): SelfieMatch | undefined;
|
|
229
|
+
/**
|
|
230
|
+
* Return the Markdown document with its frontmatter `author` set to the given
|
|
231
|
+
* value. An existing top-level `author` key is replaced so the result always
|
|
232
|
+
* reflects the provided author; the document body is left untouched.
|
|
233
|
+
*/
|
|
234
|
+
declare function upsertFrontmatterAuthor(content: string, author: string): string;
|
|
235
|
+
/**
|
|
236
|
+
* Rewrite `./<page-id>.md` child-page links so they resolve from the selfie
|
|
237
|
+
* file's directory instead of the canonical store directory. Links are left
|
|
238
|
+
* unchanged when the selfie file already sits next to the store files, and
|
|
239
|
+
* non-store relative links (for example user asset links) are never touched.
|
|
240
|
+
*/
|
|
241
|
+
declare function relativizeStoreLinks(content: string, selfiePath: string, storePath: string): string;
|
|
242
|
+
//#endregion
|
|
217
243
|
//#region src/lib/noblog/writer.d.ts
|
|
218
244
|
declare function writePages(pages: readonly RenderedPage[], options?: WritePagesOptions): Promise<string[]>;
|
|
245
|
+
declare function writeSelfie(content: string, selfiePath?: string): Promise<string>;
|
|
219
246
|
declare function resolveStoreLayoutPath(options?: StoreLayoutOptions): string;
|
|
220
247
|
//#endregion
|
|
221
248
|
//#region src/lib/notion/client.d.ts
|
|
@@ -228,5 +255,5 @@ interface NotionSource extends NotionReader {
|
|
|
228
255
|
}
|
|
229
256
|
declare function createNotionSource(config: NotionSourceConfig): NotionSource;
|
|
230
257
|
//#endregion
|
|
231
|
-
export { BUILT_IN_FRONTMATTER_SOURCES, BlockTransformer, BlockTransformerContext, BlockTransformers, CalloutIcon, ChildPageWrapper, ConfigValue, DEFAULT_CHILD_PAGE_MARKER, DEFAULT_FRONTMATTER_FIELDS, DEFAULT_LAYOUT_PATH, DEFAULT_SAVE_PATH, DEFAULT_STORE_PATH, FrontmatterAdapter, FrontmatterContext, FrontmatterField, FrontmatterFields, FrontmatterOptions, FrontmatterRecord, FrontmatterValue, Heading4BlockObjectResponse, MetadataValue, NoblogBlock, NoblogFileConfig, NotionPropertyMap, NotionReader, NotionSource, NotionSourceConfig, PageMetadata, RenderOptions, RenderedPage, StoreLayoutOptions, WritePagesOptions, bold, bullet, callout, codeBlock, createFrontmatterAdapter, createNotionSource, divider, equation, extractPageMetadata, frontmatterFieldSchema, frontmatterFieldsSchema, heading1, heading2, heading3, heading4, image, inlineCode, inlineEquation, italic, link, noblogConfigSchema, notionPropertiesSchema, quote, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, writePages };
|
|
258
|
+
export { BUILT_IN_FRONTMATTER_SOURCES, BlockTransformer, BlockTransformerContext, BlockTransformers, CalloutIcon, ChildPageWrapper, ConfigValue, DEFAULT_CHILD_PAGE_MARKER, DEFAULT_FRONTMATTER_FIELDS, DEFAULT_LAYOUT_PATH, DEFAULT_SAVE_PATH, DEFAULT_SELFIE_PATH, DEFAULT_STORE_PATH, FrontmatterAdapter, FrontmatterContext, FrontmatterField, FrontmatterFields, FrontmatterOptions, FrontmatterRecord, FrontmatterValue, Heading4BlockObjectResponse, MetadataValue, NoblogBlock, NoblogFileConfig, NotionPropertyMap, NotionReader, NotionSource, NotionSourceConfig, PageMetadata, RenderOptions, RenderedPage, SelfieMatch, StoreLayoutOptions, WritePagesOptions, bold, bullet, callout, codeBlock, createFrontmatterAdapter, createNotionSource, divider, equation, extractPageMetadata, findSelfiePage, frontmatterFieldSchema, frontmatterFieldsSchema, heading1, heading2, heading3, heading4, image, inlineCode, inlineEquation, italic, link, noblogConfigSchema, notionPropertiesSchema, quote, relativizeStoreLinks, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, upsertFrontmatterAuthor, writePages, writeSelfie };
|
|
232
259
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as noblogConfigSchema, i as frontmatterFieldsSchema, n as BUILT_IN_FRONTMATTER_SOURCES, o as notionPropertiesSchema, r as frontmatterFieldSchema } from "./errors-
|
|
2
|
-
import {
|
|
3
|
-
import { A as
|
|
1
|
+
import { a as noblogConfigSchema, i as frontmatterFieldsSchema, n as BUILT_IN_FRONTMATTER_SOURCES, o as notionPropertiesSchema, r as frontmatterFieldSchema } from "./errors-v1sSdZ3l.js";
|
|
2
|
+
import { a as DEFAULT_STORE_PATH, i as DEFAULT_SELFIE_PATH, n as DEFAULT_LAYOUT_PATH, r as DEFAULT_SAVE_PATH, t as DEFAULT_CHILD_PAGE_MARKER } from "./defaults-DdsgY0uy.js";
|
|
3
|
+
import { A as todo, C as inlineCode, D as quote, E as link, M as underline, N as renderFrontmatter, O as strikethrough, P as extractPageMetadata, S as image, T as italic, _ as equation, a as findSelfiePage, b as heading3, c as renderPage, d as renderRichText, f as bold, g as divider, h as codeBlock, i as writeSelfie, j as toggle, k as table, l as renderPages, m as callout, n as resolveStoreLayoutPath, o as relativizeStoreLinks, p as bullet, r as writePages, s as upsertFrontmatterAuthor, t as createNotionSource, u as renderPlainText, v as heading1, w as inlineEquation, x as heading4, y as heading2 } from "./client-CJ_0duRz.js";
|
|
4
4
|
import { n as createFrontmatterAdapter, t as DEFAULT_FRONTMATTER_FIELDS } from "./frontmatter-fields-BkXq38CO.js";
|
|
5
|
-
export { BUILT_IN_FRONTMATTER_SOURCES, DEFAULT_CHILD_PAGE_MARKER, DEFAULT_FRONTMATTER_FIELDS, DEFAULT_LAYOUT_PATH, DEFAULT_SAVE_PATH, DEFAULT_STORE_PATH, bold, bullet, callout, codeBlock, createFrontmatterAdapter, createNotionSource, divider, equation, extractPageMetadata, frontmatterFieldSchema, frontmatterFieldsSchema, heading1, heading2, heading3, heading4, image, inlineCode, inlineEquation, italic, link, noblogConfigSchema, notionPropertiesSchema, quote, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, writePages };
|
|
5
|
+
export { BUILT_IN_FRONTMATTER_SOURCES, DEFAULT_CHILD_PAGE_MARKER, DEFAULT_FRONTMATTER_FIELDS, DEFAULT_LAYOUT_PATH, DEFAULT_SAVE_PATH, DEFAULT_SELFIE_PATH, DEFAULT_STORE_PATH, bold, bullet, callout, codeBlock, createFrontmatterAdapter, createNotionSource, divider, equation, extractPageMetadata, findSelfiePage, frontmatterFieldSchema, frontmatterFieldsSchema, heading1, heading2, heading3, heading4, image, inlineCode, inlineEquation, italic, link, noblogConfigSchema, notionPropertiesSchema, quote, relativizeStoreLinks, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, upsertFrontmatterAuthor, writePages, writeSelfie };
|
package/package.json
CHANGED
package/schema/config.json
CHANGED
|
@@ -13,7 +13,7 @@ Create a `version: 1` Noblog YAML file that matches the installed theme, not a g
|
|
|
13
13
|
2. **Inspect the real content contract.** Read collection schemas, Zod/Valibot/TypeScript frontmatter definitions, Markdown layouts, and the code that lists or routes content. For Astro, check both `src/content.config.ts` and `src/content/config.ts`, plus `Astro.glob` or dynamic collection routes. A layout alone is not proof of validation rules.
|
|
14
14
|
3. **Record the contract before writing YAML.** Note required and optional fields, native types, defaults, date handling, collection glob patterns, and which directories produce routes. Distinguish file-based Markdown routes from content collections.
|
|
15
15
|
4. **Confirm Notion property names.** Use names supplied by the user or visible in existing configuration/code. Do not invent custom properties or request credentials. Map logical sources to exact Notion names under `notion.properties`.
|
|
16
|
-
5. **Choose output paths with routing in mind.** `savePath` receives database-post symlinks; `storePath` receives every real Markdown file, including children. Child links target `./<page-id>.md` inside the store and are prefixed by `output.childPageMarker` (default `⸎`, U+2E0E EDITORIAL CORONIS); set it if the theme expects a specific marker. If the theme expects a wrapper, use `output.childPageWrapper` with non-empty single-line `head`/`tail` values; it overrides the marker. Ensure the theme actually loads/routes the store if child pages must be public. Noblog does not create `nob_children` routes or remove old nested views.
|
|
16
|
+
5. **Choose output paths with routing in mind.** `savePath` receives database-post symlinks; `storePath` receives every real Markdown file, including children. Child links target `./<page-id>.md` inside the store and are prefixed by `output.childPageMarker` (default `⸎`, U+2E0E EDITORIAL CORONIS); set it if the theme expects a specific marker. If the theme expects a wrapper, use `output.childPageWrapper` with non-empty single-line `head`/`tail` values; it overrides the marker. Ensure the theme actually loads/routes the store if child pages must be public. Noblog does not create `nob_children` routes or remove old nested views. If the site renders an author/footer bio from Markdown (for example `frontmatter.author`), set `output.selfiePath` and have the author build pass `--selfie PAGE_TITLE` so the matching database page is written there with `author` set; without a match the committed file stays untouched.
|
|
17
17
|
6. **Write only the fields the theme consumes.** Custom `frontmatter.fields` replaces the legacy field set. Use exactly one of `from` or `value` per field. Keep booleans, numbers, arrays, and objects native unless the theme requires strings.
|
|
18
18
|
7. **Validate without generating.** Run:
|
|
19
19
|
|
|
@@ -32,6 +32,7 @@ output:
|
|
|
32
32
|
| `recursive` | `true` | Collect and render child pages recursively. |
|
|
33
33
|
| `childPageMarker` | `⸎` (U+2E0E EDITORIAL CORONIS) | Non-empty prefix added to child-page links. Set it to the marker the theme recognizes. |
|
|
34
34
|
| `childPageWrapper` | — | Optional `{ head, tail }` wrapper around child-page links. Overrides `childPageMarker`. Both values must be non-empty single-line strings. |
|
|
35
|
+
| `selfiePath` | `.noblog/selfie.md` | Output file for the page named by `--selfie PAGE_TITLE`, written next to the canonical store. Written only when the title matches a database page; otherwise the existing file is left untouched. Child-page links inside the file are rewritten so they resolve from this file's directory. |
|
|
35
36
|
|
|
36
37
|
Optional wrapper (overrides `childPageMarker`):
|
|
37
38
|
|
|
@@ -17,6 +17,8 @@ Nobloger uses file-based Markdown routes and `Astro.glob("@/pages/posts/*.md")`.
|
|
|
17
17
|
|
|
18
18
|
The existing glob does not list child pages in the hidden store. A theme route must explicitly load that store to expose children. Noblog does not recreate the removed `nob_children` routes.
|
|
19
19
|
|
|
20
|
+
When the Nobloger site renders `frontmatter.author` in its footer, the author's build can pass `--selfie humbornjo` so the bio page from the author's database is written to `output.selfiePath` (`.noblog/selfie.md` by default) with `author` set to the page title. Other builds, and builds with no matching title, keep the committed `.noblog/selfie.md` sample.
|
|
21
|
+
|
|
20
22
|
## ThoughtLite
|
|
21
23
|
|
|
22
24
|
Evidence inspected: `src/content.config.ts` in [tuyuritio/astro-theme-thought-lite](https://github.com/tuyuritio/astro-theme-thought-lite). Its `note` collection loads `./src/content/note/**/*.md` and validates `title`, native date `timestamp`, optional `series`, `tags`, `description`, and native `sensitive`, `toc`, `top`, and `draft` controls.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client-BIBJQzBg.js","names":["md.toggle","md.callout","md.bullet","md.image","md.divider","md.equation","md.link","md.heading2","md.codeBlock","md.heading1","md.heading3","md.heading4","md.quote","md.todo","link","md.table"],"sources":["../src/lib/noblog/metadata.ts","../src/lib/noblog/frontmatter.ts","../src/lib/noblog/markdown.ts","../src/lib/noblog/rich-text.ts","../src/lib/noblog/blocks.ts","../src/lib/noblog/pages.ts","../src/lib/noblog/writer.ts","../src/lib/notion/client.ts"],"sourcesContent":["import type { PageObjectResponse } from \"@notionhq/client\";\nimport type { NotionPropertyMap } from \"./config.js\";\n\ntype NotionProperty = PageObjectResponse[\"properties\"][string];\n\nexport type MetadataValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined;\n\nexport interface PageMetadata {\n readonly title: string;\n readonly description?: string | undefined;\n readonly tags: readonly string[];\n readonly publishedAt?: string | undefined;\n readonly archived?: boolean | undefined;\n readonly values: Readonly<Record<string, MetadataValue>>;\n}\n\nconst DEFAULT_PROPERTIES = {\n description: \"description\",\n tags: \"tags\",\n publishedAt: \"date\",\n archived: \"archived\",\n} as const;\n\nexport function extractPageMetadata(\n page: PageObjectResponse,\n properties: NotionPropertyMap = {},\n): PageMetadata {\n const propertyNames = new Map<string, string>(Object.entries(properties));\n for (const [key, name] of Object.entries(DEFAULT_PROPERTIES)) {\n if (!propertyNames.has(key)) propertyNames.set(key, name);\n }\n\n const values: Record<string, MetadataValue> = {};\n if (!propertyNames.has(\"title\")) {\n const titleProperty = Object.values(page.properties).findLast(\n (property) => property.type === \"title\",\n );\n values[\"title\"] = normalizeProperty(titleProperty);\n }\n for (const [key, name] of propertyNames) {\n values[key] = normalizeProperty(page.properties[name]);\n }\n\n return {\n title: asString(values[\"title\"]) ?? \"\",\n description: asString(values[\"description\"]),\n tags: asStringArray(values[\"tags\"]) ?? [],\n publishedAt: asString(values[\"publishedAt\"]),\n archived: asBoolean(values[\"archived\"]),\n values,\n };\n}\n\nfunction normalizeProperty(property?: NotionProperty): MetadataValue {\n if (!property) return undefined;\n\n switch (property.type) {\n case \"title\":\n return plainText(property.title);\n case \"rich_text\":\n return plainText(property.rich_text);\n case \"multi_select\":\n return property.multi_select.map(({ name }) => name);\n case \"select\":\n return property.select?.name;\n case \"status\":\n return property.status?.name;\n case \"checkbox\":\n return property.checkbox;\n case \"number\":\n return property.number ?? undefined;\n case \"date\":\n return property.date?.start;\n case \"url\":\n return property.url ?? undefined;\n case \"email\":\n return property.email ?? undefined;\n case \"phone_number\":\n return property.phone_number ?? undefined;\n case \"people\":\n return property.people.map(entityName);\n case \"relation\":\n return property.relation.map(({ id }) => id);\n case \"files\":\n return property.files.map((file) =>\n \"file\" in file ? file.file.url : file.external.url,\n );\n case \"formula\":\n return normalizeFormula(property.formula);\n case \"created_time\":\n return property.created_time;\n case \"last_edited_time\":\n return property.last_edited_time;\n case \"created_by\":\n return entityName(property.created_by);\n case \"last_edited_by\":\n return entityName(property.last_edited_by);\n case \"unique_id\":\n return property.unique_id.prefix\n ? `${property.unique_id.prefix}-${property.unique_id.number}`\n : String(property.unique_id.number);\n default:\n return undefined;\n }\n}\n\nfunction normalizeFormula(\n formula: Extract<NotionProperty, { type: \"formula\" }>[\"formula\"],\n): MetadataValue {\n switch (formula.type) {\n case \"string\":\n return formula.string ?? undefined;\n case \"number\":\n return formula.number ?? undefined;\n case \"boolean\":\n return formula.boolean ?? undefined;\n case \"date\":\n return formula.date?.start;\n }\n}\n\nfunction plainText(\n richText: Extract<NotionProperty, { type: \"title\" }>[\"title\"],\n): string {\n return richText.map((item) => item.plain_text).join(\"\");\n}\n\nfunction entityName(entity: {\n readonly id: string;\n readonly name?: string | null;\n}): string {\n return entity.name ?? entity.id;\n}\n\nfunction asString(value: MetadataValue): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction asBoolean(value: MetadataValue): boolean | undefined {\n return typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction asStringArray(value: MetadataValue): readonly string[] | undefined {\n if (typeof value === \"string\") return [value];\n return Array.isArray(value) ? value : undefined;\n}\n","import type { PageObjectResponse } from \"@notionhq/client\";\nimport { stringify } from \"yaml\";\nimport type { NotionPropertyMap } from \"./config.js\";\nimport { DEFAULT_LAYOUT_PATH } from \"./defaults.js\";\nimport { extractPageMetadata } from \"./metadata.js\";\nimport type { FrontmatterAdapter, FrontmatterRecord } from \"./types.js\";\n\nexport interface FrontmatterOptions {\n readonly layoutPath?: string;\n readonly propertyMap?: NotionPropertyMap;\n readonly frontmatter?: FrontmatterAdapter;\n readonly isDatabasePage?: boolean;\n}\n\nexport function renderFrontmatter(\n page: PageObjectResponse,\n options: FrontmatterOptions = {},\n): string {\n const { layoutPath = DEFAULT_LAYOUT_PATH } = options;\n if (options.frontmatter) {\n const metadata = extractPageMetadata(page, options.propertyMap);\n const values = options.frontmatter({\n page,\n metadata,\n layoutPath,\n isDatabasePage: options.isDatabasePage ?? true,\n });\n return serializeFrontmatter(values);\n }\n\n let title = \"\";\n let description = \"\";\n let tags: string[] = [];\n let pubDate = \"\";\n let archived = \"\";\n\n for (const [name, property] of Object.entries(page.properties)) {\n switch (property.type) {\n case \"title\":\n title = property.title.map((item) => item.plain_text).join(\"\");\n break;\n case \"multi_select\":\n if (name === \"tags\") {\n tags = property.multi_select.map((tag) => tag.name);\n }\n break;\n case \"date\":\n if (name === \"date\") {\n pubDate =\n property.date?.start ?? new Date().toISOString().slice(0, 10);\n }\n break;\n }\n\n if (name === \"archived\" && property.type === \"checkbox\") {\n archived = property.checkbox ? \"true\" : \"false\";\n }\n if (name === \"description\" && property.type === \"rich_text\") {\n description = property.rich_text\n .map((item) => item.plain_text)\n .join(\"\");\n }\n }\n\n return (\n \"---\\n\" +\n `layout: ${layoutPath}\\n` +\n `title: ${JSON.stringify(title)}\\n` +\n `description: ${JSON.stringify(description)}\\n` +\n `tags: ${JSON.stringify(tags)}\\n` +\n `pubDate: ${JSON.stringify(pubDate)}\\n` +\n `archived: ${JSON.stringify(archived)}\\n` +\n \"---\\n\"\n );\n}\n\nfunction serializeFrontmatter(values: FrontmatterRecord): string {\n if (\n values === null ||\n typeof values !== \"object\" ||\n Array.isArray(values) ||\n (Object.getPrototypeOf(values) !== Object.prototype &&\n Object.getPrototypeOf(values) !== null)\n ) {\n throw new Error(\"Frontmatter adapter must return a plain object\");\n }\n\n const entries = Object.entries(values).filter(\n ([, value]) => value !== undefined,\n );\n if (entries.length === 0) return \"---\\n---\\n\";\n\n const yaml = stringify(Object.fromEntries(entries), {\n aliasDuplicateObjects: false,\n schema: \"yaml-1.1\",\n });\n return `---\\n${yaml}---\\n`;\n}\n","import { markdownTable } from \"markdown-table\";\nimport type { CalloutIcon } from \"./types.js\";\n\nexport const inlineCode = (text: string) => `\\`${text}\\``;\nexport const inlineEquation = (text: string) => `$${text}$`;\nexport const bold = (text: string) => `**${text}**`;\nexport const italic = (text: string) => `_${text}_`;\nexport const strikethrough = (text: string) => `~~${text}~~`;\nexport const underline = (text: string) => `<u>${text}</u>`;\nexport const link = (text: string, href: string) => `[${text}](${href})`;\n\nexport function codeBlock(text: string, language = \"\"): string {\n const normalizedLanguage = language === \"plain text\" ? \"text\" : language;\n return `\\`\\`\\`${normalizedLanguage}\\n${text}\\n\\`\\`\\``;\n}\n\nexport const equation = (text: string) => `$$\\n${text}\\n$$`;\nexport const heading1 = (text: string) => `# ${text}`;\nexport const heading2 = (text: string) => `## ${text}`;\nexport const heading3 = (text: string) => `### ${text}`;\nexport const heading4 = (text: string) => `#### ${text}`;\nexport const quote = (text: string) => `> ${text.replace(/\\n/g, \" \\n> \")}`;\n\nexport function callout(text: string, icon?: CalloutIcon): string {\n const emoji = icon?.type === \"emoji\" ? `${icon.emoji} ` : \"\";\n return `> ${emoji}${text.replace(/\\n/g, \" \\n> \")}`;\n}\n\nexport function bullet(text: string, count?: number): string {\n const rendered = text.trim();\n return count === undefined ? `- ${rendered}` : `${count}. ${rendered}`;\n}\n\nexport const todo = (text: string, checked: boolean) =>\n checked ? `- [x] ${text}` : `- [ ] ${text}`;\n\nexport async function image(\n alt: string,\n href: string,\n convertToBase64 = false,\n): Promise<string> {\n if (!convertToBase64 || href.startsWith(\"data:\")) {\n if (href.startsWith(\"data:\")) {\n const base64 = href.split(\",\").pop();\n return ``;\n }\n return ``;\n }\n\n const response = await fetch(href);\n if (!response.ok) {\n throw new Error(\n `Failed to download image: ${response.status} ${response.statusText}`,\n );\n }\n const buffer = await response.arrayBuffer();\n return `.toString(\"base64\")})`;\n}\n\nexport const divider = () => \"---\";\n\nexport function toggle(summary?: string, children?: string): string {\n if (!summary) return children ?? \"\";\n return `<details>\\n<summary>${summary}</summary>\\n${children ?? \"\"}\\n</details>\\n\\n`;\n}\n\nexport function table(cells: string[][], hasColumnHeader = true): string {\n if (hasColumnHeader || cells.length === 0) return markdownTable(cells);\n\n const width = Math.max(...cells.map((row) => row.length));\n if (width === 0) return \"\";\n const emptyHeader = Array.from({ length: width }, () => \"\");\n return markdownTable([emptyHeader, ...cells]);\n}\n","import type { RichTextItemResponse } from \"@notionhq/client\";\nimport { inlineEquation, link } from \"./markdown.js\";\n\ntype AnnotationName = Exclude<\n keyof RichTextItemResponse[\"annotations\"],\n \"color\"\n>;\n\ninterface AnnotationFormat {\n readonly name: AnnotationName;\n readonly open: string;\n readonly close: string;\n}\n\n// Ordered from the outermost marker to the innermost marker. Stable sorting\n// keeps this order when annotations end on the same rich-text segment.\nconst ANNOTATION_FORMATS: readonly AnnotationFormat[] = [\n { name: \"strikethrough\", open: \"~~\", close: \"~~\" },\n { name: \"underline\", open: \"<u>\", close: \"</u>\" },\n { name: \"italic\", open: \"_\", close: \"_\" },\n { name: \"bold\", open: \"**\", close: \"**\" },\n { name: \"code\", open: \"`\", close: \"`\" },\n];\n\nexport function renderPlainText(\n richText: readonly RichTextItemResponse[],\n): string {\n return richText.map((item) => item.plain_text).join(\"\");\n}\n\nexport function renderRichText(\n richText: readonly RichTextItemResponse[],\n): string {\n let rendered = \"\";\n let active: AnnotationFormat[] = [];\n\n for (const [index, item] of richText.entries()) {\n const desired = getAnnotationFormats(richText, index, item);\n let shared = 0;\n while (\n shared < active.length &&\n shared < desired.length &&\n active[shared]?.name === desired[shared]?.name\n ) {\n shared++;\n }\n\n for (const format of active.slice(shared).toReversed()) {\n rendered += format.close;\n }\n for (const format of desired.slice(shared)) {\n rendered += format.open;\n }\n\n rendered += renderContent(item);\n active = desired;\n }\n\n for (const format of active.toReversed()) {\n rendered += format.close;\n }\n return rendered;\n}\n\nfunction getAnnotationFormats(\n richText: readonly RichTextItemResponse[],\n index: number,\n item: RichTextItemResponse,\n): AnnotationFormat[] {\n if (item.type === \"equation\") return [];\n\n return ANNOTATION_FORMATS.filter((format) => item.annotations[format.name])\n .map((format) => ({\n format,\n runEnd: getAnnotationRunEnd(richText, index, format.name),\n }))\n .sort((left, right) => right.runEnd - left.runEnd)\n .map(({ format }) => format);\n}\n\nfunction getAnnotationRunEnd(\n richText: readonly RichTextItemResponse[],\n start: number,\n name: AnnotationName,\n): number {\n let end = start;\n for (const [offset, next] of richText.slice(start + 1).entries()) {\n if (next.type === \"equation\" || !next.annotations[name]) break;\n end = start + offset + 1;\n }\n return end;\n}\n\nfunction renderContent(item: RichTextItemResponse): string {\n if (item.type === \"equation\") {\n return inlineEquation(item.equation.expression);\n }\n return item.href ? link(item.plain_text, item.href) : item.plain_text;\n}\n","import type { BlockObjectResponse } from \"@notionhq/client\";\nimport { DEFAULT_CHILD_PAGE_MARKER } from \"./defaults.js\";\nimport * as md from \"./markdown.js\";\nimport { renderPlainText, renderRichText } from \"./rich-text.js\";\nimport type {\n BlockTransformerContext,\n BlockTransformers,\n ChildPageWrapper,\n NoblogBlock,\n NotionReader,\n} from \"./types.js\";\n\nexport interface BlockNode {\n readonly block: NoblogBlock;\n readonly children: BlockNode[];\n}\n\nexport interface BlockRenderContext extends BlockTransformerContext {\n readonly childPageMarker?: string;\n readonly childPageWrapper?: ChildPageWrapper;\n readonly transformers?: BlockTransformers;\n}\n\nexport interface RenderedBlocks {\n content: string;\n childPageIds: string[];\n}\n\nexport async function loadBlockTree(\n blockId: string,\n reader: NotionReader,\n ancestors: ReadonlySet<string> = new Set(),\n): Promise<BlockNode[]> {\n if (ancestors.has(blockId)) return [];\n\n const blocks = await reader.getBlockChildren(blockId);\n const childAncestors = new Set(ancestors).add(blockId);\n const nodes: BlockNode[] = [];\n\n for (const block of blocks) {\n if (block.type === \"unsupported\") continue;\n\n const childSourceId = getChildSourceId(block);\n const children =\n childSourceId && !childAncestors.has(childSourceId)\n ? await loadBlockTree(childSourceId, reader, childAncestors)\n : [];\n nodes.push({ block, children });\n }\n\n return nodes;\n}\n\nfunction getChildSourceId(block: NoblogBlock): string | undefined {\n if (block.type === \"child_page\" || block.type === \"child_database\") {\n return undefined;\n }\n if (block.type === \"synced_block\") {\n return (\n block.synced_block.synced_from?.block_id ??\n (block.has_children ? block.id : undefined)\n );\n }\n return block.has_children ? block.id : undefined;\n}\n\nexport async function renderBlockTree(\n nodes: readonly BlockNode[],\n context: BlockRenderContext,\n): Promise<RenderedBlocks> {\n const result: RenderedBlocks = { content: \"\", childPageIds: [] };\n let numberedListIndex = 0;\n\n for (const node of nodes) {\n const { block } = node;\n numberedListIndex =\n block.type === \"numbered_list_item\" ? numberedListIndex + 1 : 0;\n\n const children =\n block.type === \"table\"\n ? { content: \"\", childPageIds: [] }\n : await renderBlockTree(node.children, context);\n const content = await renderBlockNode(\n node,\n children.content,\n numberedListIndex || undefined,\n context,\n );\n\n if (block.type === \"toggle\") {\n result.content += content;\n } else {\n const lineWrap = isListBlock(block) ? \"\" : \"\\n\";\n result.content += `${lineWrap}${content}\\n${lineWrap}`;\n if (!containsOwnChildren(block)) {\n result.content += children.content;\n }\n }\n\n if (block.type === \"child_page\") {\n result.childPageIds.push(block.id);\n } else {\n result.childPageIds.push(...children.childPageIds);\n }\n }\n\n return result;\n}\n\nfunction isListBlock(block: NoblogBlock): boolean {\n return (\n block.type === \"to_do\" ||\n block.type === \"bulleted_list_item\" ||\n block.type === \"numbered_list_item\"\n );\n}\n\nfunction containsOwnChildren(block: NoblogBlock): boolean {\n return (\n block.type === \"child_page\" ||\n block.type === \"callout\" ||\n block.type === \"bulleted_list_item\" ||\n block.type === \"numbered_list_item\" ||\n block.type === \"table\"\n );\n}\n\nfunction transformBlock<K extends NoblogBlock[\"type\"]>(\n type: K,\n block: Extract<NoblogBlock, { type: NoInfer<K> }>,\n context: BlockRenderContext,\n): string | undefined | Promise<string | undefined> {\n const transformer = context.transformers?.[type];\n return transformer?.(block, context);\n}\n\nasync function renderBlockNode(\n node: BlockNode,\n children: string,\n listNumber: number | undefined,\n context: BlockRenderContext,\n): Promise<string> {\n const { block } = node;\n const transformed = await transformBlock(block.type, block, context);\n const content =\n transformed ??\n (await renderDefaultBlock(\n node,\n context.childPageMarker ?? DEFAULT_CHILD_PAGE_MARKER,\n context.childPageWrapper,\n ));\n\n switch (block.type) {\n case \"toggle\":\n return md.toggle(content, children);\n case \"callout\":\n return md.callout(\n children ? `${content}\\n${children}`.trim() : content,\n block.callout.icon,\n );\n case \"bulleted_list_item\":\n return md.bullet(withIndentedChildren(content, children, 2));\n case \"numbered_list_item\":\n return md.bullet(\n withIndentedChildren(\n content,\n children,\n `${listNumber ?? 1}. `.length,\n ),\n listNumber,\n );\n default:\n return content;\n }\n}\n\nfunction withIndentedChildren(\n content: string,\n children: string,\n markerWidth: number,\n): string {\n const nested = children.trim();\n if (!nested) return content;\n\n const indentation = \" \".repeat(markerWidth);\n const indented = nested\n .split(\"\\n\")\n .map((line) => (line ? `${indentation}${line}` : \"\"))\n .join(\"\\n\");\n return `${content}\\n${indented}`;\n}\n\nasync function renderDefaultBlock(\n node: BlockNode,\n childPageMarker: string,\n childPageWrapper: ChildPageWrapper | undefined,\n): Promise<string> {\n const { block } = node;\n\n switch (block.type) {\n case \"image\": {\n const url = getFileUrl(block.image);\n const caption = renderPlainText(block.image.caption);\n const alt = caption.trim() || getFileName(url) || \"image\";\n return md.image(alt, url);\n }\n case \"divider\":\n return md.divider();\n case \"equation\":\n return md.equation(block.equation.expression);\n case \"pdf\":\n return renderFileLink(block.pdf, block.type);\n case \"file\":\n return renderFileLink(block.file, block.type);\n case \"video\":\n return renderFileLink(block.video, block.type);\n case \"audio\":\n return renderFileLink(block.audio, block.type);\n case \"embed\":\n return md.link(block.type, block.embed.url);\n case \"bookmark\":\n return md.link(block.type, block.bookmark.url);\n case \"link_preview\":\n return md.link(block.type, block.link_preview.url);\n case \"link_to_page\": {\n const target = block.link_to_page;\n if (target.type !== \"page_id\" && target.type !== \"database_id\") {\n return \"\";\n }\n const id = target.type === \"page_id\" ? target.page_id : target.database_id;\n return md.link(block.type, `https://www.notion.so/${id}`);\n }\n case \"child_page\":\n return renderChildPageLink(block, childPageMarker, childPageWrapper);\n case \"child_database\":\n return md.heading2(block.child_database.title || \"child_database\");\n case \"table\":\n return renderTable(node.children, block.table.has_column_header);\n case \"code\":\n return md.codeBlock(\n renderRichText(block.code.rich_text),\n block.code.language,\n );\n case \"heading_1\":\n return md.heading1(renderRichText(block.heading_1.rich_text));\n case \"heading_2\":\n return md.heading2(renderRichText(block.heading_2.rich_text));\n case \"heading_3\":\n return md.heading3(renderRichText(block.heading_3.rich_text));\n case \"heading_4\":\n return md.heading4(renderRichText(block.heading_4.rich_text));\n case \"quote\":\n return md.quote(renderRichText(block.quote.rich_text));\n case \"to_do\":\n return md.todo(\n renderRichText(block.to_do.rich_text),\n block.to_do.checked,\n );\n case \"paragraph\":\n return renderRichText(block.paragraph.rich_text);\n case \"toggle\":\n return renderRichText(block.toggle.rich_text);\n case \"callout\":\n return renderRichText(block.callout.rich_text);\n case \"bulleted_list_item\":\n return renderRichText(block.bulleted_list_item.rich_text);\n case \"numbered_list_item\":\n return renderRichText(block.numbered_list_item.rich_text);\n case \"template\":\n return renderRichText(block.template.rich_text);\n default:\n return \"\";\n }\n}\n\ntype FileContent =\n | Extract<BlockObjectResponse, { type: \"image\" }>[\"image\"]\n | Extract<BlockObjectResponse, { type: \"pdf\" }>[\"pdf\"]\n | Extract<BlockObjectResponse, { type: \"file\" }>[\"file\"]\n | Extract<BlockObjectResponse, { type: \"video\" }>[\"video\"]\n | Extract<BlockObjectResponse, { type: \"audio\" }>[\"audio\"];\n\nfunction getFileUrl(content: FileContent): string {\n if (content.type === \"external\") return content.external.url;\n return content.file.url;\n}\n\nfunction getFileName(url: string): string | undefined {\n return url.match(/[^\\/\\\\&?]+\\.\\w{3,4}(?=([?&].*$|$))/)?.[0];\n}\n\nfunction renderFileLink(content: FileContent, fallbackTitle: string): string {\n const url = getFileUrl(content);\n const caption = renderPlainText(content.caption);\n const title = caption.trim() || getFileName(url) || fallbackTitle;\n return md.link(title, url);\n}\n\nfunction renderChildPageLink(\n block: Extract<BlockObjectResponse, { type: \"child_page\" }>,\n marker: string,\n wrapper: ChildPageWrapper | undefined,\n): string {\n // The theme consumes the marker or wrapper to render subpage links.\n const link = md.link(block.child_page.title, `./${block.id}.md`);\n return wrapper === undefined\n ? `${marker} ${link}`\n : `${wrapper.head}${link}${wrapper.tail}`;\n}\n\nfunction renderTable(\n nodes: readonly BlockNode[],\n hasColumnHeader: boolean,\n): string {\n const rows = nodes.flatMap(({ block }) => {\n if (block.type !== \"table_row\") return [];\n return [\n block.table_row.cells.map((cell) => renderRichText(cell)),\n ];\n });\n return md.table(rows, hasColumnHeader);\n}\n","import type { PageObjectResponse } from \"@notionhq/client\";\nimport { loadBlockTree, renderBlockTree } from \"./blocks.js\";\nimport {\n DEFAULT_CHILD_PAGE_MARKER,\n DEFAULT_LAYOUT_PATH,\n} from \"./defaults.js\";\nimport { renderFrontmatter } from \"./frontmatter.js\";\nimport type { NotionPropertyMap } from \"./config.js\";\nimport type {\n BlockTransformers,\n ChildPageWrapper,\n FrontmatterAdapter,\n NotionReader,\n RenderedPage,\n RenderOptions,\n} from \"./types.js\";\n\ninterface PageRenderContext {\n readonly reader: NotionReader;\n readonly databasePageIds: ReadonlySet<string>;\n readonly layoutPath: string;\n readonly propertyMap: NotionPropertyMap | undefined;\n readonly frontmatter: FrontmatterAdapter | undefined;\n readonly recursive: boolean;\n readonly childPageMarker: string;\n readonly childPageWrapper: ChildPageWrapper | undefined;\n readonly transformers: BlockTransformers | undefined;\n readonly renderedPages: Map<string, Promise<RenderedPage>>;\n}\n\nexport async function renderPage(\n pageId: string,\n reader: NotionReader,\n options: RenderOptions = {},\n): Promise<RenderedPage> {\n const context = createContext([pageId], reader, options);\n return collectPage(pageId, context);\n}\n\nexport async function renderPages(\n posts: readonly PageObjectResponse[],\n reader: NotionReader,\n options: RenderOptions = {},\n): Promise<RenderedPage[]> {\n const context = createContext(\n posts.map((post) => post.id),\n reader,\n options,\n );\n\n for (const post of posts) {\n await collectPage(post.id, context, post);\n }\n\n return Promise.all([...context.renderedPages.values()]);\n}\n\nfunction createContext(\n databasePageIds: readonly string[],\n reader: NotionReader,\n options: RenderOptions,\n): PageRenderContext {\n return {\n reader,\n databasePageIds: new Set(databasePageIds),\n layoutPath: options.layoutPath ?? DEFAULT_LAYOUT_PATH,\n propertyMap: options.propertyMap,\n frontmatter: options.frontmatter,\n recursive: options.recursive ?? true,\n childPageMarker: options.childPageMarker ?? DEFAULT_CHILD_PAGE_MARKER,\n childPageWrapper: options.childPageWrapper,\n transformers: options.transformers,\n renderedPages: new Map(),\n };\n}\n\nasync function collectPage(\n pageId: string,\n context: PageRenderContext,\n post?: PageObjectResponse,\n): Promise<RenderedPage> {\n const cached = context.renderedPages.get(pageId);\n if (cached) return cached;\n\n const rendering = renderSinglePage(pageId, context, post);\n context.renderedPages.set(pageId, rendering);\n const rendered = await rendering;\n\n if (context.recursive) {\n for (const childPageId of rendered.childPageIds) {\n await collectPage(childPageId, context);\n }\n }\n\n return rendered;\n}\n\nasync function renderSinglePage(\n pageId: string,\n context: PageRenderContext,\n post?: PageObjectResponse,\n): Promise<RenderedPage> {\n const [page, blockTree] = await Promise.all([\n post ? Promise.resolve(post) : context.reader.getPage(pageId),\n loadBlockTree(pageId, context.reader),\n ]);\n const rendered = await renderBlockTree(blockTree, {\n pageId,\n childPageMarker: context.childPageMarker,\n ...(context.childPageWrapper !== undefined\n ? { childPageWrapper: context.childPageWrapper }\n : {}),\n ...(context.transformers !== undefined\n ? { transformers: context.transformers }\n : {}),\n });\n const frontmatter = renderFrontmatter(page, {\n layoutPath: context.layoutPath,\n ...(context.propertyMap !== undefined\n ? { propertyMap: context.propertyMap }\n : {}),\n ...(context.frontmatter !== undefined\n ? { frontmatter: context.frontmatter }\n : {}),\n isDatabasePage: context.databasePageIds.has(pageId),\n });\n\n return {\n id: pageId,\n content: frontmatter + rendered.content,\n childPageIds: [...new Set(rendered.childPageIds)],\n isDatabasePage: context.databasePageIds.has(pageId),\n };\n}\n","import { randomUUID } from \"node:crypto\";\nimport {\n lstat,\n mkdir,\n readlink,\n rename,\n rm,\n symlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { getErrorCode } from \"../errors.js\";\nimport {\n DEFAULT_LAYOUT_PATH,\n DEFAULT_SAVE_PATH,\n DEFAULT_STORE_PATH,\n} from \"./defaults.js\";\nimport type {\n RenderedPage,\n StoreLayoutOptions,\n WritePagesOptions,\n} from \"./types.js\";\n\nexport async function writePages(\n pages: readonly RenderedPage[],\n options: WritePagesOptions = {},\n): Promise<string[]> {\n const savePath = options.savePath ?? DEFAULT_SAVE_PATH;\n const storePath = options.storePath ?? DEFAULT_STORE_PATH;\n const writtenPaths: string[] = [];\n await mkdir(storePath, { recursive: true });\n\n for (const page of pages) {\n const storedPath = join(storePath, `${page.id}.md`);\n const storedStats = await getStats(storedPath);\n if (storedStats && !storedStats.isFile()) {\n throw new Error(`Page store path is not a regular file: ${storedPath}`);\n }\n await writeFile(storedPath, page.content, \"utf8\");\n\n if (!page.isDatabasePage) continue;\n\n const viewPath = join(savePath, `${page.id}.md`);\n await mkdir(savePath, { recursive: true });\n await linkStoredPage(storedPath, viewPath);\n writtenPaths.push(viewPath);\n }\n\n return writtenPaths;\n}\n\nexport function resolveStoreLayoutPath(\n options: StoreLayoutOptions = {},\n): string {\n const savePath = options.savePath ?? DEFAULT_SAVE_PATH;\n const storePath = options.storePath ?? DEFAULT_STORE_PATH;\n const layoutPath = options.layoutPath ?? DEFAULT_LAYOUT_PATH;\n const layoutTarget = resolve(savePath, layoutPath);\n return relative(resolve(storePath), layoutTarget).split(sep).join(\"/\");\n}\n\nasync function linkStoredPage(\n storedPath: string,\n viewPath: string,\n): Promise<void> {\n if (resolve(storedPath) === resolve(viewPath)) return;\n\n const viewStats = await getStats(viewPath);\n if (viewStats && !viewStats.isFile() && !viewStats.isSymbolicLink()) {\n throw new Error(`Refusing to replace non-file output: ${viewPath}`);\n }\n\n const target = relative(dirname(viewPath), storedPath);\n if (viewStats?.isSymbolicLink() && (await readlink(viewPath)) === target) {\n return;\n }\n\n const temporaryPath = `${viewPath}.noblog-${randomUUID()}.tmp`;\n try {\n await symlink(target, temporaryPath, \"file\");\n await rename(temporaryPath, viewPath);\n } catch (error) {\n await rm(temporaryPath, { force: true }).catch(() => {});\n throw error;\n }\n}\n\nasync function getStats(path: string) {\n try {\n return await lstat(path);\n } catch (error: unknown) {\n if (getErrorCode(error) === \"ENOENT\") return undefined;\n throw error;\n }\n}\n","import {\n Client,\n isFullBlock,\n isFullDatabase,\n isFullPage,\n isNotionClientError,\n} from \"@notionhq/client\";\nimport type {\n BlockObjectResponse,\n PageObjectResponse,\n} from \"@notionhq/client\";\nimport type { NotionReader } from \"../noblog/types.js\";\n\nexport interface NotionSourceConfig {\n readonly notionApiSecret: string;\n readonly databaseId: string;\n}\n\nexport interface NotionSource extends NotionReader {\n listPages(): Promise<PageObjectResponse[]>;\n}\n\nexport function createNotionSource(\n config: NotionSourceConfig,\n): NotionSource {\n const client = new Client({ auth: config.notionApiSecret });\n return {\n listPages: () => listPages(client, config.databaseId),\n getPage: (pageId) => getPage(client, pageId),\n getBlockChildren: (blockId) => getBlockChildren(client, blockId),\n };\n}\n\nasync function listPages(\n client: Client,\n databaseId: string,\n): Promise<PageObjectResponse[]> {\n const database = await withRetry(() =>\n client.databases.retrieve({ database_id: databaseId }),\n );\n if (!isFullDatabase(database)) {\n throw new Error(`Notion database ${databaseId} is not a full database`);\n }\n\n const pages: PageObjectResponse[] = [];\n for (const source of database.data_sources) {\n let startCursor: string | undefined;\n do {\n const response = await withRetry(() =>\n client.dataSources.query({\n data_source_id: source.id,\n filter: {\n property: \"publish\",\n checkbox: { equals: true },\n },\n sorts: [{ property: \"date\", direction: \"descending\" }],\n page_size: 100,\n ...(startCursor !== undefined\n ? { start_cursor: startCursor }\n : {}),\n }),\n );\n pages.push(...response.results.filter(isFullPage));\n startCursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (startCursor);\n }\n\n return pages;\n}\n\nasync function getPage(\n client: Client,\n pageId: string,\n): Promise<PageObjectResponse> {\n const page = await withRetry(() => client.pages.retrieve({ page_id: pageId }));\n if (!isFullPage(page)) {\n throw new Error(`Notion page ${pageId} is not a full page`);\n }\n return page;\n}\n\nasync function getBlockChildren(\n client: Client,\n blockId: string,\n): Promise<BlockObjectResponse[]> {\n const blocks: BlockObjectResponse[] = [];\n let startCursor: string | undefined;\n\n do {\n const response = await withRetry(() =>\n client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(startCursor !== undefined ? { start_cursor: startCursor } : {}),\n }),\n );\n blocks.push(...response.results.filter(isFullBlock));\n startCursor = response.next_cursor ?? undefined;\n } while (startCursor);\n\n return blocks;\n}\n\nconst MAX_ATTEMPTS = 3;\nconst INITIAL_RETRY_DELAY_MS = 500;\n\nasync function withRetry<T>(operation: () => Promise<T>): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await operation();\n } catch (error: unknown) {\n if (attempt === MAX_ATTEMPTS - 1 || !isRetryable(error)) throw error;\n await delay(INITIAL_RETRY_DELAY_MS * 2 ** attempt);\n }\n }\n}\n\nfunction isRetryable(error: unknown): boolean {\n if (!isNotionClientError(error) || !(\"status\" in error)) return true;\n return error.status === 408 || error.status === 429 || error.status >= 500;\n}\n\nfunction delay(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors-Bm4iVQVp.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\nconst childPageWrapperSchema = z.strictObject({\n head: z\n .string()\n .min(1)\n .regex(/^[^\\r\\n]+$/, \"must be a single line\")\n .describe(\"Opening wrapper emitted before the child-page Markdown link.\"),\n tail: z\n .string()\n .min(1)\n .regex(/^[^\\r\\n]+$/, \"must be a single line\")\n .describe(\"Closing wrapper emitted after the child-page Markdown link.\"),\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 childPageWrapper: childPageWrapperSchema\n .optional()\n .describe(\n \"Optional wrapper around child-page Markdown links; overrides childPageMarker.\",\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":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generate.impl-zbCKiANA.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 childPageWrapper = fileConfig?.output?.childPageWrapper;\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 ...(childPageWrapper !== undefined ? { childPageWrapper } : {}),\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,mBAAmB,YAAY,QAAQ;EAC7C,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,qBAAqB,KAAA,IAAY,EAAE,iBAAiB,IAAI,CAAC;GAC7D,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"}
|