noblog 0.3.0-beta.4 → 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 +2 -2
- package/dist/{client-89Tyrzz2.js → client-CJ_0duRz.js} +18 -5
- package/dist/{client-89Tyrzz2.js.map → client-CJ_0duRz.js.map} +1 -1
- package/dist/{config-check.impl-DN5F6naR.js → config-check.impl-CkV2SIkF.js} +2 -2
- package/dist/{config-check.impl-DN5F6naR.js.map → config-check.impl-CkV2SIkF.js.map} +1 -1
- package/dist/{config-file-7YYV3n-J.js → config-file-Ti3lB3Et.js} +2 -2
- package/dist/{config-file-7YYV3n-J.js.map → config-file-Ti3lB3Et.js.map} +1 -1
- package/dist/{defaults-CjNHlVIB.js → defaults-DdsgY0uy.js} +2 -2
- package/dist/defaults-DdsgY0uy.js.map +1 -0
- package/dist/{errors-CiuisfUA.js → errors-v1sSdZ3l.js} +2 -2
- package/dist/errors-v1sSdZ3l.js.map +1 -0
- package/dist/{generate.impl-CETmE-DO.js → generate.impl-uVcUsW_1.js} +4 -4
- package/dist/{generate.impl-CETmE-DO.js.map → generate.impl-uVcUsW_1.js.map} +1 -1
- package/dist/generate.js +7 -7
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.js +4 -4
- package/package.json +15 -14
- package/schema/config.json +1 -1
- package/skills/noblog-config/references/config-schema.md +1 -1
- package/skills/noblog-config/references/theme-examples.md +1 -1
- package/dist/defaults-CjNHlVIB.js.map +0 -1
- package/dist/errors-CiuisfUA.js.map +0 -1
package/README.md
CHANGED
|
@@ -170,13 +170,13 @@ When configured, the wrapper overrides `output.childPageMarker`. Both strings mu
|
|
|
170
170
|
|
|
171
171
|
### Selfie page
|
|
172
172
|
|
|
173
|
-
`--selfie PAGE_TITLE` renders the database page with that exact title to `output.selfiePath` (default
|
|
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
174
|
|
|
175
175
|
```bash
|
|
176
176
|
noblog --selfie humbornjo
|
|
177
177
|
```
|
|
178
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
|
|
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
180
|
|
|
181
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.
|
|
182
182
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { t as getErrorCode } from "./errors-
|
|
2
|
-
import { i as DEFAULT_SELFIE_PATH, 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 = {
|
|
@@ -461,6 +461,19 @@ function upsertFrontmatterAuthor(content, author) {
|
|
|
461
461
|
...lines.slice(closing)
|
|
462
462
|
].join("\n");
|
|
463
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
|
+
}
|
|
464
477
|
//#endregion
|
|
465
478
|
//#region src/lib/noblog/writer.ts
|
|
466
479
|
async function writePages(pages, options = {}) {
|
|
@@ -590,6 +603,6 @@ function delay(milliseconds) {
|
|
|
590
603
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
591
604
|
}
|
|
592
605
|
//#endregion
|
|
593
|
-
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 };
|
|
594
607
|
|
|
595
|
-
//# sourceMappingURL=client-
|
|
608
|
+
//# sourceMappingURL=client-CJ_0duRz.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-89Tyrzz2.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 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","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":"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"}
|
|
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":"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"}
|
|
@@ -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,10 +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 = "
|
|
5
|
+
const DEFAULT_SELFIE_PATH = ".noblog/selfie.md";
|
|
6
6
|
/** U+2E0E EDITORIAL CORONIS, a rare marker for theme child-page links. */
|
|
7
7
|
const DEFAULT_CHILD_PAGE_MARKER = "⸎";
|
|
8
8
|
//#endregion
|
|
9
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 };
|
|
10
10
|
|
|
11
|
-
//# 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"}
|
|
@@ -48,7 +48,7 @@ const noblogConfigSchema = z.strictObject({
|
|
|
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
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:
|
|
51
|
+
selfiePath: z.string().min(1).optional().describe("Output file for the page named by --selfie (default: .noblog/selfie.md).")
|
|
52
52
|
}).optional(),
|
|
53
53
|
frontmatter: z.strictObject({ fields: frontmatterFieldsSchema.optional() }).optional()
|
|
54
54
|
}).superRefine((config, context) => {
|
|
@@ -76,4 +76,4 @@ function getErrorCode(error) {
|
|
|
76
76
|
//#endregion
|
|
77
77
|
export { noblogConfigSchema as a, frontmatterFieldsSchema as i, BUILT_IN_FRONTMATTER_SOURCES as n, notionPropertiesSchema as o, frontmatterFieldSchema as r, getErrorCode as t };
|
|
78
78
|
|
|
79
|
-
//# 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,7 +18,7 @@ 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 ?? "
|
|
21
|
+
selfiePath: fileConfig?.output?.selfiePath ?? ".noblog/selfie.md",
|
|
22
22
|
...childPageWrapper !== void 0 ? { childPageWrapper } : {},
|
|
23
23
|
...propertyMap !== void 0 ? { propertyMap } : {},
|
|
24
24
|
...frontmatter !== void 0 ? { frontmatter } : {},
|
|
@@ -35,4 +35,4 @@ async function generate(flags, savePath, legacySubPath) {
|
|
|
35
35
|
//#endregion
|
|
36
36
|
export { generate as default };
|
|
37
37
|
|
|
38
|
-
//# sourceMappingURL=generate.impl-
|
|
38
|
+
//# sourceMappingURL=generate.impl-uVcUsW_1.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.impl-
|
|
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,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as ConfigurationError } from "./config-csbquMhF.js";
|
|
3
|
-
import "./defaults-
|
|
4
|
-
import { a as findSelfiePage,
|
|
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";
|
|
5
5
|
import { config } from "dotenv";
|
|
6
6
|
import { ExitCode, buildApplication, buildCommand, buildRouteMap, help, run, version } from "@stricli/core";
|
|
7
7
|
const app = buildApplication(buildRouteMap({
|
|
8
8
|
routes: {
|
|
9
9
|
config: buildRouteMap({
|
|
10
10
|
routes: { check: buildCommand({
|
|
11
|
-
loader: async () => import("./config-check.impl-
|
|
11
|
+
loader: async () => import("./config-check.impl-CkV2SIkF.js"),
|
|
12
12
|
parameters: {
|
|
13
13
|
flags: { config: {
|
|
14
14
|
kind: "parsed",
|
|
@@ -26,7 +26,7 @@ const app = buildApplication(buildRouteMap({
|
|
|
26
26
|
docs: { brief: "Inspect Noblog configuration" }
|
|
27
27
|
}),
|
|
28
28
|
generate: buildCommand({
|
|
29
|
-
loader: async () => import("./generate.impl-
|
|
29
|
+
loader: async () => import("./generate.impl-uVcUsW_1.js"),
|
|
30
30
|
parameters: {
|
|
31
31
|
flags: {
|
|
32
32
|
verbose: {
|
|
@@ -106,7 +106,7 @@ const app = buildApplication(buildRouteMap({
|
|
|
106
106
|
}),
|
|
107
107
|
version: version({
|
|
108
108
|
brief: "Show version information",
|
|
109
|
-
info: { currentVersion: `noblog v0.3.0-beta.
|
|
109
|
+
info: { currentVersion: `noblog v0.3.0-beta.5` },
|
|
110
110
|
alias: false
|
|
111
111
|
})
|
|
112
112
|
});
|
|
@@ -180,11 +180,11 @@ async function generateBlog(config, options, output) {
|
|
|
180
180
|
});
|
|
181
181
|
const selfieTitle = options.selfie?.trim() ?? "";
|
|
182
182
|
if (selfieTitle !== "") {
|
|
183
|
-
const selfiePath = options.selfiePath ?? "
|
|
183
|
+
const selfiePath = options.selfiePath ?? ".noblog/selfie.md";
|
|
184
184
|
const match = findSelfiePage(posts, selfieTitle, options.propertyMap);
|
|
185
185
|
const rendered = pages.find((page) => page.id === match?.page.id);
|
|
186
186
|
if (match && rendered) {
|
|
187
|
-
await writeSelfie(upsertFrontmatterAuthor(rendered.content, match.title), selfiePath);
|
|
187
|
+
await writeSelfie(upsertFrontmatterAuthor(relativizeStoreLinks(rendered.content, selfiePath, options.storePath), match.title), selfiePath);
|
|
188
188
|
if (options.verbose) output.stderr.write(`Wrote selfie page "${match.title}" to ${selfiePath}\n`);
|
|
189
189
|
} else if (options.verbose) output.stderr.write(`Selfie page "${selfieTitle}" not found in the database; leaving ${selfiePath} untouched\n`);
|
|
190
190
|
}
|
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 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 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(rendered.content, match.title);\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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"}
|
|
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
|
@@ -93,7 +93,7 @@ type NoblogFileConfig = z.infer<typeof noblogConfigSchema>;
|
|
|
93
93
|
declare const DEFAULT_SAVE_PATH = "./src/pages/posts/";
|
|
94
94
|
declare const DEFAULT_STORE_PATH = ".noblog/pages/";
|
|
95
95
|
declare const DEFAULT_LAYOUT_PATH = "../../layouts/MarkdownPostLayout.astro";
|
|
96
|
-
declare const DEFAULT_SELFIE_PATH = "
|
|
96
|
+
declare const DEFAULT_SELFIE_PATH = ".noblog/selfie.md";
|
|
97
97
|
/** U+2E0E EDITORIAL CORONIS, a rare marker for theme child-page links. */
|
|
98
98
|
declare const DEFAULT_CHILD_PAGE_MARKER = "\u2E0E";
|
|
99
99
|
//#endregion
|
|
@@ -232,6 +232,13 @@ declare function findSelfiePage(posts: readonly PageObjectResponse[], title: str
|
|
|
232
232
|
* reflects the provided author; the document body is left untouched.
|
|
233
233
|
*/
|
|
234
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;
|
|
235
242
|
//#endregion
|
|
236
243
|
//#region src/lib/noblog/writer.d.ts
|
|
237
244
|
declare function writePages(pages: readonly RenderedPage[], options?: WritePagesOptions): Promise<string[]>;
|
|
@@ -248,5 +255,5 @@ interface NotionSource extends NotionReader {
|
|
|
248
255
|
}
|
|
249
256
|
declare function createNotionSource(config: NotionSourceConfig): NotionSource;
|
|
250
257
|
//#endregion
|
|
251
|
-
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, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, upsertFrontmatterAuthor, writePages, writeSelfie };
|
|
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 };
|
|
252
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 { 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-
|
|
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_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, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, upsertFrontmatterAuthor, writePages, writeSelfie };
|
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noblog",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.5",
|
|
4
4
|
"description": "Generate static blog content from a Notion.so table.",
|
|
5
5
|
"author": "humbornjo",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,6 +37,19 @@
|
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=24"
|
|
39
39
|
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"dev": "tsx src/cli.ts",
|
|
42
|
+
"schema:generate": "tsx scripts/generate-config-schema.ts",
|
|
43
|
+
"build": "pnpm run schema:generate && tsdown",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest",
|
|
47
|
+
"test:built": "pnpm run build && node scripts/smoke.mjs",
|
|
48
|
+
"check": "pnpm run typecheck && pnpm run test && pnpm run test:built && publint",
|
|
49
|
+
"prepack": "pnpm run build",
|
|
50
|
+
"release": "pnpm run check && npm publish",
|
|
51
|
+
"release:beta": "pnpm run check && pnpm publish --tag beta --no-git-checks"
|
|
52
|
+
},
|
|
40
53
|
"dependencies": {
|
|
41
54
|
"@notionhq/client": "^5.1.0",
|
|
42
55
|
"@stricli/core": "^1.3.0",
|
|
@@ -52,17 +65,5 @@
|
|
|
52
65
|
"tsx": "^4.23.15",
|
|
53
66
|
"typescript": "^6.0.3",
|
|
54
67
|
"vitest": "^5.0.5"
|
|
55
|
-
},
|
|
56
|
-
"scripts": {
|
|
57
|
-
"dev": "tsx src/cli.ts",
|
|
58
|
-
"schema:generate": "tsx scripts/generate-config-schema.ts",
|
|
59
|
-
"build": "pnpm run schema:generate && tsdown",
|
|
60
|
-
"typecheck": "tsc --noEmit",
|
|
61
|
-
"test": "vitest run",
|
|
62
|
-
"test:watch": "vitest",
|
|
63
|
-
"test:built": "pnpm run build && node scripts/smoke.mjs",
|
|
64
|
-
"check": "pnpm run typecheck && pnpm run test && pnpm run test:built && publint",
|
|
65
|
-
"release": "pnpm run check && npm publish",
|
|
66
|
-
"release:beta": "pnpm run check && pnpm publish --tag beta --no-git-checks"
|
|
67
68
|
}
|
|
68
|
-
}
|
|
69
|
+
}
|
package/schema/config.json
CHANGED
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"additionalProperties": false
|
|
75
75
|
},
|
|
76
76
|
"selfiePath": {
|
|
77
|
-
"description": "Output file for the page named by --selfie (default:
|
|
77
|
+
"description": "Output file for the page named by --selfie (default: .noblog/selfie.md).",
|
|
78
78
|
"type": "string",
|
|
79
79
|
"minLength": 1
|
|
80
80
|
}
|
|
@@ -32,7 +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` |
|
|
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. |
|
|
36
36
|
|
|
37
37
|
Optional wrapper (overrides `childPageMarker`):
|
|
38
38
|
|
|
@@ -17,7 +17,7 @@ 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` with `author` set to the page title. Other builds, and builds with no matching title, keep the committed
|
|
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
21
|
|
|
22
22
|
## ThoughtLite
|
|
23
23
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"defaults-CjNHlVIB.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 = \"public/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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors-CiuisfUA.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: public/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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"}
|