noblog 0.2.2 → 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 -932
- 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 -962
- 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/dist/generate.js
CHANGED
|
@@ -1,940 +1,205 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
const addTabSpace = (text, n = 0) => {
|
|
107
|
-
const tab = " ";
|
|
108
|
-
for (let i = 0; i < n; i++) {
|
|
109
|
-
if (text.includes("\n")) {
|
|
110
|
-
const multiLineText = text.split(/(?:^|\n)/).join(`\n${tab}`);
|
|
111
|
-
text = tab + multiLineText;
|
|
112
|
-
}
|
|
113
|
-
else
|
|
114
|
-
text = tab + text;
|
|
115
|
-
}
|
|
116
|
-
return text;
|
|
117
|
-
};
|
|
118
|
-
const divider = () => {
|
|
119
|
-
return "---";
|
|
120
|
-
};
|
|
121
|
-
const toggle = (summary, children) => {
|
|
122
|
-
if (!summary)
|
|
123
|
-
return children || "";
|
|
124
|
-
return `<details>
|
|
125
|
-
<summary>${summary}</summary>
|
|
126
|
-
${children || ""}
|
|
127
|
-
</details>\n\n`;
|
|
128
|
-
};
|
|
129
|
-
const table = (cells) => {
|
|
130
|
-
return markdownTable(cells);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const NOTION_API_SECRET = process.env.NOTION_API_SECRET ?? "";
|
|
134
|
-
const NOBLOG_DATABASE_ID = process.env.NOBLOG_DATABASE_ID ?? "";
|
|
135
|
-
const client = new Client({
|
|
136
|
-
auth: NOTION_API_SECRET,
|
|
2
|
+
import { t as ConfigurationError } from "./config-csbquMhF.js";
|
|
3
|
+
import { a as renderPages, n as resolveStoreLayoutPath, r as writePages, t as createNotionSource } from "./client-CkOIgSvW.js";
|
|
4
|
+
import { config } from "dotenv";
|
|
5
|
+
import { ExitCode, buildApplication, buildCommand, buildRouteMap, help, run, version } from "@stricli/core";
|
|
6
|
+
const app = buildApplication(buildRouteMap({
|
|
7
|
+
routes: {
|
|
8
|
+
config: buildRouteMap({
|
|
9
|
+
routes: { check: buildCommand({
|
|
10
|
+
loader: async () => import("./config-check.impl-BQvIrgNe.js"),
|
|
11
|
+
parameters: {
|
|
12
|
+
flags: { config: {
|
|
13
|
+
kind: "parsed",
|
|
14
|
+
brief: "YAML configuration file",
|
|
15
|
+
placeholder: "PATH",
|
|
16
|
+
parse: (input) => input
|
|
17
|
+
} },
|
|
18
|
+
positional: {
|
|
19
|
+
kind: "tuple",
|
|
20
|
+
parameters: []
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
docs: { brief: "Validate a YAML configuration without network access" }
|
|
24
|
+
}) },
|
|
25
|
+
docs: { brief: "Inspect Noblog configuration" }
|
|
26
|
+
}),
|
|
27
|
+
generate: buildCommand({
|
|
28
|
+
loader: async () => import("./generate.impl-BKu-Ix4a.js"),
|
|
29
|
+
parameters: {
|
|
30
|
+
flags: {
|
|
31
|
+
verbose: {
|
|
32
|
+
kind: "boolean",
|
|
33
|
+
brief: "Print diagnostic information",
|
|
34
|
+
default: false,
|
|
35
|
+
withNegated: false
|
|
36
|
+
},
|
|
37
|
+
config: {
|
|
38
|
+
kind: "parsed",
|
|
39
|
+
brief: "YAML configuration file",
|
|
40
|
+
placeholder: "PATH",
|
|
41
|
+
parse: (input) => input,
|
|
42
|
+
optional: true
|
|
43
|
+
},
|
|
44
|
+
layout: {
|
|
45
|
+
kind: "parsed",
|
|
46
|
+
brief: "Layout path relative to SAVE_PATH",
|
|
47
|
+
placeholder: "PATH",
|
|
48
|
+
parse: (input) => input,
|
|
49
|
+
optional: true
|
|
50
|
+
},
|
|
51
|
+
store: {
|
|
52
|
+
kind: "parsed",
|
|
53
|
+
brief: "Directory for canonical Markdown files",
|
|
54
|
+
placeholder: "PATH",
|
|
55
|
+
parse: (input) => input,
|
|
56
|
+
optional: true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
aliases: { v: "verbose" },
|
|
60
|
+
positional: {
|
|
61
|
+
kind: "tuple",
|
|
62
|
+
parameters: [{
|
|
63
|
+
brief: "Public view directory for database posts",
|
|
64
|
+
placeholder: "SAVE_PATH",
|
|
65
|
+
parse: (input) => input,
|
|
66
|
+
optional: true
|
|
67
|
+
}, {
|
|
68
|
+
brief: "Deprecated compatibility argument; ignored",
|
|
69
|
+
placeholder: "SUB_PATH",
|
|
70
|
+
parse: (input) => input,
|
|
71
|
+
optional: true
|
|
72
|
+
}]
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
docs: {
|
|
76
|
+
brief: "Generate blog content from Notion",
|
|
77
|
+
fullDescription: "Fetch published pages from Notion and write Astro-compatible Markdown files. Only database posts receive public symlinks; child pages live in the canonical store. The legacy SUB_PATH argument is accepted but ignored."
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
},
|
|
81
|
+
docs: {
|
|
82
|
+
brief: "Generate static blog content from Notion",
|
|
83
|
+
fullDescription: "Running noblog without a subcommand is equivalent to 'noblog generate'.\nThe legacy 'help' and 'version' commands are also supported.\n\nGeneration requires environment variables:\n NOTION_API_SECRET Your Notion API secret\n NOBLOG_DATABASE_ID Your Notion database ID"
|
|
84
|
+
}
|
|
85
|
+
}), {
|
|
86
|
+
name: "noblog",
|
|
87
|
+
scanner: { allowArgumentEscapeSequence: true },
|
|
88
|
+
determineExitCode: (error) => error instanceof ConfigurationError ? 2 : ExitCode.CommandRunError
|
|
89
|
+
}, {
|
|
90
|
+
help: help({
|
|
91
|
+
brief: "Show help information",
|
|
92
|
+
defaultForRouteMap: true,
|
|
93
|
+
formatting: {
|
|
94
|
+
caseStyle: "original",
|
|
95
|
+
useAliasInUsageLine: false,
|
|
96
|
+
onlyRequiredInUsageLine: false
|
|
97
|
+
}
|
|
98
|
+
}),
|
|
99
|
+
version: version({
|
|
100
|
+
brief: "Show version information",
|
|
101
|
+
info: { currentVersion: `noblog v0.3.0-beta.1` },
|
|
102
|
+
alias: false
|
|
103
|
+
})
|
|
137
104
|
});
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
catch (error) {
|
|
170
|
-
if (isNotionClientError(error)) {
|
|
171
|
-
bail(error);
|
|
172
|
-
}
|
|
173
|
-
throw error;
|
|
174
|
-
}
|
|
175
|
-
}, { retries: numberOfRetry });
|
|
176
|
-
results = results.concat(resp.results);
|
|
177
|
-
if (!resp.has_more) {
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
params.start_cursor = resp.next_cursor;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return results;
|
|
105
|
+
const PATH_FLAGS = /* @__PURE__ */ new Set([
|
|
106
|
+
"--config",
|
|
107
|
+
"--layout",
|
|
108
|
+
"--store"
|
|
109
|
+
]);
|
|
110
|
+
async function runCli(inputs, context) {
|
|
111
|
+
const escapeIndex = inputs.indexOf("--");
|
|
112
|
+
const commandInputs = escapeIndex === -1 ? inputs : inputs.slice(0, escapeIndex);
|
|
113
|
+
const escapedInputs = escapeIndex === -1 ? [] : inputs.slice(escapeIndex);
|
|
114
|
+
let normalizedInputs;
|
|
115
|
+
if (hasLegacyControl(commandInputs, "help")) normalizedInputs = ["--help"];
|
|
116
|
+
else if (hasLegacyControl(commandInputs, "version") || hasLegacyControl(commandInputs, "--version")) normalizedInputs = ["--version"];
|
|
117
|
+
else {
|
|
118
|
+
const remainingInputs = [];
|
|
119
|
+
for (let index = 0; index < commandInputs.length; index++) {
|
|
120
|
+
const input = commandInputs[index];
|
|
121
|
+
if (input === void 0) continue;
|
|
122
|
+
if (input === "generate" && index !== 0) continue;
|
|
123
|
+
remainingInputs.push(input);
|
|
124
|
+
const flagValue = getFlagValue(commandInputs, index);
|
|
125
|
+
if (PATH_FLAGS.has(input) && flagValue !== void 0) {
|
|
126
|
+
remainingInputs.push(flagValue);
|
|
127
|
+
index++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
normalizedInputs = [...remainingInputs, ...escapedInputs];
|
|
131
|
+
if (normalizedInputs[0] !== "generate" && normalizedInputs[0] !== "config" && normalizedInputs[0] !== "-h" && normalizedInputs[0] !== "--help" && normalizedInputs[0] !== "--version") normalizedInputs = ["generate", ...normalizedInputs];
|
|
132
|
+
}
|
|
133
|
+
await run(app, normalizedInputs, context);
|
|
134
|
+
const exitCode = context.process.exitCode;
|
|
135
|
+
if (typeof exitCode === "number" && exitCode < 0) context.process.exitCode = exitCode === ExitCode.InvalidArgument || exitCode === ExitCode.UnknownCommand ? 2 : 1;
|
|
184
136
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
throw error;
|
|
197
|
-
}
|
|
198
|
-
}, { retries: numberOfRetry });
|
|
199
|
-
return result;
|
|
137
|
+
function hasLegacyControl(inputs, control) {
|
|
138
|
+
for (let index = 0; index < inputs.length; index++) {
|
|
139
|
+
const input = inputs[index];
|
|
140
|
+
if (input === void 0) continue;
|
|
141
|
+
if (PATH_FLAGS.has(input) && getFlagValue(inputs, index) !== void 0) {
|
|
142
|
+
index++;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (input === control) return true;
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
200
148
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
let start_cursor = undefined;
|
|
205
|
-
do {
|
|
206
|
-
const response = (await client.blocks.children.list({
|
|
207
|
-
start_cursor: start_cursor,
|
|
208
|
-
block_id: block_id,
|
|
209
|
-
}));
|
|
210
|
-
results.push(...response.results);
|
|
211
|
-
start_cursor = response?.next_cursor;
|
|
212
|
-
pageCount += 1;
|
|
213
|
-
} while (start_cursor != null &&
|
|
214
|
-
(totalPage == null || pageCount < totalPage));
|
|
215
|
-
ModifyNumberedListObject(results);
|
|
216
|
-
return results;
|
|
149
|
+
function getFlagValue(inputs, flagIndex) {
|
|
150
|
+
const value = inputs[flagIndex + 1];
|
|
151
|
+
return value !== void 0 && !value.startsWith("-") ? value : void 0;
|
|
217
152
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/workflows/generate.ts
|
|
155
|
+
async function generateBlog(config, options, output) {
|
|
156
|
+
const notion = createNotionSource(config);
|
|
157
|
+
const storeLayoutPath = resolveStoreLayoutPath(options);
|
|
158
|
+
if (options.verbose) output.stderr.write("Fetching posts from Notion...\n");
|
|
159
|
+
const posts = await notion.listPages();
|
|
160
|
+
if (options.verbose) output.stderr.write(`Found ${posts.length} pages\nSave path: ${options.savePath}\nStore path: ${options.storePath}\nLayout path: ${options.layoutPath}\n`);
|
|
161
|
+
await writePages(await renderPages(posts, notion, {
|
|
162
|
+
layoutPath: storeLayoutPath,
|
|
163
|
+
recursive: options.recursive,
|
|
164
|
+
...options.propertyMap !== void 0 ? { propertyMap: options.propertyMap } : {},
|
|
165
|
+
...options.frontmatter !== void 0 ? { frontmatter: options.frontmatter } : {}
|
|
166
|
+
}), {
|
|
167
|
+
savePath: options.savePath,
|
|
168
|
+
storePath: options.storePath
|
|
169
|
+
});
|
|
170
|
+
output.stdout.write("✅ Successfully generated blog content\n");
|
|
230
171
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const BLOCK_PARAGRAPH = "paragraph";
|
|
258
|
-
const LINEWRAP_NULL = "";
|
|
259
|
-
const LINEWRAP_NEWLINE = "\n";
|
|
260
|
-
|
|
261
|
-
async function SaveJelly(fname, dir, jelly, dry_run = false) {
|
|
262
|
-
if (jelly === undefined)
|
|
263
|
-
return `failed: ${fname} has empty jelly: `;
|
|
264
|
-
try {
|
|
265
|
-
if (dry_run) {
|
|
266
|
-
console.log(jelly.content);
|
|
267
|
-
return `success: ${fname} saved to ${dir}`;
|
|
268
|
-
}
|
|
269
|
-
await fs.mkdir(dir, { recursive: true });
|
|
270
|
-
const filePath = path.join(dir, fname);
|
|
271
|
-
await fs.writeFile(filePath, jelly.content, "utf-8");
|
|
272
|
-
return `success: ${fname} saved to ${filePath}`;
|
|
273
|
-
}
|
|
274
|
-
catch (error) {
|
|
275
|
-
if (error instanceof Error) {
|
|
276
|
-
return `failed: ${fname} - ${error.message}`;
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
return "failed: an unknown error occurred";
|
|
280
|
-
}
|
|
281
|
-
}
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/main.ts
|
|
174
|
+
const nodeProcess = {
|
|
175
|
+
env: process.env,
|
|
176
|
+
stdout: process.stdout,
|
|
177
|
+
stderr: process.stderr,
|
|
178
|
+
get exitCode() {
|
|
179
|
+
const exitCode = process.exitCode;
|
|
180
|
+
return typeof exitCode === "number" ? exitCode : null;
|
|
181
|
+
},
|
|
182
|
+
set exitCode(value) {
|
|
183
|
+
process.exitCode = value ?? void 0;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
async function main(inputs = process.argv.slice(2), context = {
|
|
187
|
+
process: nodeProcess,
|
|
188
|
+
generateBlog
|
|
189
|
+
}) {
|
|
190
|
+
const environment = context.process.env ?? {};
|
|
191
|
+
const processEnv = Object.fromEntries(Object.entries(environment).filter((entry) => entry[1] !== void 0));
|
|
192
|
+
config({
|
|
193
|
+
processEnv,
|
|
194
|
+
quiet: true
|
|
195
|
+
});
|
|
196
|
+
Object.assign(environment, processEnv);
|
|
197
|
+
await runCli(inputs, context);
|
|
282
198
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
LayoutPath = "../../layouts/MarkdownPostLayout.astro";
|
|
289
|
-
MdCollection = {};
|
|
290
|
-
CustomTransformers = {};
|
|
291
|
-
constructor(posts, save_path, child_path, layout_path) {
|
|
292
|
-
this.Posts = posts;
|
|
293
|
-
if (child_path != undefined) {
|
|
294
|
-
this.ChildPath = child_path;
|
|
295
|
-
}
|
|
296
|
-
if (save_path != undefined) {
|
|
297
|
-
this.SavePath = save_path;
|
|
298
|
-
}
|
|
299
|
-
if (layout_path != undefined) {
|
|
300
|
-
this.LayoutPath = layout_path;
|
|
301
|
-
}
|
|
302
|
-
this.CustomTransformers[BLOCK_CHILD_PAGE] = async (block) => {
|
|
303
|
-
const page = block;
|
|
304
|
-
if (!page?.id) {
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
|
-
// if (curr_page.id in this.Post) and (page.id in this.Post) ||
|
|
308
|
-
// (curr_page.id not in this.Post) and (page.id not in this.Post)
|
|
309
|
-
// * path should be "../pageid"
|
|
310
|
-
// if (curr_page.id in this.Post) and (page.id not in this.Post)
|
|
311
|
-
// * path should be "../save_path/pageid"
|
|
312
|
-
// if (curr_page.id not in this.Post) and (page.id in this.Post)
|
|
313
|
-
// * path should be "../../pageid"
|
|
314
|
-
let sub_path;
|
|
315
|
-
const parent_surface = this.Posts.filter((post) => post.id === this.CurrPage).length > 0;
|
|
316
|
-
const child_surface = this.Posts.filter((post) => post.id === page.id).length > 0;
|
|
317
|
-
if (parent_surface === child_surface) {
|
|
318
|
-
sub_path = "";
|
|
319
|
-
}
|
|
320
|
-
else if (parent_surface) {
|
|
321
|
-
sub_path = this.ChildPath;
|
|
322
|
-
}
|
|
323
|
-
else {
|
|
324
|
-
sub_path = "..";
|
|
325
|
-
}
|
|
326
|
-
const full_path = path.join("../", sub_path, page.id);
|
|
327
|
-
return `(」゚ロ゚)」 [${page.child_page.title}](${full_path})`;
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
// Collect converts all pages in database to markdown files
|
|
331
|
-
async Collect(recursive = true) {
|
|
332
|
-
const save_dir = this.SavePath;
|
|
333
|
-
const sub_dir = path.join(save_dir, this.ChildPath);
|
|
334
|
-
for (const page of this.Posts) {
|
|
335
|
-
await this.FromPageId(page.id, recursive);
|
|
336
|
-
}
|
|
337
|
-
try {
|
|
338
|
-
for (const page_id of Object.keys(this.MdCollection)) {
|
|
339
|
-
let fpath = sub_dir;
|
|
340
|
-
if (this.Posts.find((page) => page.id === page_id))
|
|
341
|
-
fpath = save_dir;
|
|
342
|
-
await SaveJelly(page_id + ".md", fpath, this.MdCollection[page_id]);
|
|
343
|
-
}
|
|
344
|
-
console.log(`success: finish dump all files to "${save_dir}"`);
|
|
345
|
-
}
|
|
346
|
-
catch (error) {
|
|
347
|
-
if (error instanceof Error) {
|
|
348
|
-
console.log(`failed: consider run verbose with v=true - ${error.message}`);
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
351
|
-
console.log("failed: an unknown error occurred");
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
// FromBlocks converts blocks fetched from notion api to
|
|
356
|
-
// rendered chunk (which is called jelly). Content in jelly
|
|
357
|
-
// will be collected to form the final markdown. If recursive
|
|
358
|
-
// is true, it will also fetch child pages exists in the given
|
|
359
|
-
// blocks.
|
|
360
|
-
async FromBlocks(blocks, recursive) {
|
|
361
|
-
const jelly = { content: "", children: [] };
|
|
362
|
-
for (const block of blocks) {
|
|
363
|
-
const subjelly = await this.FromBlocks(block.children, recursive);
|
|
364
|
-
let line_wrap = LINEWRAP_NULL;
|
|
365
|
-
if (block.type !== BLOCK_TO_DO &&
|
|
366
|
-
block.type !== BLOCK_BULLETED_LIST_ITEM &&
|
|
367
|
-
block.type !== BLOCK_NUMBERED_LIST_ITEM) {
|
|
368
|
-
line_wrap = LINEWRAP_NEWLINE;
|
|
369
|
-
}
|
|
370
|
-
switch (block.type) {
|
|
371
|
-
case BLOCK_TOGGLE:
|
|
372
|
-
jelly.content += toggle(block.parent, subjelly.content);
|
|
373
|
-
jelly.children = [...jelly.children, ...subjelly.children];
|
|
374
|
-
continue;
|
|
375
|
-
default:
|
|
376
|
-
jelly.content +=
|
|
377
|
-
line_wrap + `${addTabSpace(block.parent, 0)}\n` + line_wrap;
|
|
378
|
-
}
|
|
379
|
-
if (block.type === BLOCK_CHILD_PAGE) {
|
|
380
|
-
jelly.children = [...jelly.children, block.blockId];
|
|
381
|
-
if (recursive)
|
|
382
|
-
await this.FromPageId(block.blockId, recursive);
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
385
|
-
jelly.content += subjelly.content;
|
|
386
|
-
jelly.children = [...jelly.children, ...subjelly.children];
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
return jelly;
|
|
390
|
-
}
|
|
391
|
-
// FromPageid converts a page id to rendered chunk (which is
|
|
392
|
-
// called jelly). Content in jelly will be collected to form
|
|
393
|
-
// the final markdown. If recursive is true, it will also
|
|
394
|
-
// fetch child pages exists in the given page.
|
|
395
|
-
async FromPageId(page_id, recursive = true) {
|
|
396
|
-
// preserve the curr page info
|
|
397
|
-
const prev_page = this.CurrPage;
|
|
398
|
-
this.CurrPage = page_id;
|
|
399
|
-
if (this.MdCollection.hasOwnProperty(page_id)) {
|
|
400
|
-
return this.MdCollection[page_id];
|
|
401
|
-
}
|
|
402
|
-
const blocks = await this.PageToMarkdown(page_id);
|
|
403
|
-
const jelly = await this.FromBlocks(blocks, recursive);
|
|
404
|
-
const meta = await GetPage(page_id);
|
|
405
|
-
const astro_meta = await this.AssembleAstroFrontmatter(meta);
|
|
406
|
-
jelly.content = astro_meta + jelly.content;
|
|
407
|
-
this.MdCollection[page_id] = jelly;
|
|
408
|
-
// restore the prev page info
|
|
409
|
-
this.CurrPage = prev_page;
|
|
410
|
-
return jelly;
|
|
411
|
-
}
|
|
412
|
-
async PageToMarkdown(id, totalPage) {
|
|
413
|
-
if (!client) {
|
|
414
|
-
throw new Error("notion client is not provided");
|
|
415
|
-
}
|
|
416
|
-
const blocks = await GetBlockChildren(id, totalPage);
|
|
417
|
-
const parsedData = await this.BlocksToMarkdown(blocks);
|
|
418
|
-
return parsedData;
|
|
419
|
-
}
|
|
420
|
-
async BlocksToMarkdown(blocks, totalPage = null, mdBlocks = []) {
|
|
421
|
-
if (!client) {
|
|
422
|
-
throw new Error("notion client is not provided");
|
|
423
|
-
}
|
|
424
|
-
if (!blocks)
|
|
425
|
-
return mdBlocks;
|
|
426
|
-
for (const block of blocks) {
|
|
427
|
-
// @ts-ignore
|
|
428
|
-
if (block.type === BLOCK_UNSUPPORTED) {
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
431
|
-
if (!("has_children" in block) || !block.has_children) {
|
|
432
|
-
mdBlocks.push({
|
|
433
|
-
// @ts-ignore
|
|
434
|
-
type: block.type,
|
|
435
|
-
blockId: block.id,
|
|
436
|
-
children: [],
|
|
437
|
-
parent: await this.BlockToMarkdown(block),
|
|
438
|
-
});
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
const block_id = block.type == BLOCK_SYNCED_BLOCK &&
|
|
442
|
-
block.synced_block?.synced_from?.block_id
|
|
443
|
-
? block.synced_block.synced_from.block_id
|
|
444
|
-
: block.id;
|
|
445
|
-
// Get children of this block.
|
|
446
|
-
const child_blocks = await GetBlockChildren(block_id, totalPage);
|
|
447
|
-
// Push this block to mdBlocks.
|
|
448
|
-
mdBlocks.push({
|
|
449
|
-
type: block.type,
|
|
450
|
-
blockId: block.id,
|
|
451
|
-
parent: await this.BlockToMarkdown(block),
|
|
452
|
-
children: [],
|
|
453
|
-
});
|
|
454
|
-
if (block.type === BLOCK_CALLOUT) {
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
if (!(block.type in this.CustomTransformers)) {
|
|
458
|
-
continue;
|
|
459
|
-
}
|
|
460
|
-
// Recursively call BlocksToMarkdown to collect children. check for custom transformer before parsing child
|
|
461
|
-
if (mdBlocks !== undefined) {
|
|
462
|
-
await this.BlocksToMarkdown(child_blocks, totalPage, mdBlocks[mdBlocks.length - 1]?.children);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
return mdBlocks;
|
|
466
|
-
}
|
|
467
|
-
async BlockToMarkdown(block) {
|
|
468
|
-
if (typeof block !== "object" || !("type" in block))
|
|
469
|
-
return "";
|
|
470
|
-
let renderedData = "";
|
|
471
|
-
const { type } = block;
|
|
472
|
-
if (type in this.CustomTransformers && !!this.CustomTransformers[type]) {
|
|
473
|
-
const transformer = this.CustomTransformers[type];
|
|
474
|
-
const customTransformerValue = await transformer(block);
|
|
475
|
-
if (typeof customTransformerValue === "string")
|
|
476
|
-
return customTransformerValue;
|
|
477
|
-
}
|
|
478
|
-
switch (type) {
|
|
479
|
-
case BLOCK_IMAGE:
|
|
480
|
-
const blockContent = block.image;
|
|
481
|
-
let image_title = "image";
|
|
482
|
-
const image_caption_plain = blockContent.caption
|
|
483
|
-
.map((item) => item.plain_text)
|
|
484
|
-
.join("");
|
|
485
|
-
let link$1 = "";
|
|
486
|
-
const image_type = blockContent.type;
|
|
487
|
-
if (image_type === "external") {
|
|
488
|
-
link$1 = blockContent.external.url;
|
|
489
|
-
}
|
|
490
|
-
if (image_type === "file") {
|
|
491
|
-
link$1 = blockContent.file.url;
|
|
492
|
-
}
|
|
493
|
-
// image caption with high priority
|
|
494
|
-
if (image_caption_plain.trim().length > 0) {
|
|
495
|
-
image_title = image_caption_plain;
|
|
496
|
-
}
|
|
497
|
-
else if (image_type === "file" || image_type === "external") {
|
|
498
|
-
const matches = link$1.match(/[^\/\\&\?]+\.\w{3,4}(?=([\?&].*$|$))/);
|
|
499
|
-
image_title = matches ? matches[0] : image_title;
|
|
500
|
-
}
|
|
501
|
-
return await image(image_title, link$1, false);
|
|
502
|
-
case BLOCK_DIVIDER:
|
|
503
|
-
return divider();
|
|
504
|
-
case BLOCK_EQUATION:
|
|
505
|
-
return equation(block.equation.expression);
|
|
506
|
-
case BLOCK_PDF:
|
|
507
|
-
case BLOCK_FILE:
|
|
508
|
-
case BLOCK_VIDEO:
|
|
509
|
-
{
|
|
510
|
-
let blockContent;
|
|
511
|
-
let title = type;
|
|
512
|
-
if (type === BLOCK_PDF)
|
|
513
|
-
blockContent = block.pdf;
|
|
514
|
-
if (type === BLOCK_FILE)
|
|
515
|
-
blockContent = block.file;
|
|
516
|
-
if (type === BLOCK_VIDEO)
|
|
517
|
-
blockContent = block.video;
|
|
518
|
-
const caption = blockContent?.caption
|
|
519
|
-
.map((item) => item.plain_text)
|
|
520
|
-
.join("");
|
|
521
|
-
if (blockContent) {
|
|
522
|
-
const file_type = blockContent.type;
|
|
523
|
-
let link$1 = "";
|
|
524
|
-
if (file_type === "file")
|
|
525
|
-
link$1 = blockContent.file.url;
|
|
526
|
-
if (file_type === "external")
|
|
527
|
-
link$1 = blockContent.external.url;
|
|
528
|
-
if (caption && caption.trim().length > 0) {
|
|
529
|
-
title = caption;
|
|
530
|
-
}
|
|
531
|
-
else if (link$1) {
|
|
532
|
-
const matches = link$1.match(/[^\/\\&\?]+\.\w{3,4}(?=([\?&].*$|$))/);
|
|
533
|
-
title = matches ? matches[0] : type;
|
|
534
|
-
}
|
|
535
|
-
return link(title, link$1);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
break;
|
|
539
|
-
case BLOCK_EMBED:
|
|
540
|
-
case BLOCK_BOOKMARK:
|
|
541
|
-
case BLOCK_LINK_PREVIEW:
|
|
542
|
-
case BLOCK_LINK_TO_PAGE:
|
|
543
|
-
{
|
|
544
|
-
let blockContent;
|
|
545
|
-
const title = type;
|
|
546
|
-
if (type === BLOCK_EMBED)
|
|
547
|
-
blockContent = block.embed;
|
|
548
|
-
if (type === BLOCK_BOOKMARK)
|
|
549
|
-
blockContent = block.bookmark;
|
|
550
|
-
if (type === BLOCK_LINK_PREVIEW)
|
|
551
|
-
blockContent = block.link_preview;
|
|
552
|
-
if (type === BLOCK_LINK_TO_PAGE &&
|
|
553
|
-
block.link_to_page.type === "page_id") {
|
|
554
|
-
blockContent = {
|
|
555
|
-
url: `https://www.notion.so/${block.link_to_page.page_id}`,
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
if (blockContent)
|
|
559
|
-
return link(title, blockContent.url);
|
|
560
|
-
}
|
|
561
|
-
break;
|
|
562
|
-
case BLOCK_CHILD_PAGE: {
|
|
563
|
-
const pageTitle = block.child_page.title;
|
|
564
|
-
return heading2(pageTitle);
|
|
565
|
-
}
|
|
566
|
-
case BLOCK_CHILD_DATABASE: {
|
|
567
|
-
const pageTitle = block.child_database.title || `child_database`;
|
|
568
|
-
return heading2(pageTitle);
|
|
569
|
-
}
|
|
570
|
-
case BLOCK_TABLE: {
|
|
571
|
-
const { id, has_children } = block;
|
|
572
|
-
const tableArr = [];
|
|
573
|
-
if (has_children) {
|
|
574
|
-
const tableRows = await GetBlockChildren(id, 100);
|
|
575
|
-
const rowsPromise = tableRows?.map(async (row) => {
|
|
576
|
-
const { type } = row;
|
|
577
|
-
const cells = row[type]["cells"];
|
|
578
|
-
/**
|
|
579
|
-
* this is more like a hack since matching the type text was
|
|
580
|
-
* difficult. So converting each cell to paragraph type to
|
|
581
|
-
* reuse the BlockToMarkdown function
|
|
582
|
-
*/
|
|
583
|
-
const cellStringPromise = cells.map(async (cell) => await this.BlockToMarkdown({
|
|
584
|
-
type: BLOCK_PARAGRAPH,
|
|
585
|
-
paragraph: { rich_text: cell },
|
|
586
|
-
}));
|
|
587
|
-
const cellStringArr = await Promise.all(cellStringPromise);
|
|
588
|
-
tableArr.push(cellStringArr);
|
|
589
|
-
});
|
|
590
|
-
await Promise.all(rowsPromise || []);
|
|
591
|
-
}
|
|
592
|
-
return table(tableArr);
|
|
593
|
-
}
|
|
594
|
-
// Rest of the types:
|
|
595
|
-
// "paragraph" "heading_1" "heading_2" "heading_3"
|
|
596
|
-
// "bulleted_list_item" "numbered_list_item"
|
|
597
|
-
// "quote" "to_do" "template" "synced_block"
|
|
598
|
-
// "child_page" "child_database" "code" "callout" "breadcrumb"
|
|
599
|
-
// "table_of_contents" "link_to_page" "audio" "unsupported"
|
|
600
|
-
default: {
|
|
601
|
-
// @ts-ignore
|
|
602
|
-
const blockContent = block[type].text || block[type].rich_text || [];
|
|
603
|
-
const bucket = [];
|
|
604
|
-
const code_range = [];
|
|
605
|
-
const bold_range = [];
|
|
606
|
-
const italic_range = [];
|
|
607
|
-
const underline_range = [];
|
|
608
|
-
const strikethrough_range = [];
|
|
609
|
-
const push_or_merge = (arr, idx) => {
|
|
610
|
-
if (arr.length == 0) {
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
// @ts-ignore
|
|
614
|
-
if (arr[arr.length - 1][1] === idx) {
|
|
615
|
-
// @ts-ignore
|
|
616
|
-
arr[arr.length - 1][1] += 1;
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
arr.push([idx, idx + 1]);
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
for (let i = 0; i < blockContent.length; i++) {
|
|
623
|
-
const content = blockContent[i];
|
|
624
|
-
if (content.type === "equation") {
|
|
625
|
-
bucket.push(inlineEquation(content.equation.expression));
|
|
626
|
-
continue;
|
|
627
|
-
}
|
|
628
|
-
let plain_text = content.plain_text;
|
|
629
|
-
if (content["href"])
|
|
630
|
-
plain_text = link(plain_text, content["href"]);
|
|
631
|
-
bucket.push(plain_text);
|
|
632
|
-
const annotations = content.annotations;
|
|
633
|
-
if (annotations.code)
|
|
634
|
-
push_or_merge(code_range, i);
|
|
635
|
-
if (annotations.bold)
|
|
636
|
-
push_or_merge(bold_range, i);
|
|
637
|
-
if (annotations.italic)
|
|
638
|
-
push_or_merge(italic_range, i);
|
|
639
|
-
if (annotations.underline)
|
|
640
|
-
push_or_merge(underline_range, i);
|
|
641
|
-
if (annotations.strikethrough)
|
|
642
|
-
push_or_merge(strikethrough_range, i);
|
|
643
|
-
}
|
|
644
|
-
const annoed_bucket = [];
|
|
645
|
-
for (const range of code_range)
|
|
646
|
-
annoed_bucket.push({ range: range, type: "code" });
|
|
647
|
-
for (const range of bold_range)
|
|
648
|
-
annoed_bucket.push({ range: range, type: "bold" });
|
|
649
|
-
for (const range of italic_range)
|
|
650
|
-
annoed_bucket.push({ range: range, type: "italic" });
|
|
651
|
-
for (const range of underline_range)
|
|
652
|
-
annoed_bucket.push({ range: range, type: "underline" });
|
|
653
|
-
for (const range of strikethrough_range)
|
|
654
|
-
annoed_bucket.push({ range: range, type: "strikethrough" });
|
|
655
|
-
annoed_bucket.sort(
|
|
656
|
-
// @ts-ignore
|
|
657
|
-
(a, b) => a.range[1] - a.range[0] - (b.range[1] - b.range[0]));
|
|
658
|
-
for (const annoed of annoed_bucket) {
|
|
659
|
-
const type = annoed.type;
|
|
660
|
-
const [from, to] = annoed.range;
|
|
661
|
-
let panno, sanno = GetPrefixSuffix(type);
|
|
662
|
-
// @ts-ignore
|
|
663
|
-
bucket[from] = panno + bucket[from];
|
|
664
|
-
// @ts-ignore
|
|
665
|
-
bucket[to - 1] = bucket[to - 1] + sanno;
|
|
666
|
-
}
|
|
667
|
-
bucket.forEach((x) => (renderedData += x));
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
switch (type) {
|
|
671
|
-
case BLOCK_CODE:
|
|
672
|
-
renderedData = codeBlock(renderedData, block[type].language);
|
|
673
|
-
break;
|
|
674
|
-
case BLOCK_HEADING_1:
|
|
675
|
-
renderedData = heading1(renderedData);
|
|
676
|
-
break;
|
|
677
|
-
case BLOCK_HEADING_2:
|
|
678
|
-
renderedData = heading2(renderedData);
|
|
679
|
-
break;
|
|
680
|
-
case BLOCK_HEADING_3:
|
|
681
|
-
renderedData = heading3(renderedData);
|
|
682
|
-
break;
|
|
683
|
-
case BLOCK_QUOTE:
|
|
684
|
-
renderedData = quote(renderedData);
|
|
685
|
-
break;
|
|
686
|
-
case BLOCK_CALLOUT:
|
|
687
|
-
const { id, has_children } = block;
|
|
688
|
-
let callout_string = "";
|
|
689
|
-
if (!has_children) {
|
|
690
|
-
return callout(renderedData, block.callout.icon);
|
|
691
|
-
}
|
|
692
|
-
const callout_children_object = await GetBlockChildren(id, 100);
|
|
693
|
-
const callout_children = await this.BlocksToMarkdown(callout_children_object);
|
|
694
|
-
callout_string += `${renderedData}\n`;
|
|
695
|
-
callout_string += (await this.FromBlocks(callout_children, true))
|
|
696
|
-
.content;
|
|
697
|
-
renderedData = callout(callout_string.trim(), block.callout.icon);
|
|
698
|
-
break;
|
|
699
|
-
case BLOCK_BULLETED_LIST_ITEM:
|
|
700
|
-
renderedData = bullet(renderedData);
|
|
701
|
-
break;
|
|
702
|
-
case BLOCK_NUMBERED_LIST_ITEM:
|
|
703
|
-
renderedData = bullet(renderedData, block.numbered_list_item.number);
|
|
704
|
-
break;
|
|
705
|
-
case BLOCK_TO_DO:
|
|
706
|
-
renderedData = todo(renderedData, block.to_do.checked);
|
|
707
|
-
break;
|
|
708
|
-
}
|
|
709
|
-
return renderedData;
|
|
710
|
-
}
|
|
711
|
-
async AssembleAstroFrontmatter(page) {
|
|
712
|
-
let frontmatter = "";
|
|
713
|
-
// Default layout
|
|
714
|
-
const ischild = this.Posts.filter((post) => post.id === page.id).length > 0 ? "" : "../";
|
|
715
|
-
frontmatter += "layout: " + ischild + this.LayoutPath + "\n";
|
|
716
|
-
let title = "";
|
|
717
|
-
let description = "";
|
|
718
|
-
let tags = [];
|
|
719
|
-
let pubDate = "";
|
|
720
|
-
let archived = "";
|
|
721
|
-
Object.entries(page.properties).forEach(([key, property]) => {
|
|
722
|
-
switch (property.type) {
|
|
723
|
-
case "title": // Get title
|
|
724
|
-
title = property.title
|
|
725
|
-
?.map((title) => title.plain_text)
|
|
726
|
-
.reduce((a, b) => a + b, "");
|
|
727
|
-
break;
|
|
728
|
-
case "multi_select": // Get tags
|
|
729
|
-
tags = property.multi_select?.map((tag) => tag.name);
|
|
730
|
-
break;
|
|
731
|
-
case "date": // Get date
|
|
732
|
-
pubDate =
|
|
733
|
-
property.date?.start ?? new Date().toISOString().split("T")[0];
|
|
734
|
-
break;
|
|
735
|
-
default: // Customized properties from the template given in the Github repo
|
|
736
|
-
switch (key) {
|
|
737
|
-
case "archived": // Get archived
|
|
738
|
-
archived =
|
|
739
|
-
property.type === "checkbox" && property.checkbox
|
|
740
|
-
? "true"
|
|
741
|
-
: "false";
|
|
742
|
-
break;
|
|
743
|
-
case "description": // Get description
|
|
744
|
-
description =
|
|
745
|
-
property.type === "rich_text"
|
|
746
|
-
? property.rich_text
|
|
747
|
-
?.map((text) => text.plain_text)
|
|
748
|
-
.reduce((a, b) => a + b, "")
|
|
749
|
-
: "";
|
|
750
|
-
break;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
});
|
|
754
|
-
frontmatter += "title: " + JSON.stringify(title) + "\n";
|
|
755
|
-
frontmatter += "description: " + JSON.stringify(description) + "\n";
|
|
756
|
-
frontmatter += "tags: " + JSON.stringify(tags) + "\n";
|
|
757
|
-
frontmatter += "pubDate: " + JSON.stringify(pubDate) + "\n";
|
|
758
|
-
frontmatter += "archived: " + JSON.stringify(archived) + "\n";
|
|
759
|
-
return "---\n" + frontmatter + "---\n";
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
function getVersion() {
|
|
764
|
-
try {
|
|
765
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
766
|
-
const __dirname = dirname(__filename);
|
|
767
|
-
const packagePath = join(__dirname, "../../package.json");
|
|
768
|
-
const packageJson = JSON.parse(readFileSync(packagePath, "utf-8"));
|
|
769
|
-
return packageJson.version;
|
|
770
|
-
}
|
|
771
|
-
catch {
|
|
772
|
-
return "unknown";
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
function help() {
|
|
776
|
-
console.log(`
|
|
777
|
-
noblog - Generate static blog content from Notion.so
|
|
778
|
-
|
|
779
|
-
Usage: noblog [command] [options] [SAVE_PATH] [SUB_PATH]
|
|
780
|
-
|
|
781
|
-
Options:
|
|
782
|
-
-v, --verbose Print more messages for debugging
|
|
783
|
-
-h, --help Show help message
|
|
784
|
-
--version Show version information
|
|
785
|
-
--layout PATH Custom layout path (default: ../../layouts/MarkdownPostLayout.astro)
|
|
786
|
-
|
|
787
|
-
Commands:
|
|
788
|
-
help Show this help message
|
|
789
|
-
version Show version information
|
|
790
|
-
generate Generate blog content (default command)
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/cli.ts
|
|
201
|
+
await main();
|
|
202
|
+
//#endregion
|
|
203
|
+
export {};
|
|
791
204
|
|
|
792
|
-
|
|
793
|
-
-v, --verbose Print more messages for debugging
|
|
794
|
-
-h, --help Show help message
|
|
795
|
-
--version Show version information
|
|
796
|
-
|
|
797
|
-
Arguments:
|
|
798
|
-
SAVE_PATH Directory to save main posts (default: ./src/pages/posts/)
|
|
799
|
-
SUB_PATH Subdirectory for nested content (default: nob_children/)
|
|
800
|
-
|
|
801
|
-
Layout Options:
|
|
802
|
-
--layout PATH Custom layout path relative to save directory (default: ../../layouts/MarkdownPostLayout.astro)
|
|
803
|
-
|
|
804
|
-
Examples:
|
|
805
|
-
noblog help
|
|
806
|
-
noblog version
|
|
807
|
-
noblog generate
|
|
808
|
-
noblog -v
|
|
809
|
-
noblog src/pages/posts/ nob_children/
|
|
810
|
-
noblog --layout ../../layouts/BlogLayout.astro
|
|
811
|
-
noblog src/posts/ children/ --layout ../../../layouts/CustomLayout.astro -v
|
|
812
|
-
|
|
813
|
-
Environment Variables:
|
|
814
|
-
NOTION_API_SECRET Your Notion API secret
|
|
815
|
-
NOBLOG_DATABASE_ID Your Notion database ID
|
|
816
|
-
`);
|
|
817
|
-
}
|
|
818
|
-
function parseArgs(argv) {
|
|
819
|
-
const options = {
|
|
820
|
-
verbose: false,
|
|
821
|
-
help: false,
|
|
822
|
-
version: false,
|
|
823
|
-
savePath: "./src/pages/posts/",
|
|
824
|
-
subPath: "nob_children/",
|
|
825
|
-
layoutPath: "../../layouts/MarkdownPostLayout.astro",
|
|
826
|
-
};
|
|
827
|
-
const positionalArgs = [];
|
|
828
|
-
for (let i = 0; i < argv.length; i++) {
|
|
829
|
-
const arg = argv[i];
|
|
830
|
-
switch (arg) {
|
|
831
|
-
case "-v":
|
|
832
|
-
case "--verbose":
|
|
833
|
-
options.verbose = true;
|
|
834
|
-
break;
|
|
835
|
-
case "-h":
|
|
836
|
-
case "--help":
|
|
837
|
-
case "help":
|
|
838
|
-
options.help = true;
|
|
839
|
-
break;
|
|
840
|
-
case "--version":
|
|
841
|
-
case "version":
|
|
842
|
-
options.version = true;
|
|
843
|
-
break;
|
|
844
|
-
case "--layout":
|
|
845
|
-
if (i + 1 < argv.length && !argv[i + 1].startsWith("-")) {
|
|
846
|
-
options.layoutPath = argv[++i];
|
|
847
|
-
}
|
|
848
|
-
else {
|
|
849
|
-
console.error("Error: --layout requires a path argument");
|
|
850
|
-
help();
|
|
851
|
-
process.exit(1);
|
|
852
|
-
}
|
|
853
|
-
break;
|
|
854
|
-
case "generate":
|
|
855
|
-
// Explicit generate command, skip it
|
|
856
|
-
break;
|
|
857
|
-
default:
|
|
858
|
-
if (arg.startsWith("-")) {
|
|
859
|
-
console.error(`Unknown option: ${arg}`);
|
|
860
|
-
help();
|
|
861
|
-
process.exit(1);
|
|
862
|
-
}
|
|
863
|
-
positionalArgs.push(arg);
|
|
864
|
-
break;
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
// Handle positional arguments
|
|
868
|
-
if (positionalArgs.length > 0) {
|
|
869
|
-
options.savePath = positionalArgs[0];
|
|
870
|
-
}
|
|
871
|
-
if (positionalArgs.length > 1) {
|
|
872
|
-
options.subPath = positionalArgs[1];
|
|
873
|
-
}
|
|
874
|
-
if (positionalArgs.length > 2) {
|
|
875
|
-
console.error("Error: Too many arguments.");
|
|
876
|
-
help();
|
|
877
|
-
process.exit(1);
|
|
878
|
-
}
|
|
879
|
-
return options;
|
|
880
|
-
}
|
|
881
|
-
async function generateBlog(options) {
|
|
882
|
-
try {
|
|
883
|
-
if (options.verbose) {
|
|
884
|
-
console.log("Fetching posts from Notion...");
|
|
885
|
-
}
|
|
886
|
-
const pages = await ListPages();
|
|
887
|
-
if (options.verbose) {
|
|
888
|
-
console.log(`Found ${pages.length} pages`);
|
|
889
|
-
console.log(`Save path: ${options.savePath}`);
|
|
890
|
-
console.log(`Sub path: ${options.subPath}`);
|
|
891
|
-
console.log(`Layout path: ${options.layoutPath}`);
|
|
892
|
-
}
|
|
893
|
-
const nob = new Noblog(pages, options.savePath, options.subPath, options.layoutPath);
|
|
894
|
-
await nob.Collect(true);
|
|
895
|
-
console.log(`✅ Successfully generated blog content`);
|
|
896
|
-
}
|
|
897
|
-
catch (error) {
|
|
898
|
-
console.error(`❌ Error generating blog content:`);
|
|
899
|
-
if (error instanceof Error) {
|
|
900
|
-
console.error(` ${error.message}`);
|
|
901
|
-
if (options.verbose) {
|
|
902
|
-
console.error(error.stack);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
else {
|
|
906
|
-
console.error(` An unknown error occurred`);
|
|
907
|
-
}
|
|
908
|
-
process.exit(1);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
async function main() {
|
|
912
|
-
const argv = process.argv.slice(2);
|
|
913
|
-
if (argv.length === 0) {
|
|
914
|
-
// Default behavior: generate with default options
|
|
915
|
-
await generateBlog({
|
|
916
|
-
verbose: false,
|
|
917
|
-
help: false,
|
|
918
|
-
version: false,
|
|
919
|
-
savePath: "./src/pages/posts/",
|
|
920
|
-
subPath: "nob_children/",
|
|
921
|
-
layoutPath: "../../layouts/MarkdownPostLayout.astro",
|
|
922
|
-
});
|
|
923
|
-
return;
|
|
924
|
-
}
|
|
925
|
-
const options = parseArgs(argv);
|
|
926
|
-
if (options.help) {
|
|
927
|
-
help();
|
|
928
|
-
return;
|
|
929
|
-
}
|
|
930
|
-
if (options.version) {
|
|
931
|
-
console.log(`noblog v${getVersion()}`);
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
await generateBlog(options);
|
|
935
|
-
}
|
|
936
|
-
main().catch((error) => {
|
|
937
|
-
console.error(`❌ Unexpected error:`, error);
|
|
938
|
-
process.exit(1);
|
|
939
|
-
});
|
|
940
|
-
//# sourceMappingURL=generate.js.map
|
|
205
|
+
//# sourceMappingURL=generate.js.map
|