noblog 0.2.3 β†’ 0.3.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +280 -121
  2. package/dist/client-CzKR81Or.js +551 -0
  3. package/dist/client-CzKR81Or.js.map +1 -0
  4. package/dist/config-check.impl-C5FM7uWi.js +14 -0
  5. package/dist/config-check.impl-C5FM7uWi.js.map +1 -0
  6. package/dist/config-csbquMhF.js +21 -0
  7. package/dist/config-csbquMhF.js.map +1 -0
  8. package/dist/config-file-C07nmm45.js +27 -0
  9. package/dist/config-file-C07nmm45.js.map +1 -0
  10. package/dist/defaults-RGOB9tfi.js +10 -0
  11. package/dist/defaults-RGOB9tfi.js.map +1 -0
  12. package/dist/errors-BCRhKbgG.js +73 -0
  13. package/dist/errors-BCRhKbgG.js.map +1 -0
  14. package/dist/frontmatter-fields-BkXq38CO.js +71 -0
  15. package/dist/frontmatter-fields-BkXq38CO.js.map +1 -0
  16. package/dist/generate.d.ts +1 -0
  17. package/dist/generate.impl-ChFrtwtC.js +34 -0
  18. package/dist/generate.impl-ChFrtwtC.js.map +1 -0
  19. package/dist/generate.js +198 -940
  20. package/dist/generate.js.map +1 -1
  21. package/dist/index.d.ts +213 -18
  22. package/dist/index.js +5 -0
  23. package/package.json +32 -17
  24. package/schema/config.json +178 -0
  25. package/skills/noblog-config/SKILL.md +40 -0
  26. package/skills/noblog-config/assets/nobloger.yaml +30 -0
  27. package/skills/noblog-config/assets/thought-lite.yaml +47 -0
  28. package/skills/noblog-config/references/config-schema.md +110 -0
  29. package/skills/noblog-config/references/notion-property-types.md +42 -0
  30. package/skills/noblog-config/references/theme-examples.md +41 -0
  31. package/dist/cmds/generate.d.ts +0 -3
  32. package/dist/cmds/generate.d.ts.map +0 -1
  33. package/dist/generate.cjs +0 -970
  34. package/dist/generate.cjs.map +0 -1
  35. package/dist/index.d.ts.map +0 -1
  36. package/dist/lib/noblog/format.d.ts +0 -24
  37. package/dist/lib/noblog/format.d.ts.map +0 -1
  38. package/dist/lib/noblog/noblog.d.ts +0 -31
  39. package/dist/lib/noblog/noblog.d.ts.map +0 -1
  40. package/dist/lib/noblog/utils.d.ts +0 -36
  41. package/dist/lib/noblog/utils.d.ts.map +0 -1
  42. package/dist/lib/notion/client.d.ts +0 -8
  43. package/dist/lib/notion/client.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,208 +1,367 @@
1
1
  # πŸ“ Noblog
2
2
 
