noblog 0.0.9 → 0.0.11
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 +31 -21
- package/dist/cmds/generate.js +28 -0
- package/dist/cmds/generate.js.map +1 -0
- package/dist/generate.cjs +487 -41
- package/dist/generate.cjs.map +1 -1
- package/dist/generate.js +487 -41
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/nomd/format.d.ts.map +1 -1
- package/dist/lib/nomd/format.js +111 -0
- package/dist/lib/nomd/format.js.map +1 -0
- package/dist/lib/nomd/nomd.d.ts +9 -3
- package/dist/lib/nomd/nomd.d.ts.map +1 -1
- package/dist/lib/nomd/nomd.js +489 -0
- package/dist/lib/nomd/nomd.js.map +1 -0
- package/dist/lib/notion/client.d.ts +12 -3
- package/dist/lib/notion/client.d.ts.map +1 -1
- package/dist/lib/notion/client.js +98 -0
- package/dist/lib/notion/client.js.map +1 -0
- package/dist/lib/notion/object.d.ts.map +1 -1
- package/dist/lib/notion/object.js +2 -0
- package/dist/lib/notion/object.js.map +1 -0
- package/dist/lib/notion/request.js +2 -0
- package/dist/lib/notion/request.js.map +1 -0
- package/package.json +45 -37
package/dist/generate.js
CHANGED
|
@@ -1,13 +1,79 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as fs from 'fs/promises';
|
|
4
|
-
import
|
|
5
|
-
import '
|
|
6
|
-
import 'markdown-table';
|
|
4
|
+
import fetch from 'node-fetch';
|
|
5
|
+
import { markdownTable } from 'markdown-table';
|
|
7
6
|
import 'dotenv/config';
|
|
8
7
|
import retry from 'async-retry';
|
|
9
8
|
import { Client, APIResponseError } from '@notionhq/client';
|
|
10
9
|
|
|
10
|
+
const inlineEquation = (text) => {
|
|
11
|
+
return `$${text}$`;
|
|
12
|
+
};
|
|
13
|
+
const link = (text, href) => {
|
|
14
|
+
return `[${text}](${href})`;
|
|
15
|
+
};
|
|
16
|
+
const codeBlock = (text, language) => {
|
|
17
|
+
if (language === "plain text")
|
|
18
|
+
language = "text";
|
|
19
|
+
return `\`\`\`${language}
|
|
20
|
+
${text}
|
|
21
|
+
\`\`\``;
|
|
22
|
+
};
|
|
23
|
+
const equation = (text) => {
|
|
24
|
+
return `$$
|
|
25
|
+
${text}
|
|
26
|
+
$$`;
|
|
27
|
+
};
|
|
28
|
+
const heading1 = (text) => {
|
|
29
|
+
return `# ${text}`;
|
|
30
|
+
};
|
|
31
|
+
const heading2 = (text) => {
|
|
32
|
+
return `## ${text}`;
|
|
33
|
+
};
|
|
34
|
+
const heading3 = (text) => {
|
|
35
|
+
return `### ${text}`;
|
|
36
|
+
};
|
|
37
|
+
const quote = (text) => {
|
|
38
|
+
// the replace is done to handle multiple lines
|
|
39
|
+
return `> ${text.replace(/\n/g, " \n> ")}`;
|
|
40
|
+
};
|
|
41
|
+
const callout = (text, icon) => {
|
|
42
|
+
let emoji;
|
|
43
|
+
if (icon?.type === "emoji") {
|
|
44
|
+
emoji = icon.emoji;
|
|
45
|
+
}
|
|
46
|
+
// the replace is done to handle multiple lines
|
|
47
|
+
return `> ${emoji ? emoji + " " : ""}${text.replace(/\n/g, " \n> ")}`;
|
|
48
|
+
};
|
|
49
|
+
const bullet = (text, count) => {
|
|
50
|
+
let renderText = text.trim();
|
|
51
|
+
return count ? `${count}. ${renderText}` : `- ${renderText}`;
|
|
52
|
+
};
|
|
53
|
+
const todo = (text, checked) => {
|
|
54
|
+
return checked ? `- [x] ${text}` : `- [ ] ${text}`;
|
|
55
|
+
};
|
|
56
|
+
const image = async (alt, href, convertToBase64 = false) => {
|
|
57
|
+
// In case the user does not want to convert the images to Base64
|
|
58
|
+
// or the image is already base64
|
|
59
|
+
if (!convertToBase64 || href.startsWith("data:")) {
|
|
60
|
+
if (href.startsWith("data:")) {
|
|
61
|
+
// Extract base64 data, i.e. the string after 'data:mime/type;base64,'
|
|
62
|
+
const base64 = href.split(",").pop();
|
|
63
|
+
// This overrides incorrect data: string format to png
|
|
64
|
+
// so that browsers can correctly render the data
|
|
65
|
+
return ``;
|
|
66
|
+
}
|
|
67
|
+
return ``;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Otherwise, download the image and convert it to base64
|
|
71
|
+
const res = await fetch(href);
|
|
72
|
+
const buf = await res.arrayBuffer();
|
|
73
|
+
const base64 = Buffer.from(buf).toString("base64");
|
|
74
|
+
return ``;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
11
77
|
const addTabSpace = (text, n = 0) => {
|
|
12
78
|
const tab = " ";
|
|
13
79
|
for (let i = 0; i < n; i++) {
|
|
@@ -20,6 +86,9 @@ const addTabSpace = (text, n = 0) => {
|
|
|
20
86
|
}
|
|
21
87
|
return text;
|
|
22
88
|
};
|
|
89
|
+
const divider = () => {
|
|
90
|
+
return "---";
|
|
91
|
+
};
|
|
23
92
|
const toggle = (summary, children) => {
|
|
24
93
|
if (!summary)
|
|
25
94
|
return children || "";
|
|
@@ -28,9 +97,12 @@ const toggle = (summary, children) => {
|
|
|
28
97
|
${children || ""}
|
|
29
98
|
</details>\n\n`;
|
|
30
99
|
};
|
|
100
|
+
const table = (cells) => {
|
|
101
|
+
return markdownTable(cells);
|
|
102
|
+
};
|
|
31
103
|
|
|
32
|
-
const NOTION_API_SECRET = process.env.NOTION_API_SECRET ??
|
|
33
|
-
const NOBLOG_DATABASE_ID = process.env.NOBLOG_DATABASE_ID ??
|
|
104
|
+
const NOTION_API_SECRET = process.env.NOTION_API_SECRET ?? "";
|
|
105
|
+
const NOBLOG_DATABASE_ID = process.env.NOBLOG_DATABASE_ID ?? "";
|
|
34
106
|
const client = new Client({
|
|
35
107
|
auth: NOTION_API_SECRET,
|
|
36
108
|
});
|
|
@@ -39,15 +111,19 @@ async function GetAllPosts() {
|
|
|
39
111
|
const params = {
|
|
40
112
|
database_id: NOBLOG_DATABASE_ID,
|
|
41
113
|
filter: {
|
|
42
|
-
and: [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
114
|
+
and: [
|
|
115
|
+
{
|
|
116
|
+
property: "publish",
|
|
117
|
+
checkbox: { equals: true },
|
|
118
|
+
},
|
|
119
|
+
],
|
|
46
120
|
},
|
|
47
|
-
sorts: [
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
121
|
+
sorts: [
|
|
122
|
+
{
|
|
123
|
+
property: "date",
|
|
124
|
+
direction: "descending",
|
|
125
|
+
},
|
|
126
|
+
],
|
|
51
127
|
page_size: 100,
|
|
52
128
|
};
|
|
53
129
|
let results = [];
|
|
@@ -64,12 +140,12 @@ async function GetAllPosts() {
|
|
|
64
140
|
}
|
|
65
141
|
throw error;
|
|
66
142
|
}
|
|
67
|
-
}, { retries: numberOfRetry
|
|
143
|
+
}, { retries: numberOfRetry });
|
|
68
144
|
results = results.concat(res.results);
|
|
69
145
|
if (!res.has_more) {
|
|
70
146
|
break;
|
|
71
147
|
}
|
|
72
|
-
params[
|
|
148
|
+
params["start_cursor"] = res.next_cursor;
|
|
73
149
|
}
|
|
74
150
|
return results;
|
|
75
151
|
}
|
|
@@ -86,17 +162,48 @@ async function GetPageMeta(page_id) {
|
|
|
86
162
|
}
|
|
87
163
|
throw error;
|
|
88
164
|
}
|
|
89
|
-
}, { retries: numberOfRetry
|
|
165
|
+
}, { retries: numberOfRetry });
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
async function GetBlockChildren(block_id, totalPage) {
|
|
169
|
+
let result = [];
|
|
170
|
+
let pageCount = 0;
|
|
171
|
+
let start_cursor = undefined;
|
|
172
|
+
do {
|
|
173
|
+
const response = (await client.blocks.children.list({
|
|
174
|
+
start_cursor: start_cursor,
|
|
175
|
+
block_id: block_id,
|
|
176
|
+
}));
|
|
177
|
+
result.push(...response.results);
|
|
178
|
+
start_cursor = response?.next_cursor;
|
|
179
|
+
pageCount += 1;
|
|
180
|
+
} while (start_cursor != null &&
|
|
181
|
+
(totalPage == null || pageCount < totalPage));
|
|
182
|
+
ModifyNumberedListObject(result);
|
|
90
183
|
return result;
|
|
91
184
|
}
|
|
185
|
+
function ModifyNumberedListObject(blocks) {
|
|
186
|
+
let numberedListIndex = 0;
|
|
187
|
+
for (const block of blocks) {
|
|
188
|
+
if ("type" in block && block.type === "numbered_list_item") {
|
|
189
|
+
// add numbers
|
|
190
|
+
// @ts-ignore
|
|
191
|
+
block.numbered_list_item.number = ++numberedListIndex;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
numberedListIndex = 0;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
92
198
|
|
|
93
|
-
class Noblog
|
|
199
|
+
class Noblog {
|
|
94
200
|
Posts = [];
|
|
201
|
+
CurrPage = "";
|
|
95
202
|
SavePath = "./src/pages/posts/";
|
|
96
203
|
ChildPath = "nob_children/";
|
|
97
204
|
MdCollection = {};
|
|
205
|
+
CustomTransformers = {};
|
|
98
206
|
constructor(posts, save_path, child_path) {
|
|
99
|
-
super({ notionClient: client });
|
|
100
207
|
this.Posts = posts;
|
|
101
208
|
if (child_path != undefined) {
|
|
102
209
|
this.ChildPath = child_path;
|
|
@@ -104,29 +211,44 @@ class Noblog extends NotionToMarkdown {
|
|
|
104
211
|
if (save_path != undefined) {
|
|
105
212
|
this.SavePath = save_path;
|
|
106
213
|
}
|
|
107
|
-
this.
|
|
108
|
-
let path = this.ChildPath;
|
|
214
|
+
this.CustomTransformers["child_page"] = async (block) => {
|
|
109
215
|
const page = block;
|
|
110
216
|
if (!page?.id) {
|
|
111
217
|
return false;
|
|
112
218
|
}
|
|
113
|
-
|
|
114
|
-
|
|
219
|
+
let sub_path;
|
|
220
|
+
// if (curr_page.id in this.Post) and (page.id in this.Post) ||
|
|
221
|
+
// (curr_page.id not in this.Post) and (page.id not in this.Post)
|
|
222
|
+
// * path should be "../pageid"
|
|
223
|
+
// if (curr_page.id in this.Post) and (page.id not in this.Post)
|
|
224
|
+
// * path should be "../save_path/pageid"
|
|
225
|
+
// if (curr_page.id not in this.Post) and (page.id in this.Post)
|
|
226
|
+
// * path should be "../../pageid"
|
|
227
|
+
const parent_surface = this.Posts.filter((post) => post.id === this.CurrPage).length > 0;
|
|
228
|
+
const child_surface = this.Posts.filter((post) => post.id === page.id).length > 0;
|
|
229
|
+
if (parent_surface === child_surface) {
|
|
230
|
+
sub_path = "";
|
|
115
231
|
}
|
|
116
|
-
|
|
117
|
-
|
|
232
|
+
else if (parent_surface) {
|
|
233
|
+
sub_path = this.ChildPath;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
sub_path = "..";
|
|
237
|
+
}
|
|
238
|
+
const full_path = path.join("../", sub_path, page.id);
|
|
239
|
+
return `&[${page.child_page.title}](${full_path})`;
|
|
240
|
+
};
|
|
118
241
|
}
|
|
119
242
|
async Collect(recur = true) {
|
|
120
243
|
const save_dir = this.SavePath;
|
|
121
244
|
const sub_dir = path.join(save_dir, this.ChildPath);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
245
|
+
for (let page of this.Posts) {
|
|
246
|
+
await this.FromPageid(page.id, recur);
|
|
247
|
+
}
|
|
125
248
|
try {
|
|
126
|
-
await Promise.all(futs);
|
|
127
249
|
for (const pageid of Object.keys(this.MdCollection)) {
|
|
128
250
|
let fpath = sub_dir;
|
|
129
|
-
if (this.Posts.find(page => page.id === pageid))
|
|
251
|
+
if (this.Posts.find((page) => page.id === pageid))
|
|
130
252
|
fpath = save_dir;
|
|
131
253
|
await this.SaveJelly(pageid + ".md", fpath, this.MdCollection[pageid]);
|
|
132
254
|
}
|
|
@@ -137,7 +259,7 @@ class Noblog extends NotionToMarkdown {
|
|
|
137
259
|
console.log(`failed: consider run verbose with v=true - ${error.message}`);
|
|
138
260
|
}
|
|
139
261
|
else {
|
|
140
|
-
console.log(
|
|
262
|
+
console.log("failed: an unknown error occurred");
|
|
141
263
|
}
|
|
142
264
|
}
|
|
143
265
|
}
|
|
@@ -156,7 +278,8 @@ class Noblog extends NotionToMarkdown {
|
|
|
156
278
|
block.type !== "numbered_list_item") {
|
|
157
279
|
line_wrap = "\n";
|
|
158
280
|
}
|
|
159
|
-
jelly.content +=
|
|
281
|
+
jelly.content +=
|
|
282
|
+
line_wrap + `${addTabSpace(block.parent, 0)}\n` + line_wrap;
|
|
160
283
|
if (block.type === "child_page") {
|
|
161
284
|
jelly.children = [...jelly.children, block.blockId];
|
|
162
285
|
if (recursive)
|
|
@@ -170,24 +293,332 @@ class Noblog extends NotionToMarkdown {
|
|
|
170
293
|
return jelly;
|
|
171
294
|
}
|
|
172
295
|
async FromPageid(page_id, recursive = true) {
|
|
296
|
+
// preserve the curr page info
|
|
297
|
+
const prev_page = this.CurrPage;
|
|
298
|
+
this.CurrPage = page_id;
|
|
173
299
|
if (this.MdCollection.hasOwnProperty(page_id)) {
|
|
174
300
|
return this.MdCollection[page_id];
|
|
175
301
|
}
|
|
176
|
-
const blocks = await this.
|
|
302
|
+
const blocks = await this.PageToMarkdown(page_id);
|
|
177
303
|
const jelly = await this.FromBlocks(blocks, recursive);
|
|
178
304
|
const meta = await GetPageMeta(page_id);
|
|
179
305
|
const astro_meta = await this.AssembleAstroFrontmatter(meta);
|
|
180
306
|
jelly.content = astro_meta + jelly.content;
|
|
181
307
|
this.MdCollection[page_id] = jelly;
|
|
308
|
+
// restore the prev page info
|
|
309
|
+
this.CurrPage = prev_page;
|
|
182
310
|
return jelly;
|
|
183
311
|
}
|
|
312
|
+
async PageToMarkdown(id, totalPage) {
|
|
313
|
+
if (!client) {
|
|
314
|
+
throw new Error("notion client is not provided, for more details check out https://github.com/souvikinator/notion-to-md");
|
|
315
|
+
}
|
|
316
|
+
const blocks = await GetBlockChildren(id, totalPage);
|
|
317
|
+
const parsedData = await this.BlocksToMarkdown(blocks);
|
|
318
|
+
return parsedData;
|
|
319
|
+
}
|
|
320
|
+
async BlocksToMarkdown(blocks, totalPage, mdBlocks) {
|
|
321
|
+
if (!client) {
|
|
322
|
+
throw new Error("notion client is not provided, for more details check out https://github.com/souvikinator/notion-to-md");
|
|
323
|
+
}
|
|
324
|
+
if (!blocks)
|
|
325
|
+
return mdBlocks;
|
|
326
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
327
|
+
let block = blocks[i];
|
|
328
|
+
if (
|
|
329
|
+
// @ts-ignore
|
|
330
|
+
block.type === "unsupported" ||
|
|
331
|
+
// @ts-ignore
|
|
332
|
+
(block.type === "child_page" && !this.config.parseChildPages)) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if ("has_children" in block && block.has_children) {
|
|
336
|
+
const block_id = block.type == "synced_block" &&
|
|
337
|
+
block.synced_block?.synced_from?.block_id
|
|
338
|
+
? block.synced_block.synced_from.block_id
|
|
339
|
+
: block.id;
|
|
340
|
+
// Get children of this block.
|
|
341
|
+
let child_blocks = await GetBlockChildren(block_id, totalPage);
|
|
342
|
+
// Recursively call BlocksToMarkdown to get children of this block.
|
|
343
|
+
// check for custom transformer before parsing child
|
|
344
|
+
if (!(block.type in this.CustomTransformers) &&
|
|
345
|
+
!this.CustomTransformers[block.type]) {
|
|
346
|
+
let l = mdBlocks.length;
|
|
347
|
+
if (mdBlocks !== undefined) {
|
|
348
|
+
await this.BlocksToMarkdown(child_blocks, totalPage, mdBlocks[l - 1]?.children);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// Push this block to mdBlocks.
|
|
352
|
+
mdBlocks.push({
|
|
353
|
+
type: block.type,
|
|
354
|
+
blockId: block.id,
|
|
355
|
+
parent: await this.BlockToMarkdown(block),
|
|
356
|
+
children: [],
|
|
357
|
+
});
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
let tmp = await this.BlockToMarkdown(block);
|
|
361
|
+
mdBlocks.push({
|
|
362
|
+
// @ts-ignore
|
|
363
|
+
type: block.type,
|
|
364
|
+
blockId: block.id,
|
|
365
|
+
parent: tmp,
|
|
366
|
+
children: [],
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
return mdBlocks;
|
|
370
|
+
}
|
|
371
|
+
async BlockToMarkdown(block) {
|
|
372
|
+
if (typeof block !== "object" || !("type" in block))
|
|
373
|
+
return "";
|
|
374
|
+
let parsedData = "";
|
|
375
|
+
const { type } = block;
|
|
376
|
+
if (type in this.CustomTransformers && !!this.CustomTransformers[type]) {
|
|
377
|
+
const transformer = this.CustomTransformers[type];
|
|
378
|
+
const customTransformerValue = transformer(block);
|
|
379
|
+
if (typeof customTransformerValue === "string")
|
|
380
|
+
return customTransformerValue;
|
|
381
|
+
}
|
|
382
|
+
switch (type) {
|
|
383
|
+
case "image":
|
|
384
|
+
let blockContent = block.image;
|
|
385
|
+
let image_title = "image";
|
|
386
|
+
const image_caption_plain = blockContent.caption
|
|
387
|
+
.map((item) => item.plain_text)
|
|
388
|
+
.join("");
|
|
389
|
+
const image_type = blockContent.type;
|
|
390
|
+
let link$1 = "";
|
|
391
|
+
if (image_type === "external") {
|
|
392
|
+
link$1 = blockContent.external.url;
|
|
393
|
+
}
|
|
394
|
+
if (image_type === "file") {
|
|
395
|
+
link$1 = blockContent.file.url;
|
|
396
|
+
}
|
|
397
|
+
// image caption with high priority
|
|
398
|
+
if (image_caption_plain.trim().length > 0) {
|
|
399
|
+
image_title = image_caption_plain;
|
|
400
|
+
}
|
|
401
|
+
else if (image_type === "file" || image_type === "external") {
|
|
402
|
+
const matches = link$1.match(/[^\/\\&\?]+\.\w{3,4}(?=([\?&].*$|$))/);
|
|
403
|
+
image_title = matches ? matches[0] : image_title;
|
|
404
|
+
}
|
|
405
|
+
return await image(image_title, link$1, false);
|
|
406
|
+
case "divider":
|
|
407
|
+
return divider();
|
|
408
|
+
case "equation":
|
|
409
|
+
return equation(block.equation.expression);
|
|
410
|
+
case "video":
|
|
411
|
+
case "file":
|
|
412
|
+
case "pdf":
|
|
413
|
+
{
|
|
414
|
+
let blockContent;
|
|
415
|
+
let title = type;
|
|
416
|
+
if (type === "video")
|
|
417
|
+
blockContent = block.video;
|
|
418
|
+
if (type === "file")
|
|
419
|
+
blockContent = block.file;
|
|
420
|
+
if (type === "pdf")
|
|
421
|
+
blockContent = block.pdf;
|
|
422
|
+
const caption = blockContent?.caption
|
|
423
|
+
.map((item) => item.plain_text)
|
|
424
|
+
.join("");
|
|
425
|
+
if (blockContent) {
|
|
426
|
+
const file_type = blockContent.type;
|
|
427
|
+
let link$1 = "";
|
|
428
|
+
if (file_type === "external")
|
|
429
|
+
link$1 = blockContent.external.url;
|
|
430
|
+
if (file_type === "file")
|
|
431
|
+
link$1 = blockContent.file.url;
|
|
432
|
+
if (caption && caption.trim().length > 0) {
|
|
433
|
+
title = caption;
|
|
434
|
+
}
|
|
435
|
+
else if (link$1) {
|
|
436
|
+
const matches = link$1.match(/[^\/\\&\?]+\.\w{3,4}(?=([\?&].*$|$))/);
|
|
437
|
+
title = matches ? matches[0] : type;
|
|
438
|
+
}
|
|
439
|
+
return link(title, link$1);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
break;
|
|
443
|
+
case "bookmark":
|
|
444
|
+
case "embed":
|
|
445
|
+
case "link_preview":
|
|
446
|
+
case "link_to_page":
|
|
447
|
+
{
|
|
448
|
+
let blockContent;
|
|
449
|
+
let title = type;
|
|
450
|
+
if (type === "bookmark")
|
|
451
|
+
blockContent = block.bookmark;
|
|
452
|
+
if (type === "embed")
|
|
453
|
+
blockContent = block.embed;
|
|
454
|
+
if (type === "link_preview")
|
|
455
|
+
blockContent = block.link_preview;
|
|
456
|
+
if (type === "link_to_page" &&
|
|
457
|
+
block.link_to_page.type === "page_id") {
|
|
458
|
+
blockContent = {
|
|
459
|
+
url: `https://www.notion.so/${block.link_to_page.page_id}`,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
if (blockContent)
|
|
463
|
+
return link(title, blockContent.url);
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
case "child_page": {
|
|
467
|
+
let pageTitle = block.child_page.title;
|
|
468
|
+
return heading2(pageTitle);
|
|
469
|
+
}
|
|
470
|
+
case "child_database": {
|
|
471
|
+
let pageTitle = block.child_database.title || `child_database`;
|
|
472
|
+
return heading2(pageTitle);
|
|
473
|
+
}
|
|
474
|
+
case "table": {
|
|
475
|
+
const { id, has_children } = block;
|
|
476
|
+
let tableArr = [];
|
|
477
|
+
if (has_children) {
|
|
478
|
+
const tableRows = await GetBlockChildren(id, 100);
|
|
479
|
+
let rowsPromise = tableRows?.map(async (row) => {
|
|
480
|
+
const { type } = row;
|
|
481
|
+
const cells = row[type]["cells"];
|
|
482
|
+
/**
|
|
483
|
+
* this is more like a hack since matching the type text was
|
|
484
|
+
* difficult. So converting each cell to paragraph type to
|
|
485
|
+
* reuse the BlockToMarkdown function
|
|
486
|
+
*/
|
|
487
|
+
let cellStringPromise = cells.map(async (cell) => await this.BlockToMarkdown({
|
|
488
|
+
type: "paragraph",
|
|
489
|
+
paragraph: { rich_text: cell },
|
|
490
|
+
}));
|
|
491
|
+
const cellStringArr = await Promise.all(cellStringPromise);
|
|
492
|
+
tableArr.push(cellStringArr);
|
|
493
|
+
});
|
|
494
|
+
await Promise.all(rowsPromise || []);
|
|
495
|
+
}
|
|
496
|
+
return table(tableArr);
|
|
497
|
+
}
|
|
498
|
+
// Rest of the types:
|
|
499
|
+
// "paragraph" "heading_1" "heading_2" "heading_3"
|
|
500
|
+
// "bulleted_list_item" "numbered_list_item"
|
|
501
|
+
// "quote" "to_do" "template" "synced_block"
|
|
502
|
+
// "child_page" "child_database" "code" "callout" "breadcrumb"
|
|
503
|
+
// "table_of_contents" "link_to_page" "audio" "unsupported"
|
|
504
|
+
default: {
|
|
505
|
+
// @ts-ignore
|
|
506
|
+
let blockContent = block[type].text || block[type].rich_text || [];
|
|
507
|
+
let bucket = [];
|
|
508
|
+
let code_range = [];
|
|
509
|
+
let bold_range = [];
|
|
510
|
+
let italic_range = [];
|
|
511
|
+
let underline_range = [];
|
|
512
|
+
let strikethrough_range = [];
|
|
513
|
+
const push_or_merge = (arr, idx) => {
|
|
514
|
+
if (arr.length == 0) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
// @ts-ignore
|
|
518
|
+
if (idx === arr[arr.length - 1][1]) {
|
|
519
|
+
// @ts-ignore
|
|
520
|
+
arr[arr.length - 1][1] += 1;
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
arr.push([idx, idx + 1]);
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
for (let i = 0; i < blockContent.length; i++) {
|
|
527
|
+
let content = blockContent[i];
|
|
528
|
+
if (content.type === "equation") {
|
|
529
|
+
bucket.push(inlineEquation(content.equation.expression));
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
let plain_text = content.plain_text;
|
|
533
|
+
if (content["href"])
|
|
534
|
+
plain_text = link(plain_text, content["href"]);
|
|
535
|
+
bucket.push(plain_text);
|
|
536
|
+
const annotations = content.annotations;
|
|
537
|
+
if (annotations.code)
|
|
538
|
+
push_or_merge(code_range, i);
|
|
539
|
+
if (annotations.bold)
|
|
540
|
+
push_or_merge(bold_range, i);
|
|
541
|
+
if (annotations.italic)
|
|
542
|
+
push_or_merge(italic_range, i);
|
|
543
|
+
if (annotations.underline)
|
|
544
|
+
push_or_merge(underline_range, i);
|
|
545
|
+
if (annotations.strikethrough)
|
|
546
|
+
push_or_merge(strikethrough_range, i);
|
|
547
|
+
}
|
|
548
|
+
let annoed_bucket = [];
|
|
549
|
+
for (let range of code_range)
|
|
550
|
+
annoed_bucket.push({ range: range, type: "code" });
|
|
551
|
+
for (let range of bold_range)
|
|
552
|
+
annoed_bucket.push({ range: range, type: "bold" });
|
|
553
|
+
for (let range of italic_range)
|
|
554
|
+
annoed_bucket.push({ range: range, type: "italic" });
|
|
555
|
+
for (let range of underline_range)
|
|
556
|
+
annoed_bucket.push({ range: range, type: "underline" });
|
|
557
|
+
for (let range of strikethrough_range)
|
|
558
|
+
annoed_bucket.push({ range: range, type: "strikethrough" });
|
|
559
|
+
annoed_bucket.sort(
|
|
560
|
+
// @ts-ignore
|
|
561
|
+
(a, b) => a.range[1] - a.range[0] - (b.range[1] - b.range[0]));
|
|
562
|
+
for (let annoed of annoed_bucket) {
|
|
563
|
+
annoed.type;
|
|
564
|
+
let [from, to] = annoed.range;
|
|
565
|
+
// @ts-ignore
|
|
566
|
+
bucket[from] = annotation + bucket[from];
|
|
567
|
+
// @ts-ignore
|
|
568
|
+
bucket[to - 1] = bucket[to - 1] + annotation;
|
|
569
|
+
}
|
|
570
|
+
bucket.forEach((x) => (parsedData += x));
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
switch (type) {
|
|
574
|
+
case "code":
|
|
575
|
+
parsedData = codeBlock(parsedData, block[type].language);
|
|
576
|
+
break;
|
|
577
|
+
case "heading_1":
|
|
578
|
+
parsedData = heading1(parsedData);
|
|
579
|
+
break;
|
|
580
|
+
case "heading_2":
|
|
581
|
+
parsedData = heading2(parsedData);
|
|
582
|
+
break;
|
|
583
|
+
case "heading_3":
|
|
584
|
+
parsedData = heading3(parsedData);
|
|
585
|
+
break;
|
|
586
|
+
case "quote":
|
|
587
|
+
parsedData = quote(parsedData);
|
|
588
|
+
break;
|
|
589
|
+
case "callout":
|
|
590
|
+
const { id, has_children } = block;
|
|
591
|
+
let callout_string = "";
|
|
592
|
+
if (!has_children) {
|
|
593
|
+
return callout(parsedData, block[type].icon);
|
|
594
|
+
}
|
|
595
|
+
const callout_children_object = await GetBlockChildren(id, 100);
|
|
596
|
+
const callout_children = await this.BlocksToMarkdown(callout_children_object);
|
|
597
|
+
callout_string += `${parsedData}\n`;
|
|
598
|
+
callout_children.map((child) => {
|
|
599
|
+
callout_string += `${child.parent}\n\n`;
|
|
600
|
+
});
|
|
601
|
+
parsedData = callout(callout_string.trim(), block[type].icon);
|
|
602
|
+
break;
|
|
603
|
+
case "bulleted_list_item":
|
|
604
|
+
parsedData = bullet(parsedData);
|
|
605
|
+
break;
|
|
606
|
+
case "numbered_list_item":
|
|
607
|
+
parsedData = bullet(parsedData, block.numbered_list_item.number);
|
|
608
|
+
break;
|
|
609
|
+
case "to_do":
|
|
610
|
+
parsedData = todo(parsedData, block.to_do.checked);
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
return parsedData;
|
|
614
|
+
}
|
|
184
615
|
async SaveJelly(fname, dir, jelly) {
|
|
185
616
|
if (jelly === undefined)
|
|
186
617
|
return `failed: ${fname} has empty jelly: `;
|
|
187
618
|
try {
|
|
188
619
|
await fs.mkdir(dir, { recursive: true });
|
|
189
620
|
const filePath = path.join(dir, fname);
|
|
190
|
-
await fs.writeFile(filePath, jelly.content,
|
|
621
|
+
await fs.writeFile(filePath, jelly.content, "utf-8");
|
|
191
622
|
return `success: ${fname} saved to ${filePath}`;
|
|
192
623
|
}
|
|
193
624
|
catch (error) {
|
|
@@ -195,25 +626,40 @@ class Noblog extends NotionToMarkdown {
|
|
|
195
626
|
return `failed: ${fname} - ${error.message}`;
|
|
196
627
|
}
|
|
197
628
|
else {
|
|
198
|
-
return
|
|
629
|
+
return "failed: an unknown error occurred";
|
|
199
630
|
}
|
|
200
631
|
}
|
|
201
632
|
}
|
|
202
633
|
async AssembleAstroFrontmatter(page) {
|
|
203
634
|
let frontmatter = "";
|
|
204
635
|
// default layout
|
|
205
|
-
const ischild = this.Posts.filter(post => post.id === page.id).length > 0 ? "" : "../";
|
|
206
|
-
frontmatter +=
|
|
636
|
+
const ischild = this.Posts.filter((post) => post.id === page.id).length > 0 ? "" : "../";
|
|
637
|
+
frontmatter +=
|
|
638
|
+
"layout: " + ischild + "../../layouts/MarkdownPostLayout.astro" + "\n";
|
|
207
639
|
// get title
|
|
208
|
-
frontmatter +=
|
|
640
|
+
frontmatter +=
|
|
641
|
+
"title: " +
|
|
642
|
+
JSON.stringify(page.properties.title?.title?.[0]?.plain_text ?? "") +
|
|
643
|
+
"\n";
|
|
209
644
|
// get tags
|
|
210
|
-
frontmatter +=
|
|
645
|
+
frontmatter +=
|
|
646
|
+
"tags: " +
|
|
647
|
+
JSON.stringify((page.properties.tags?.multi_select ?? []).map((tag) => tag.name)) +
|
|
648
|
+
"\n";
|
|
211
649
|
// get date
|
|
212
|
-
frontmatter +=
|
|
650
|
+
frontmatter +=
|
|
651
|
+
"pubDate: " +
|
|
652
|
+
(page.properties.date?.date?.start ??
|
|
653
|
+
new Date().toISOString().split("T")[0]) +
|
|
654
|
+
"\n";
|
|
213
655
|
// get archived
|
|
214
|
-
frontmatter +=
|
|
656
|
+
frontmatter +=
|
|
657
|
+
"archived: " + (page.properties.archived ? "true" : "false") + "\n";
|
|
215
658
|
// get description
|
|
216
|
-
frontmatter +=
|
|
659
|
+
frontmatter +=
|
|
660
|
+
"description: " +
|
|
661
|
+
JSON.stringify(page.properties.description?.rich_text?.[0]?.plain_text ?? "") +
|
|
662
|
+
"\n";
|
|
217
663
|
return "---\n" + frontmatter + "---\n";
|
|
218
664
|
}
|
|
219
665
|
}
|