noblog 0.2.3 → 0.3.0-beta.1

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