3
- > Transform your Notion content into beautiful static blogs with Astro
3
+ > Generate Astro-compatible Markdown from your Notion database.
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/noblog.svg)](https://badge.fury.io/js/noblog)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- Noblog generates static content from Notion databases and provides a pluggable component/theme marketplace for bloggers. Inspired by [notablog](https://github.com/dragonman225/notablog) but more flexible thanks to the power of Astro.
8
+ Noblog fetches published Notion pages, converts their blocks to Markdown, and writes a complete blog structure with Astro frontmatter. Each page lives once in a canonical store. Database posts also receive public symlinks, while child pages are referenced directly in the store. Custom output, store, and layout paths are supported.
9
9
 
10
- ## πŸ†š Why Noblog vs notion-to-md?
10
+ The renderer includes logic derived from [notion-to-md](https://github.com/souvikinator/notion-to-md), with additional handling for nested formatting and blog generation.
11
11
 
12
- While Noblog uses [notion-to-md](https://github.com/souvikinator/notion-to-md) under the hood, it provides significant improvements for blog generation:
12
+ ## Requirements
13
13
 
14
- ### πŸ”§ **Enhanced Markdown Generation**
14
+ - Node.js 24 or newer
15
+ - A Notion integration with read access to your database
16
+ - A Notion database with `publish` and `date` properties
15
17
 
16
- **Problem with notion-to-md:**
17
- When dealing with complex formatting like nested styles (e.g., `{BLOCK1}` and `{BLOCK2}` both bold, but only `BLOCK1` also italic), notion-to-md generates malformed markdown:
18
+ ## Installation
18
19
 
19
- ```markdown
20
- _**BLOCK1**_**BLOCK2** # Broken: unmerged tags
20
+ ```bash
21
+ npm install -g noblog
21
22
  ```
22
23
 
23
- **Noblog's Solution:**
24
-
25
- - **Merged Tag Handling**: Properly merges nested formatting tags
26
- - **Clean Markdown Output**: Generates valid, renderer-friendly markdown
27
- - **Astro-Optimized**: Specifically designed for static site generation
28
-
29
- ### πŸ—οΈ **Complete Blog Structure**
24
+ For use as a library in another package:
30
25
 
31
- **notion-to-md:** Just converts pages to markdown
26
+ ```bash
27
+ npm install noblog
28
+ ```
32
29
 
33
- **Noblog:**
30
+ ## Configuration
34
31
 
35
- - βœ… Automatic file structure generation
36
- - βœ… Parent/child page organization
37
- - βœ… Astro-compatible frontmatter
38
- - βœ… Customizable save paths
39
- - βœ… Layout path customization
40
- - βœ… Theme marketplace ready
32
+ Set the following environment variables in your shell or in a `.env` file in the directory where you run `noblog`:
41
33
 
42
- ### ⚑ **One-Command Solution**
34
+ ```bash
35
+ NOTION_API_SECRET="your-notion-api-secret"
36
+ NOBLOG_DATABASE_ID="your-notion-database-id"
37
+ ```
43
38
 
44
- Instead of manually handling notion-to-md output and building your own file structure:
39
+ The CLI loads `.env` automatically. Credentials are validated only when generating content, so `--help`, `--version`, and `config check` do not require them. Keep credentials in environment variables or `.env`; YAML configuration has no credential fields.
45
40
 
46
- ```bash
47
- # With Noblog - everything handled automatically
48
- noblog --layout ../../layouts/BlogLayout.astro
41
+ ## Usage
49
42
 
50
- # With notion-to-md - you'd need to build everything yourself
43
+ ```text
44
+ noblog [generate] [options] [SAVE_PATH] [SUB_PATH]
51
45
  ```
52
46
 
53
- > **Note:** Noblog includes improvements to notion-to-md's core parsing logic, fixing edge cases like unmerged formatting tags that can break markdown renderers.
47
+ Running `noblog` without arguments immediately generates content with the default settings. It does not print help.
54
48
 
55
- ## πŸš€ Quick Start
49
+ ### Commands
56
50
 
57
- ### Installation
51
+ | Command | Description |
52
+ | --- | --- |
53
+ | `noblog` / `noblog generate` | Generate blog content |
54
+ | `noblog config check --config PATH` | Validate YAML without credentials or network access |
55
+ | `noblog help` | Show general help |
56
+ | `noblog version` | Show the installed version |
58
57
 
59
- ```bash
60
- npm install -g noblog
61
- ```
58
+ ### Options
62
59
 
63
- ### Basic Usage
60
+ | Option | Description |
61
+ | --- | --- |
62
+ | `-v, --verbose` | Print diagnostics and error stack traces |
63
+ | `--config PATH` | Load YAML configuration for generation |
64
+ | `--layout PATH` | Set the layout path relative to `SAVE_PATH` |
65
+ | `--store PATH` | Set the canonical Markdown store (default: `.noblog/pages/`) |
66
+ | `-h, --help` | Show general or command-specific help |
67
+ | `--version` | Show the installed version |
68
+ | `--` | Treat all remaining inputs as positional paths |
64
69
 
65
- ```bash
66
- noblog
67
- ```
70
+ ### Positional arguments
68
71
 
69
- That's it! Noblog will fetch your Notion content and generate static markdown files.
72
+ | Argument | Default | Description |
73
+ | --- | --- | --- |
74
+ | `SAVE_PATH` | `./src/pages/posts/` | Public view directory for database posts |
75
+ | `SUB_PATH` | β€” | Deprecated compatibility argument; ignored with a warning |
70
76
 
71
- ### πŸ› οΈ Command-Line Options
72
-
73
- Noblog supports various command-line options to customize your blog generation:
77
+ ### Examples
74
78
 
75
79
  ```bash
76
- # Basic usage with default settings
80
+ # Generate with all defaults
77
81
  noblog
78
82
 
79
- # Custom layout path
83
+ # Equivalent explicit command
84
+ noblog generate
85
+
86
+ # Use a custom layout
80
87
  noblog --layout ../../layouts/BlogLayout.astro
81
88
 
82
- # Custom save and sub paths with layout
83
- noblog ./content/blog/ ./content/blog/subpages/ --layout ../../../layouts/CustomLayout.astro
89
+ # Write database-post symlinks to ./content/blog/
90
+ noblog ./content/blog/ --layout ../../layouts/CustomLayout.astro
84
91
 
85
- # Verbose output for debugging
86
- noblog -v
87
- # or
88
- noblog --verbose
89
- ```
92
+ # Store the real Markdown files in a custom canonical store
93
+ noblog --store content/.noblog-pages
90
94
 
91
- **Available Options:**
95
+ # Generate using YAML configuration
96
+ noblog --config ./noblog.yaml
92
97
 
93
- - `--layout PATH` - Custom layout path relative to the posts directory (default: `../../layouts/MarkdownPostLayout.astro`)
94
- - `-v, --verbose` - Print more messages for debugging
95
- - `-h, --help` - Show help message
96
- - `--version` - Show version information
98
+ # Validate configuration without credentials or Notion access
99
+ noblog config check --config ./noblog.yaml
97
100
 
98
- **Positional Arguments:**
101
+ # Print diagnostics
102
+ noblog generate -v
99
103
 
100
- - `SAVE_PATH` - Directory to save main posts (default: `./src/pages/posts/`)
101
- - `SUB_PATH` - Subdirectory for nested content (default: `nob_children/`)
104
+ # Show help for the generate command and its arguments
105
+ noblog generate --help
106
+ ```
107
+
108
+ ## YAML configuration
102
109
 
103
- ## πŸ“‹ Setup Guide
110
+ Pass `--config PATH` to load a versioned YAML file. Configuration is explicit; Noblog does not search for a YAML file automatically. Generation resolves each output setting with this precedence:
104
111
 
105
- ### 1. Create Your Notion Database
112
+ ```text
113
+ built-in defaults < YAML configuration < explicit CLI flags and SAVE_PATH
114
+ ```
106
115
 
107
- Duplicate this [template database](https://www.notion.so/humbornjo/fa0faae85c504934a4a86cfa70302850?v=2abd1079ae134fbd8df2604765baa1df) and start writing your blog posts in Notion.
116
+ For example, this config adapts output and frontmatter to a theme with a native date, custom Notion properties, and typed boolean/number fields:
117
+
118
+ ```yaml
119
+ version: 1
120
+ notion:
121
+ properties:
122
+ title: Name
123
+ publishedAt: Published
124
+ series: Series
125
+ sensitive: Sensitive
126
+ top: Top
127
+ output:
128
+ savePath: src/pages/posts/
129
+ storePath: .noblog/pages/
130
+ layoutPath: ../../layouts/MarkdownPostLayout.astro
131
+ recursive: true
132
+ childPageMarker: "⸎"
133
+ frontmatter:
134
+ fields:
135
+ title:
136
+ from: title
137
+ required: true
138
+ timestamp:
139
+ from: publishedAt
140
+ as: date
141
+ required: true
142
+ series:
143
+ from: series
144
+ omitEmpty: true
145
+ sensitive:
146
+ from: sensitive
147
+ default: false
148
+ top:
149
+ from: top
150
+ default: 0
151
+ toc:
152
+ value: true
153
+ ```
108
154
 
109
- ### 2. Configure Environment Variables
155
+ `output.childPageMarker` sets the prefix on child-page links. It defaults to `⸎` (U+2E0E EDITORIAL CORONIS) and must be a non-empty string. Set it to a marker your theme recognizes.
110
156
 
111
- Set up your Notion API credentials:
157
+ 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.
158
+
159
+ Validate a file without Notion credentials, a Notion client, or network access:
112
160
 
113
161
  ```bash
114
- export NOBLOG_DATABASE_ID="your-notion-database-id"
115
- export NOTION_API_SECRET="your-notion-api-secret"
162
+ noblog config check --config ./noblog.yaml
116
163
  ```
117
164
 
118
- ### 3. Choose a Template
165
+ The machine-readable JSON Schema is generated from the runtime Zod schema and shipped as `schema/config.json` (package export: `noblog/config.schema.json`). For a local installation, enable editor validation with:
119
166
 
120
- Pick an Astro template that supports Noblog. Here's a starter template: **[nobloger](https://github.com/humbornjo/nobloger)**
167
+ ```yaml
168
+ # yaml-language-server: $schema=./node_modules/noblog/schema/config.json
169
+ ```
121
170
 
122
- ### 4. Deploy Your Site
171
+ The schema checks structure and native types. `config check` additionally enforces cross-field rules such as declared custom sources. Invalid YAML, unsupported versions, unknown keys, and invalid field definitions exit with status 2.
123
172
 
124
- Run `noblog` to generate your content, then deploy your Astro site to your preferred hosting platform.
173
+ A standard Agent Skill is included at [`skills/noblog-config/SKILL.md`](skills/noblog-config/SKILL.md), with detailed references and Nobloger/ThoughtLite examples. Copy that directory into your agent's skills location or point the agent at it. The skill inspects the theme's actual collection schema, frontmatter consumers, and routes before generating YAML; it does not need or request Notion credentials.
125
174
 
126
- ## πŸ“ File Structure
175
+ ## Output structure
127
176
 
128
- By default, Noblog organizes your content like this:
177
+ With the default paths, Noblog names files by Notion page ID. All real Markdown files are flat in `.noblog/pages/`; database posts also have relative symlinks in `SAVE_PATH`:
129
178
 
130
- ```
179
+ ```text
131
180
  project-root/
181
+ β”œβ”€β”€ .noblog/
182
+ β”‚ └── pages/
183
+ β”‚ β”œβ”€β”€ <database-page-id>.md # real file
184
+ β”‚ └── <child-page-id>.md # real file
132
185
  └── src/
133
186
  └── pages/
134
187
  └── posts/
135
- β”œβ”€β”€ blog-post-1.md # Direct database entries
136
- β”œβ”€β”€ blog-post-2.md
137
- └── nob_children/
138
- β”œβ”€β”€ sub-post-1.md # Referenced/child pages
139
- β”œβ”€β”€ sub-post-2.md
140
- └── sub-post-3.md
188
+ └── <database-page-id>.md # relative symlink
141
189
  ```
142
190
 
143
- ### Custom Save Paths
191
+ Only database posts receive public views. Child pages live exclusively in the canonical store, and child links are written as `<marker> [title](./<child-page-id>.md)`. The marker defaults to `⸎` (U+2E0E EDITORIAL CORONIS) and is configurable with `output.childPageMarker`; set it to match the marker your theme recognizes. Links resolve as siblings after Vite/Astro resolves symlinks to real paths, so child pages stay out of `src/pages/posts/*.md` listings. Use `--store PATH` to change the canonical store independently of the public path.
144
192
 
145
- You can customize the output directories:
193
+ **Routing migration:** Noblog no longer creates `/nob_children/` routes. Configure the theme to load and route child pages from `.noblog/pages/` (or the custom store). Existing nested view files are not deleted; remove or redirect them separately after updating the theme. Existing `SUB_PATH` arguments are ignored with a warning.
146
194
 
147
- ```bash
148
- noblog <SAVE_PATH> <SUB_PATH>
195
+ Generation is repeatable. Existing regular database-post view files with matching page IDs are replaced by links, and all canonical files are updated. Unrelated files are left unchanged; directories and other non-file outputs are not replaced.
149
196
 
150
- # Example:
151
- noblog ./content/blog/ ./content/blog/subpages/
152
- ```
197
+ The store and views must remain together when building or deploying. If generated content is committed, include both; otherwise regenerate before building. Creating the views requires filesystem symlink support. On Windows, this may require Developer Mode or elevated privileges.
153
198
 
154
- ## 🎯 Markdown Output
199
+ ## Frontmatter
155
200
 
156
- Generated markdown files include Astro-compatible frontmatter:
201
+ Without custom Notion or frontmatter mappings, generated files retain the legacy Astro-compatible frontmatter:
157
202
 
158
- ```markdown
203
+ ```yaml
159
204
  ---
160
- layout: ../../layouts/MarkdownPostLayout.astro
205
+ layout: ../../src/layouts/MarkdownPostLayout.astro
161
206
  title: "Your Blog Post Title"
162
- tags: ["tag1", "tag2", "tag3"]
163
- pubDate: 2024-01-24
164
- archived: false
165
207
  description: "Your post description here"
208
+ tags: ["tag1", "tag2", "tag3"]
209
+ pubDate: "2024-01-24"
210
+ archived: "false"
166
211
  ---
212
+ ```
213
+
214
+ When `frontmatter.fields` is present, it defines the complete output field set; legacy fields are not implicitly added. When only `notion.properties` is present, Noblog uses the declarative equivalent of the legacy fields (`layout`, `title`, `description`, `tags`, `pubDate`, and `archived`) with the configured property names.
215
+
216
+ Every custom field has exactly one of:
217
+
218
+ - `from`: read a built-in source or a key declared under `notion.properties`.
219
+ - `value`: emit a static native string, finite number, boolean, null, array, or nested object.
220
+
221
+ Built-in sources are `title`, `description`, `tags`, `publishedAt`, `archived`, `layoutPath`, `pageId`, and `isDatabasePage`. Without explicit property mappings, the conventional Notion names are `description`, `tags`, `date`, and `archived`; title comes from the last Notion title property.
222
+
223
+ Field behavior can be refined with:
224
+
225
+ | Directive | Behavior |
226
+ | --- | --- |
227
+ | `default` | Use this value when the source is empty |
228
+ | `required` | Fail generation if the value remains empty after defaults; errors include the field and page ID |
229
+ | `omitEmpty` | Omit an empty value after default/required processing |
230
+ | `as: string` | Convert a string, number, or boolean to a string; date-like strings remain quoted |
231
+ | `as: date` | Convert a valid date string or finite number to an unquoted YAML timestamp |
232
+
233
+ Empty means `undefined`, `null`, `""`, or `[]`; `false` and `0` are preserved. Processing order is source/static value, default, required check, omission, then conversion.
234
+
235
+ Notion values are normalized according to their actual property types. Text, select/status, dates, URLs, times, users, and unique IDs become strings; checkboxes and numeric properties remain native; multi-selects, people, relations, and files become string arrays. Formula results follow their own value type. Unsupported or missing properties become `undefined`, so defaults and omission rules apply. See the [property-type reference](skills/noblog-config/references/notion-property-types.md) for the complete table.
236
+
237
+ This makes native theme schemas possible without stringly-typed frontmatter: `as: string` can produce `pubDate: "2024-01-24"`, while `as: date` produces `timestamp: 2024-01-24`, and booleans/numbers remain unquoted unless conversion is requested.
238
+
239
+ Vite and Astro resolve Markdown symlinks to their real paths, so layout imports must be relative to the canonical store. The CLI interprets `--layout` relative to `SAVE_PATH`, then writes the equivalent store-relative path for every page.
240
+
241
+ ## Programmatic API
242
+
243
+ The package exposes a reusable renderer and filesystem writer. The optional Notion adapter is configured explicitly; importing `noblog` has no environment or network side effects.
244
+
245
+ ```ts
246
+ import {
247
+ createNotionSource,
248
+ renderPages,
249
+ resolveStoreLayoutPath,
250
+ writePages,
251
+ } from "noblog";
252
+
253
+ const notionApiSecret = process.env["NOTION_API_SECRET"];
254
+ const databaseId = process.env["NOBLOG_DATABASE_ID"];
255
+ if (!notionApiSecret || !databaseId) {
256
+ throw new Error("Notion credentials are required");
257
+ }
258
+
259
+ const notion = createNotionSource({ notionApiSecret, databaseId });
167
260
 
168
- # Your content starts here...
261
+ const savePath = "./content/blog/";
262
+ const storePath = ".noblog/pages/";
263
+ const layoutPath = resolveStoreLayoutPath({
264
+ savePath,
265
+ storePath,
266
+ layoutPath: "../../layouts/MarkdownPostLayout.astro",
267
+ });
268
+
269
+ const posts = await notion.listPages();
270
+ const pages = await renderPages(posts, notion, { layoutPath });
271
+
272
+ await writePages(pages, { savePath, storePath });
169
273
  ```
170
274
 
171
- ## πŸ”§ Requirements
275
+ `renderPages` recursively collects child pages by default and returns complete Markdown documents with frontmatter. Set `recursive: false` to render only the supplied database pages. Set `childPageMarker` in `RenderOptions` to override the default `⸎` (U+2E0E EDITORIAL CORONIS) prefix used for child-page links. `writePages` writes every canonical file but returns only the public database-post view paths. When rendering for a canonical store, use `resolveStoreLayoutPath` as above so layout imports remain valid after symlink resolution.
276
+
277
+ Use `renderPage` for a single page, or supply any implementation of the `NotionReader` interface instead of the built-in adapter. Lower-level helpers such as `renderRichText` and `renderFrontmatter` are exported as well. To customize a block, pass a `transformers` map in `RenderOptions`; return a string to override the default renderer or `undefined` to fall back to it. Each key narrows its handler's block payload to the corresponding Notion block type, so a `paragraph` transformer can access `block.paragraph` directly. The programmatic API does not load `.env`; load environment variables yourself or pass explicit configuration.
278
+
279
+ The same declarative mapping is available programmatically, or you can provide a `FrontmatterAdapter` function for full control:
280
+
281
+ ```ts
282
+ import { createFrontmatterAdapter, renderPages } from "noblog";
283
+
284
+ const frontmatter = createFrontmatterAdapter({
285
+ title: { from: "title", required: true },
286
+ timestamp: { from: "publishedAt", as: "date" },
287
+ draft: { from: "archived", default: false },
288
+ });
289
+
290
+ const pages = await renderPages(posts, notion, {
291
+ layoutPath,
292
+ propertyMap: { publishedAt: "Published" },
293
+ frontmatter,
294
+ });
295
+ ```
172
296
 
173
- **Any Astro template can be used with Noblog!** The only requirement is that your template includes a layout component.
297
+ An adapter receives `{ page, metadata, layoutPath, isDatabasePage }` and returns a plain object. It can use the raw Notion page or normalized metadata, and root `undefined` values are omitted. Values follow the exported `FrontmatterValue` contract: strings, numbers, booleans, `null`, `Date`, and readonly arrays/objects containing those values. Arrays, null, dates, and class instances are rejected as adapter roots.
174
298
 
175
- This means you can use:
299
+ ## Exit codes
176
300
 
177
- - βœ… Any existing Astro blog template
178
- - βœ… Your custom Astro theme
179
- - βœ… Popular Astro themes from the community
180
- - βœ… The starter template: **[nobloger](https://github.com/humbornjo/nobloger)**
301
+ | Code | Meaning |
302
+ | --- | --- |
303
+ | `0` | Help, version, successful validation, or successful generation |
304
+ | `1` | Generation or unexpected runtime failure |
305
+ | `2` | Invalid arguments, YAML, or configuration |
181
306
 
182
- ### Layout Customization
307
+ ## Development
183
308
 
184
- By default, Noblog uses `../../layouts/MarkdownPostLayout.astro` as the layout path. You can customize this using the `--layout` option:
309
+ Noblog uses [Stricli](https://bloomberg.github.io/stricli/) for typed command routing, [tsdown](https://tsdown.dev/) for CLI/library builds, and [Vitest](https://vitest.dev/) for tests.
185
310
 
186
311
  ```bash
187
- # Use a custom layout
188
- noblog --layout ../../layouts/BlogLayout.astro
312
+ pnpm install
189
313
 
190
- # Use with custom save paths
191
- noblog ./content/blog/ ./content/blog/subpages/ --layout ../../../layouts/CustomLayout.astro
192
- ```
314
+ # Run the TypeScript CLI directly
315
+ pnpm dev --help
316
+
317
+ # Run unit tests and type checking
318
+ pnpm test
319
+ pnpm typecheck
193
320
 
194
- **Important:** The layout path is relative to the posts directory where your markdown files are saved. For example:
195
- - If your save path is `./src/pages/posts/`, the default layout path `../../layouts/MarkdownPostLayout.astro` points to `./src/layouts/MarkdownPostLayout.astro`
196
- - If you change the save path to `./content/blog/`, you might need `--layout ../../layouts/CustomLayout.astro` to point to `./content/layouts/CustomLayout.astro`
321
+ # Regenerate the published JSON Schema (also runs during build)
322
+ pnpm schema:generate
197
323
 
198
- Adjust the layout path based on where your Astro layout files are located relative to your posts directory.
324
+ # Build and smoke-test the actual dist artifacts
325
+ pnpm test:built
199
326
 
200
- ## πŸ™ Acknowledgments
327
+ # Run the complete verification suite
328
+ pnpm check
329
+ ```
330
+
331
+ ### Project structure
332
+
333
+ ```text
334
+ src/
335
+ β”œβ”€β”€ cli.ts # Process entry
336
+ β”œβ”€β”€ main.ts # Dotenv loading and dependency composition
337
+ β”œβ”€β”€ app.ts # Routes, help/version, and exit handling
338
+ β”œβ”€β”€ config-file.ts # YAML loading and validation
339
+ β”œβ”€β”€ commands/
340
+ β”‚ β”œβ”€β”€ config*.ts # Offline configuration validation
341
+ β”‚ β”œβ”€β”€ generate.ts # Declarative arguments and documentation
342
+ β”‚ └── generate.impl.ts # Lazy-loaded command action
343
+ β”œβ”€β”€ workflows/
344
+ β”‚ └── generate.ts # CLI-oriented generation orchestration
345
+ └── lib/
346
+ β”œβ”€β”€ noblog/ # Reusable rendering and file-writing package
347
+ β”‚ β”œβ”€β”€ config.ts # Runtime YAML schema and types
348
+ β”‚ β”œβ”€β”€ metadata.ts # Notion property normalization
349
+ β”‚ β”œβ”€β”€ frontmatter-fields.ts # Declarative frontmatter adapter
350
+ β”‚ β”œβ”€β”€ pages.ts # Page collection, caching, and recursion
351
+ β”‚ β”œβ”€β”€ blocks.ts # Notion block-tree loading and rendering
352
+ β”‚ β”œβ”€β”€ rich-text.ts # Rich-text annotations and links
353
+ β”‚ β”œβ”€β”€ markdown.ts # Markdown formatting primitives
354
+ β”‚ β”œβ”€β”€ frontmatter.ts # Legacy/custom frontmatter serialization
355
+ β”‚ └── writer.ts # Canonical store and database-post symlinks
356
+ └── notion/client.ts # Explicit Notion SDK adapter
357
+ schema/config.json # Generated JSON Schema
358
+ skills/noblog-config/ # Agent Skill, references, and examples
359
+ ```
201
360
 
202
- Built upon the excellent work of these projects:
361
+ ## Acknowledgments
203
362
 
204
- - [Notion API](https://developers.notion.com/reference/intro) - Official Notion API documentation
205
- - [Notion SDK](https://developers.notion.com/docs/getting-started) - Notion's official SDK
206
- - [notion-to-md](https://github.com/souvikinator/notion-to-md) - Notion to Markdown converter
207
- - [notablog](https://github.com/dragonman225/notablog) - Original inspiration
208
- - [astro-notion-blog](https://github.com/otoyo/astro-notion-blog) - Astro + Notion integration
363
+ - [Notion API](https://developers.notion.com/reference/intro) and [Notion SDK](https://github.com/makenotion/notion-sdk-js)
364
+ - [notion-to-md](https://github.com/souvikinator/notion-to-md)
365
+ - [notablog](https://github.com/dragonman225/notablog)
366
+ - [astro-notion-blog](https://github.com/otoyo/astro-notion-blog)
367
+ - [nobloger](https://github.com/humbornjo/nobloger), a starter Astro template