noblog 0.0.10 → 0.0.12
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 +25 -23
- package/dist/cmds/generate.js +28 -0
- package/dist/cmds/generate.js.map +1 -0
- package/dist/generate.cjs +463 -38
- package/dist/generate.cjs.map +1 -1
- package/dist/generate.js +463 -38
- 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 +28 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/nomd/format.d.ts +1 -0
- package/dist/lib/nomd/format.d.ts.map +1 -1
- package/dist/lib/nomd/format.js +137 -0
- package/dist/lib/nomd/format.js.map +1 -0
- package/dist/lib/nomd/nomd.d.ts +8 -3
- package/dist/lib/nomd/nomd.d.ts.map +1 -1
- package/dist/lib/nomd/nomd.js +473 -0
- package/dist/lib/nomd/nomd.js.map +1 -0
- package/dist/lib/nomd/utils.d.ts +6 -0
- package/dist/lib/nomd/utils.d.ts.map +1 -0
- package/dist/lib/nomd/utils.js +2 -0
- package/dist/lib/nomd/utils.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.d.ts.map +1 -1
- package/dist/lib/notion/request.js +2 -0
- package/dist/lib/notion/request.js.map +1 -0
- package/package.json +44 -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,18 +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 = [];
|
|
95
201
|
CurrPage = "";
|
|
96
202
|
SavePath = "./src/pages/posts/";
|
|
97
203
|
ChildPath = "nob_children/";
|
|
98
204
|
MdCollection = {};
|
|
205
|
+
CustomTransformers = {};
|
|
99
206
|
constructor(posts, save_path, child_path) {
|
|
100
|
-
super({ notionClient: client });
|
|
101
207
|
this.Posts = posts;
|
|
102
208
|
if (child_path != undefined) {
|
|
103
209
|
this.ChildPath = child_path;
|
|
@@ -105,21 +211,21 @@ class Noblog extends NotionToMarkdown {
|
|
|
105
211
|
if (save_path != undefined) {
|
|
106
212
|
this.SavePath = save_path;
|
|
107
213
|
}
|
|
108
|
-
this.
|
|
214
|
+
this.CustomTransformers["child_page"] = async (block) => {
|
|
109
215
|
const page = block;
|
|
110
216
|
if (!page?.id) {
|
|
111
217
|
return false;
|
|
112
218
|
}
|
|
113
219
|
let sub_path;
|
|
114
|
-
// if (curr_page.id in this.Post) and (page.id in this.Post)
|
|
115
|
-
//
|
|
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)
|
|
116
222
|
// * path should be "../pageid"
|
|
117
223
|
// if (curr_page.id in this.Post) and (page.id not in this.Post)
|
|
118
224
|
// * path should be "../save_path/pageid"
|
|
119
225
|
// if (curr_page.id not in this.Post) and (page.id in this.Post)
|
|
120
226
|
// * path should be "../../pageid"
|
|
121
|
-
const parent_surface =
|
|
122
|
-
const child_surface =
|
|
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;
|
|
123
229
|
if (parent_surface === child_surface) {
|
|
124
230
|
sub_path = "";
|
|
125
231
|
}
|
|
@@ -131,7 +237,7 @@ class Noblog extends NotionToMarkdown {
|
|
|
131
237
|
}
|
|
132
238
|
const full_path = path.join("../", sub_path, page.id);
|
|
133
239
|
return `&[${page.child_page.title}](${full_path})`;
|
|
134
|
-
}
|
|
240
|
+
};
|
|
135
241
|
}
|
|
136
242
|
async Collect(recur = true) {
|
|
137
243
|
const save_dir = this.SavePath;
|
|
@@ -142,7 +248,7 @@ class Noblog extends NotionToMarkdown {
|
|
|
142
248
|
try {
|
|
143
249
|
for (const pageid of Object.keys(this.MdCollection)) {
|
|
144
250
|
let fpath = sub_dir;
|
|
145
|
-
if (this.Posts.find(page => page.id === pageid))
|
|
251
|
+
if (this.Posts.find((page) => page.id === pageid))
|
|
146
252
|
fpath = save_dir;
|
|
147
253
|
await this.SaveJelly(pageid + ".md", fpath, this.MdCollection[pageid]);
|
|
148
254
|
}
|
|
@@ -153,7 +259,7 @@ class Noblog extends NotionToMarkdown {
|
|
|
153
259
|
console.log(`failed: consider run verbose with v=true - ${error.message}`);
|
|
154
260
|
}
|
|
155
261
|
else {
|
|
156
|
-
console.log(
|
|
262
|
+
console.log("failed: an unknown error occurred");
|
|
157
263
|
}
|
|
158
264
|
}
|
|
159
265
|
}
|
|
@@ -172,7 +278,8 @@ class Noblog extends NotionToMarkdown {
|
|
|
172
278
|
block.type !== "numbered_list_item") {
|
|
173
279
|
line_wrap = "\n";
|
|
174
280
|
}
|
|
175
|
-
jelly.content +=
|
|
281
|
+
jelly.content +=
|
|
282
|
+
line_wrap + `${addTabSpace(block.parent, 0)}\n` + line_wrap;
|
|
176
283
|
if (block.type === "child_page") {
|
|
177
284
|
jelly.children = [...jelly.children, block.blockId];
|
|
178
285
|
if (recursive)
|
|
@@ -192,23 +299,326 @@ class Noblog extends NotionToMarkdown {
|
|
|
192
299
|
if (this.MdCollection.hasOwnProperty(page_id)) {
|
|
193
300
|
return this.MdCollection[page_id];
|
|
194
301
|
}
|
|
195
|
-
const blocks = await this.
|
|
302
|
+
const blocks = await this.PageToMarkdown(page_id);
|
|
196
303
|
const jelly = await this.FromBlocks(blocks, recursive);
|
|
197
304
|
const meta = await GetPageMeta(page_id);
|
|
198
305
|
const astro_meta = await this.AssembleAstroFrontmatter(meta);
|
|
199
306
|
jelly.content = astro_meta + jelly.content;
|
|
200
307
|
this.MdCollection[page_id] = jelly;
|
|
201
|
-
// restore the prev page info
|
|
308
|
+
// restore the prev page info
|
|
202
309
|
this.CurrPage = prev_page;
|
|
203
310
|
return jelly;
|
|
204
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
|
+
}
|
|
205
615
|
async SaveJelly(fname, dir, jelly) {
|
|
206
616
|
if (jelly === undefined)
|
|
207
617
|
return `failed: ${fname} has empty jelly: `;
|
|
208
618
|
try {
|
|
209
619
|
await fs.mkdir(dir, { recursive: true });
|
|
210
620
|
const filePath = path.join(dir, fname);
|
|
211
|
-
await fs.writeFile(filePath, jelly.content,
|
|
621
|
+
await fs.writeFile(filePath, jelly.content, "utf-8");
|
|
212
622
|
return `success: ${fname} saved to ${filePath}`;
|
|
213
623
|
}
|
|
214
624
|
catch (error) {
|
|
@@ -216,25 +626,40 @@ class Noblog extends NotionToMarkdown {
|
|
|
216
626
|
return `failed: ${fname} - ${error.message}`;
|
|
217
627
|
}
|
|
218
628
|
else {
|
|
219
|
-
return
|
|
629
|
+
return "failed: an unknown error occurred";
|
|
220
630
|
}
|
|
221
631
|
}
|
|
222
632
|
}
|
|
223
633
|
async AssembleAstroFrontmatter(page) {
|
|
224
634
|
let frontmatter = "";
|
|
225
635
|
// default layout
|
|
226
|
-
const ischild = this.Posts.filter(post => post.id === page.id).length > 0 ? "" : "../";
|
|
227
|
-
frontmatter +=
|
|
636
|
+
const ischild = this.Posts.filter((post) => post.id === page.id).length > 0 ? "" : "../";
|
|
637
|
+
frontmatter +=
|
|
638
|
+
"layout: " + ischild + "../../layouts/MarkdownPostLayout.astro" + "\n";
|
|
228
639
|
// get title
|
|
229
|
-
frontmatter +=
|
|
640
|
+
frontmatter +=
|
|
641
|
+
"title: " +
|
|
642
|
+
JSON.stringify(page.properties.title?.title?.[0]?.plain_text ?? "") +
|
|
643
|
+
"\n";
|
|
230
644
|
// get tags
|
|
231
|
-
frontmatter +=
|
|
645
|
+
frontmatter +=
|
|
646
|
+
"tags: " +
|
|
647
|
+
JSON.stringify((page.properties.tags?.multi_select ?? []).map((tag) => tag.name)) +
|
|
648
|
+
"\n";
|
|
232
649
|
// get date
|
|
233
|
-
frontmatter +=
|
|
650
|
+
frontmatter +=
|
|
651
|
+
"pubDate: " +
|
|
652
|
+
(page.properties.date?.date?.start ??
|
|
653
|
+
new Date().toISOString().split("T")[0]) +
|
|
654
|
+
"\n";
|
|
234
655
|
// get archived
|
|
235
|
-
frontmatter +=
|
|
656
|
+
frontmatter +=
|
|
657
|
+
"archived: " + (page.properties.archived ? "true" : "false") + "\n";
|
|
236
658
|
// get description
|
|
237
|
-
frontmatter +=
|
|
659
|
+
frontmatter +=
|
|
660
|
+
"description: " +
|
|
661
|
+
JSON.stringify(page.properties.description?.rich_text?.[0]?.plain_text ?? "") +
|
|
662
|
+
"\n";
|
|
238
663
|
return "---\n" + frontmatter + "---\n";
|
|
239
664
|
}
|
|
240
665
|
}
|