noblog 0.2.3 → 0.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +277 -121
- package/dist/client-CkOIgSvW.js +549 -0
- package/dist/client-CkOIgSvW.js.map +1 -0
- package/dist/config-check.impl-BQvIrgNe.js +14 -0
- package/dist/config-check.impl-BQvIrgNe.js.map +1 -0
- package/dist/config-csbquMhF.js +21 -0
- package/dist/config-csbquMhF.js.map +1 -0
- package/dist/config-file-BF0G2iJq.js +27 -0
- package/dist/config-file-BF0G2iJq.js.map +1 -0
- package/dist/defaults-D9BbNYsd.js +8 -0
- package/dist/defaults-D9BbNYsd.js.map +1 -0
- package/dist/errors-DgJ15MsB.js +72 -0
- package/dist/errors-DgJ15MsB.js.map +1 -0
- package/dist/frontmatter-fields-BkXq38CO.js +71 -0
- package/dist/frontmatter-fields-BkXq38CO.js.map +1 -0
- package/dist/generate.d.ts +1 -0
- package/dist/generate.impl-BKu-Ix4a.js +33 -0
- package/dist/generate.impl-BKu-Ix4a.js.map +1 -0
- package/dist/generate.js +197 -940
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +209 -18
- package/dist/index.js +5 -0
- package/package.json +32 -17
- package/schema/config.json +173 -0
- package/skills/noblog-config/SKILL.md +40 -0
- package/skills/noblog-config/assets/nobloger.yaml +30 -0
- package/skills/noblog-config/assets/thought-lite.yaml +47 -0
- package/skills/noblog-config/references/config-schema.md +108 -0
- package/skills/noblog-config/references/notion-property-types.md +42 -0
- package/skills/noblog-config/references/theme-examples.md +39 -0
- package/dist/cmds/generate.d.ts +0 -3
- package/dist/cmds/generate.d.ts.map +0 -1
- package/dist/generate.cjs +0 -970
- package/dist/generate.cjs.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/lib/noblog/format.d.ts +0 -24
- package/dist/lib/noblog/format.d.ts.map +0 -1
- package/dist/lib/noblog/noblog.d.ts +0 -31
- package/dist/lib/noblog/noblog.d.ts.map +0 -1
- package/dist/lib/noblog/utils.d.ts +0 -36
- package/dist/lib/noblog/utils.d.ts.map +0 -1
- package/dist/lib/notion/client.d.ts +0 -8
- package/dist/lib/notion/client.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,208 +1,364 @@
|
|
|
1
1
|
# 📝 Noblog
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Generate Astro-compatible Markdown from your Notion database.
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/js/noblog)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
Noblog
|
|
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
|
-
|
|
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
|
-
|
|
12
|
+
## Requirements
|
|
13
13
|
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
20
|
-
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g noblog
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
|
|
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
|
-
|
|
26
|
+
```bash
|
|
27
|
+
npm install noblog
|
|
28
|
+
```
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
## Configuration
|
|
34
31
|
|
|
35
|
-
|
|
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
|
-
|
|
34
|
+
```bash
|
|
35
|
+
NOTION_API_SECRET="your-notion-api-secret"
|
|
36
|
+
NOBLOG_DATABASE_ID="your-notion-database-id"
|
|
37
|
+
```
|
|
43
38
|
|
|
44
|
-
|
|
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
|
-
|
|
47
|
-
# With Noblog - everything handled automatically
|
|
48
|
-
noblog --layout ../../layouts/BlogLayout.astro
|
|
41
|
+
## Usage
|
|
49
42
|
|
|
50
|
-
|
|
43
|
+
```text
|
|
44
|
+
noblog [generate] [options] [SAVE_PATH] [SUB_PATH]
|
|
51
45
|
```
|
|
52
46
|
|
|
53
|
-
|
|
47
|
+
Running `noblog` without arguments immediately generates content with the default settings. It does not print help.
|
|
54
48
|
|
|
55
|
-
|
|
49
|
+
### Commands
|
|
56
50
|
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
npm install -g noblog
|
|
61
|
-
```
|
|
58
|
+
### Options
|
|
62
59
|
|
|
63
|
-
|
|
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
|
-
|
|
66
|
-
noblog
|
|
67
|
-
```
|
|
70
|
+
### Positional arguments
|
|
68
71
|
|
|
69
|
-
|
|
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
|
-
###
|
|
72
|
-
|
|
73
|
-
Noblog supports various command-line options to customize your blog generation:
|
|
77
|
+
### Examples
|
|
74
78
|
|
|
75
79
|
```bash
|
|
76
|
-
#
|
|
80
|
+
# Generate with all defaults
|
|
77
81
|
noblog
|
|
78
82
|
|
|
79
|
-
#
|
|
83
|
+
# Equivalent explicit command
|
|
84
|
+
noblog generate
|
|
85
|
+
|
|
86
|
+
# Use a custom layout
|
|
80
87
|
noblog --layout ../../layouts/BlogLayout.astro
|
|
81
88
|
|
|
82
|
-
#
|
|
83
|
-
noblog ./content/blog/
|
|
89
|
+
# Write database-post symlinks to ./content/blog/
|
|
90
|
+
noblog ./content/blog/ --layout ../../layouts/CustomLayout.astro
|
|
84
91
|
|
|
85
|
-
#
|
|
86
|
-
noblog -
|
|
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
|
-
|
|
95
|
+
# Generate using YAML configuration
|
|
96
|
+
noblog --config ./noblog.yaml
|
|
92
97
|
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
101
|
+
# Print diagnostics
|
|
102
|
+
noblog generate -v
|
|
99
103
|
|
|
100
|
-
|
|
101
|
-
|
|
104
|
+
# Show help for the generate command and its arguments
|
|
105
|
+
noblog generate --help
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## YAML configuration
|
|
102
109
|
|
|
103
|
-
|
|
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
|
-
|
|
112
|
+
```text
|
|
113
|
+
built-in defaults < YAML configuration < explicit CLI flags and SAVE_PATH
|
|
114
|
+
```
|
|
106
115
|
|
|
107
|
-
|
|
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
|
+
frontmatter:
|
|
133
|
+
fields:
|
|
134
|
+
title:
|
|
135
|
+
from: title
|
|
136
|
+
required: true
|
|
137
|
+
timestamp:
|
|
138
|
+
from: publishedAt
|
|
139
|
+
as: date
|
|
140
|
+
required: true
|
|
141
|
+
series:
|
|
142
|
+
from: series
|
|
143
|
+
omitEmpty: true
|
|
144
|
+
sensitive:
|
|
145
|
+
from: sensitive
|
|
146
|
+
default: false
|
|
147
|
+
top:
|
|
148
|
+
from: top
|
|
149
|
+
default: 0
|
|
150
|
+
toc:
|
|
151
|
+
value: true
|
|
152
|
+
```
|
|
108
153
|
|
|
109
|
-
|
|
154
|
+
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.
|
|
110
155
|
|
|
111
|
-
|
|
156
|
+
Validate a file without Notion credentials, a Notion client, or network access:
|
|
112
157
|
|
|
113
158
|
```bash
|
|
114
|
-
|
|
115
|
-
export NOTION_API_SECRET="your-notion-api-secret"
|
|
159
|
+
noblog config check --config ./noblog.yaml
|
|
116
160
|
```
|
|
117
161
|
|
|
118
|
-
|
|
162
|
+
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
163
|
|
|
120
|
-
|
|
164
|
+
```yaml
|
|
165
|
+
# yaml-language-server: $schema=./node_modules/noblog/schema/config.json
|
|
166
|
+
```
|
|
121
167
|
|
|
122
|
-
|
|
168
|
+
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
169
|
|
|
124
|
-
|
|
170
|
+
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
171
|
|
|
126
|
-
##
|
|
172
|
+
## Output structure
|
|
127
173
|
|
|
128
|
-
|
|
174
|
+
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
175
|
|
|
130
|
-
```
|
|
176
|
+
```text
|
|
131
177
|
project-root/
|
|
178
|
+
├── .noblog/
|
|
179
|
+
│ └── pages/
|
|
180
|
+
│ ├── <database-page-id>.md # real file
|
|
181
|
+
│ └── <child-page-id>.md # real file
|
|
132
182
|
└── src/
|
|
133
183
|
└── pages/
|
|
134
184
|
└── posts/
|
|
135
|
-
|
|
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
|
|
185
|
+
└── <database-page-id>.md # relative symlink
|
|
141
186
|
```
|
|
142
187
|
|
|
143
|
-
|
|
188
|
+
Only database posts receive public views. Child pages live exclusively in the canonical store, and child links use `./<child-page-id>.md` so they resolve as siblings after Vite/Astro resolves symlinks to real paths. Child pages therefore stay out of `src/pages/posts/*.md` listings. Use `--store PATH` to change the canonical store independently of the public path.
|
|
144
189
|
|
|
145
|
-
|
|
190
|
+
**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
191
|
|
|
147
|
-
|
|
148
|
-
noblog <SAVE_PATH> <SUB_PATH>
|
|
192
|
+
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
193
|
|
|
150
|
-
|
|
151
|
-
noblog ./content/blog/ ./content/blog/subpages/
|
|
152
|
-
```
|
|
194
|
+
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
195
|
|
|
154
|
-
##
|
|
196
|
+
## Frontmatter
|
|
155
197
|
|
|
156
|
-
|
|
198
|
+
Without custom Notion or frontmatter mappings, generated files retain the legacy Astro-compatible frontmatter:
|
|
157
199
|
|
|
158
|
-
```
|
|
200
|
+
```yaml
|
|
159
201
|
---
|
|
160
|
-
layout: ../../layouts/MarkdownPostLayout.astro
|
|
202
|
+
layout: ../../src/layouts/MarkdownPostLayout.astro
|
|
161
203
|
title: "Your Blog Post Title"
|
|
162
|
-
tags: ["tag1", "tag2", "tag3"]
|
|
163
|
-
pubDate: 2024-01-24
|
|
164
|
-
archived: false
|
|
165
204
|
description: "Your post description here"
|
|
205
|
+
tags: ["tag1", "tag2", "tag3"]
|
|
206
|
+
pubDate: "2024-01-24"
|
|
207
|
+
archived: "false"
|
|
166
208
|
---
|
|
209
|
+
```
|
|
167
210
|
|
|
168
|
-
|
|
211
|
+
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.
|
|
212
|
+
|
|
213
|
+
Every custom field has exactly one of:
|
|
214
|
+
|
|
215
|
+
- `from`: read a built-in source or a key declared under `notion.properties`.
|
|
216
|
+
- `value`: emit a static native string, finite number, boolean, null, array, or nested object.
|
|
217
|
+
|
|
218
|
+
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.
|
|
219
|
+
|
|
220
|
+
Field behavior can be refined with:
|
|
221
|
+
|
|
222
|
+
| Directive | Behavior |
|
|
223
|
+
| --- | --- |
|
|
224
|
+
| `default` | Use this value when the source is empty |
|
|
225
|
+
| `required` | Fail generation if the value remains empty after defaults; errors include the field and page ID |
|
|
226
|
+
| `omitEmpty` | Omit an empty value after default/required processing |
|
|
227
|
+
| `as: string` | Convert a string, number, or boolean to a string; date-like strings remain quoted |
|
|
228
|
+
| `as: date` | Convert a valid date string or finite number to an unquoted YAML timestamp |
|
|
229
|
+
|
|
230
|
+
Empty means `undefined`, `null`, `""`, or `[]`; `false` and `0` are preserved. Processing order is source/static value, default, required check, omission, then conversion.
|
|
231
|
+
|
|
232
|
+
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.
|
|
233
|
+
|
|
234
|
+
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.
|
|
235
|
+
|
|
236
|
+
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.
|
|
237
|
+
|
|
238
|
+
## Programmatic API
|
|
239
|
+
|
|
240
|
+
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.
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import {
|
|
244
|
+
createNotionSource,
|
|
245
|
+
renderPages,
|
|
246
|
+
resolveStoreLayoutPath,
|
|
247
|
+
writePages,
|
|
248
|
+
} from "noblog";
|
|
249
|
+
|
|
250
|
+
const notionApiSecret = process.env["NOTION_API_SECRET"];
|
|
251
|
+
const databaseId = process.env["NOBLOG_DATABASE_ID"];
|
|
252
|
+
if (!notionApiSecret || !databaseId) {
|
|
253
|
+
throw new Error("Notion credentials are required");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const notion = createNotionSource({ notionApiSecret, databaseId });
|
|
257
|
+
|
|
258
|
+
const savePath = "./content/blog/";
|
|
259
|
+
const storePath = ".noblog/pages/";
|
|
260
|
+
const layoutPath = resolveStoreLayoutPath({
|
|
261
|
+
savePath,
|
|
262
|
+
storePath,
|
|
263
|
+
layoutPath: "../../layouts/MarkdownPostLayout.astro",
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const posts = await notion.listPages();
|
|
267
|
+
const pages = await renderPages(posts, notion, { layoutPath });
|
|
268
|
+
|
|
269
|
+
await writePages(pages, { savePath, storePath });
|
|
169
270
|
```
|
|
170
271
|
|
|
171
|
-
|
|
272
|
+
`renderPages` recursively collects child pages by default and returns complete Markdown documents with frontmatter. Set `recursive: false` to render only the supplied database pages. `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.
|
|
273
|
+
|
|
274
|
+
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.
|
|
275
|
+
|
|
276
|
+
The same declarative mapping is available programmatically, or you can provide a `FrontmatterAdapter` function for full control:
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
import { createFrontmatterAdapter, renderPages } from "noblog";
|
|
280
|
+
|
|
281
|
+
const frontmatter = createFrontmatterAdapter({
|
|
282
|
+
title: { from: "title", required: true },
|
|
283
|
+
timestamp: { from: "publishedAt", as: "date" },
|
|
284
|
+
draft: { from: "archived", default: false },
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const pages = await renderPages(posts, notion, {
|
|
288
|
+
layoutPath,
|
|
289
|
+
propertyMap: { publishedAt: "Published" },
|
|
290
|
+
frontmatter,
|
|
291
|
+
});
|
|
292
|
+
```
|
|
172
293
|
|
|
173
|
-
|
|
294
|
+
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
295
|
|
|
175
|
-
|
|
296
|
+
## Exit codes
|
|
176
297
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
298
|
+
| Code | Meaning |
|
|
299
|
+
| --- | --- |
|
|
300
|
+
| `0` | Help, version, successful validation, or successful generation |
|
|
301
|
+
| `1` | Generation or unexpected runtime failure |
|
|
302
|
+
| `2` | Invalid arguments, YAML, or configuration |
|
|
181
303
|
|
|
182
|
-
|
|
304
|
+
## Development
|
|
183
305
|
|
|
184
|
-
|
|
306
|
+
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
307
|
|
|
186
308
|
```bash
|
|
187
|
-
|
|
188
|
-
noblog --layout ../../layouts/BlogLayout.astro
|
|
309
|
+
pnpm install
|
|
189
310
|
|
|
190
|
-
#
|
|
191
|
-
|
|
192
|
-
|
|
311
|
+
# Run the TypeScript CLI directly
|
|
312
|
+
pnpm dev --help
|
|
313
|
+
|
|
314
|
+
# Run unit tests and type checking
|
|
315
|
+
pnpm test
|
|
316
|
+
pnpm typecheck
|
|
193
317
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
- If you change the save path to `./content/blog/`, you might need `--layout ../../layouts/CustomLayout.astro` to point to `./content/layouts/CustomLayout.astro`
|
|
318
|
+
# Regenerate the published JSON Schema (also runs during build)
|
|
319
|
+
pnpm schema:generate
|
|
197
320
|
|
|
198
|
-
|
|
321
|
+
# Build and smoke-test the actual dist artifacts
|
|
322
|
+
pnpm test:built
|
|
199
323
|
|
|
200
|
-
|
|
324
|
+
# Run the complete verification suite
|
|
325
|
+
pnpm check
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Project structure
|
|
329
|
+
|
|
330
|
+
```text
|
|
331
|
+
src/
|
|
332
|
+
├── cli.ts # Process entry
|
|
333
|
+
├── main.ts # Dotenv loading and dependency composition
|
|
334
|
+
├── app.ts # Routes, help/version, and exit handling
|
|
335
|
+
├── config-file.ts # YAML loading and validation
|
|
336
|
+
├── commands/
|
|
337
|
+
│ ├── config*.ts # Offline configuration validation
|
|
338
|
+
│ ├── generate.ts # Declarative arguments and documentation
|
|
339
|
+
│ └── generate.impl.ts # Lazy-loaded command action
|
|
340
|
+
├── workflows/
|
|
341
|
+
│ └── generate.ts # CLI-oriented generation orchestration
|
|
342
|
+
└── lib/
|
|
343
|
+
├── noblog/ # Reusable rendering and file-writing package
|
|
344
|
+
│ ├── config.ts # Runtime YAML schema and types
|
|
345
|
+
│ ├── metadata.ts # Notion property normalization
|
|
346
|
+
│ ├── frontmatter-fields.ts # Declarative frontmatter adapter
|
|
347
|
+
│ ├── pages.ts # Page collection, caching, and recursion
|
|
348
|
+
│ ├── blocks.ts # Notion block-tree loading and rendering
|
|
349
|
+
│ ├── rich-text.ts # Rich-text annotations and links
|
|
350
|
+
│ ├── markdown.ts # Markdown formatting primitives
|
|
351
|
+
│ ├── frontmatter.ts # Legacy/custom frontmatter serialization
|
|
352
|
+
│ └── writer.ts # Canonical store and database-post symlinks
|
|
353
|
+
└── notion/client.ts # Explicit Notion SDK adapter
|
|
354
|
+
schema/config.json # Generated JSON Schema
|
|
355
|
+
skills/noblog-config/ # Agent Skill, references, and examples
|
|
356
|
+
```
|
|
201
357
|
|
|
202
|
-
|
|
358
|
+
## Acknowledgments
|
|
203
359
|
|
|
204
|
-
- [Notion API](https://developers.notion.com/reference/intro)
|
|
205
|
-
- [
|
|
206
|
-
- [
|
|
207
|
-
- [
|
|
208
|
-
- [
|
|
360
|
+
- [Notion API](https://developers.notion.com/reference/intro) and [Notion SDK](https://github.com/makenotion/notion-sdk-js)
|
|
361
|
+
- [notion-to-md](https://github.com/souvikinator/notion-to-md)
|
|
362
|
+
- [notablog](https://github.com/dragonman225/notablog)
|
|
363
|
+
- [astro-notion-blog](https://github.com/otoyo/astro-notion-blog)
|
|
364
|
+
- [nobloger](https://github.com/humbornjo/nobloger), a starter Astro template
|