From aa404d4b0687312d847af0e8f2fce0e13ef9740e Mon Sep 17 00:00:00 2001 From: dewkul Date: Sat, 18 Jun 2022 22:31:05 +0700 Subject: [PATCH 1/3] feat: render bookmark as link --- README.md | 2 +- src/BlockRenderer.ts | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c37e822..3240b6b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ The following [Notion API block object types](https://developers.notion.com/refe | Video | ❌ Missing | | | File | ❌ Missing | | | PDF | ❌ Missing | | -| Bookmark | ❌ Missing | | +| Bookmark | ✅ Yes | use a caption as a link name | | | Equation | ❌ Missing | | | Divider | ✅ Yes | | | Table Of Contents | ❌ not planned | static site generators have their own ToC implementations | diff --git a/src/BlockRenderer.ts b/src/BlockRenderer.ts index 54ffe9b..ddc52db 100644 --- a/src/BlockRenderer.ts +++ b/src/BlockRenderer.ts @@ -102,16 +102,28 @@ export class BlockRenderer { return { lines: "---" }; case "child_database": const msg = `\n`; - const db = await this.deferredRenderer.renderChildDatabase(block.id, context.linkResolver); + const db = await this.deferredRenderer.renderChildDatabase( + block.id, + context.linkResolver + ); return { lines: msg + db.markdown }; case "synced_block": // nothing to render, only the contents of the synced block are relevant // however, these are children nöpcl, and thus retrieved by recursion in RecusivveBodyRenderer return null; + case "bookmark": + const caption = block.bookmark.caption; + let title = block.bookmark.url; + if (caption) { + if (caption.length > 0) + title = await this.richText.renderPlainText(caption); + } + return { + lines: `[${title}](${block.bookmark.url})`, + }; case "toggle": case "child_page": case "embed": - case "bookmark": case "video": case "file": case "pdf": @@ -145,10 +157,7 @@ export class BlockRenderer { } } - async renderImage( - block: ImageBlock, - assets: AssetWriter - ): Promise { + async renderImage(block: ImageBlock, assets: AssetWriter): Promise { const url = this.parseUrl(block.image); const imageFile = await assets.download(url, block.id); From 2c11274f25f0950f6ecaa7714752ae54d92f650d Mon Sep 17 00:00:00 2001 From: dewkul Date: Wed, 22 Jun 2022 23:29:56 +0700 Subject: [PATCH 2/3] refactor: render bookmark with LinkRenderer --- src/BlockRenderer.ts | 15 ++++++++------- src/sync.ts | 41 ++++++++++++++++++++++++----------------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/BlockRenderer.ts b/src/BlockRenderer.ts index ddc52db..6f98ea9 100644 --- a/src/BlockRenderer.ts +++ b/src/BlockRenderer.ts @@ -14,6 +14,7 @@ import { DeferredRenderer } from "./DeferredRenderer"; import { RenderingContextLogger } from "./RenderingContextLogger"; import { RichTextRenderer } from "./RichTextRenderer"; import { RenderingContext } from "./RenderingContext"; +import { LinkRenderer } from "./LinkRenderer"; const debug = require("debug")("blocks"); @@ -24,7 +25,8 @@ export interface BlockRenderResult { export class BlockRenderer { constructor( private readonly richText: RichTextRenderer, - private readonly deferredRenderer: DeferredRenderer + private readonly deferredRenderer: DeferredRenderer, + private readonly link: LinkRenderer ) {} async renderBlock( @@ -112,14 +114,13 @@ export class BlockRenderer { // however, these are children nöpcl, and thus retrieved by recursion in RecusivveBodyRenderer return null; case "bookmark": - const caption = block.bookmark.caption; + // render caption (if provided) as a link name + const caption = block.bookmark.caption || []; let title = block.bookmark.url; - if (caption) { - if (caption.length > 0) - title = await this.richText.renderPlainText(caption); - } + if (caption.length > 0) + title = await this.richText.renderPlainText(caption); return { - lines: `[${title}](${block.bookmark.url})`, + lines: this.link.renderUrlLink(title, block.bookmark.url), }; case "toggle": case "child_page": diff --git a/src/sync.ts b/src/sync.ts index ffd7b14..4bbeda9 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -1,18 +1,18 @@ -import { BlockRenderer } from './BlockRenderer'; -import { ChildDatabaseRenderer } from './ChildDatabaseRenderer'; -import { DatabaseEntryRenderer } from './DatabaseEntryRenderer'; -import { DatabasePageRenderer } from './DatabasePageRenderer'; -import { DatabaseViewRenderer } from './DatabaseViewRenderer'; -import { DeferredRenderer } from './DeferredRenderer'; -import { FrontmatterRenderer } from './FrontmatterRenderer'; -import { LinkRenderer } from './LinkRenderer'; -import { MentionedPageRenderer } from './MentionedPageRenderer'; -import { NotionApiFacade } from './NotionApiFacade'; -import { PageLinkResolver } from './PageLinkResolver'; -import { PropertiesParser } from './PropertiesParser'; -import { RecursiveBodyRenderer } from './RecursiveBodyRenderer'; -import { RichTextRenderer } from './RichTextRenderer'; -import { SyncConfig } from './SyncConfig'; +import { BlockRenderer } from "./BlockRenderer"; +import { ChildDatabaseRenderer } from "./ChildDatabaseRenderer"; +import { DatabaseEntryRenderer } from "./DatabaseEntryRenderer"; +import { DatabasePageRenderer } from "./DatabasePageRenderer"; +import { DatabaseViewRenderer } from "./DatabaseViewRenderer"; +import { DeferredRenderer } from "./DeferredRenderer"; +import { FrontmatterRenderer } from "./FrontmatterRenderer"; +import { LinkRenderer } from "./LinkRenderer"; +import { MentionedPageRenderer } from "./MentionedPageRenderer"; +import { NotionApiFacade } from "./NotionApiFacade"; +import { PageLinkResolver } from "./PageLinkResolver"; +import { PropertiesParser } from "./PropertiesParser"; +import { RecursiveBodyRenderer } from "./RecursiveBodyRenderer"; +import { RichTextRenderer } from "./RichTextRenderer"; +import { SyncConfig } from "./SyncConfig"; export async function sync(notionApiToken: string, config: SyncConfig) { const publicApi = new NotionApiFacade(notionApiToken); @@ -32,7 +32,11 @@ export async function sync(notionApiToken: string, config: SyncConfig) { linkRenderer ); const propertiesParser = new PropertiesParser(richTextRenderer); - const blockRenderer = new BlockRenderer(richTextRenderer, deferredRenderer); + const blockRenderer = new BlockRenderer( + richTextRenderer, + deferredRenderer, + linkRenderer + ); const bodyRenderer = new RecursiveBodyRenderer(publicApi, blockRenderer); const entryRenderer = new DatabaseEntryRenderer(propertiesParser); const pageRenderer = new DatabasePageRenderer( @@ -52,7 +56,10 @@ export async function sync(notionApiToken: string, config: SyncConfig) { // seed it with the root database const rootLinkResolver = new PageLinkResolver("."); - await deferredRenderer.renderChildDatabase(config.cmsDatabaseId, rootLinkResolver); + await deferredRenderer.renderChildDatabase( + config.cmsDatabaseId, + rootLinkResolver + ); await deferredRenderer.process(); const rendered = deferredRenderer.getRenderedPages(); From 3c95e7cd227b2e906d868a3342c78f3d69c10f67 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Mon, 17 Oct 2022 12:05:17 +0200 Subject: [PATCH 3/3] feat: add support for database paging notion-markdown-cms would previously fail when trying to query pages from a database with more than 100 entries because paging was not implemented. --- package.json | 2 +- src/ChildDatabaseRenderer.ts | 2 +- src/NotionApiFacade.ts | 36 ++++++++++++++++++++++++------------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 203538f..361c857 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.10.1", + "version": "0.11.0", "name": "@meshcloud/notion-markdown-cms", "engines": { "node": ">=14" diff --git a/src/ChildDatabaseRenderer.ts b/src/ChildDatabaseRenderer.ts index 5273eca..ae52645 100644 --- a/src/ChildDatabaseRenderer.ts +++ b/src/ChildDatabaseRenderer.ts @@ -106,6 +106,6 @@ export class ChildDatabaseRenderer { page_size: 100, }); - return allPages.results; + return allPages; } } diff --git a/src/NotionApiFacade.ts b/src/NotionApiFacade.ts index 2cbd92b..5b4727d 100644 --- a/src/NotionApiFacade.ts +++ b/src/NotionApiFacade.ts @@ -1,7 +1,11 @@ import { - APIErrorCode, APIResponseError, Client, RequestTimeoutError, UnknownHTTPResponseError -} from '@notionhq/client'; -import { DatabasesQueryParameters } from '@notionhq/client/build/src/api-endpoints'; + APIErrorCode, + APIResponseError, + Client, + RequestTimeoutError, + UnknownHTTPResponseError, +} from "@notionhq/client"; +import { DatabasesQueryParameters } from "@notionhq/client/build/src/api-endpoints"; const debug = require("debug")("notion-api"); @@ -34,17 +38,25 @@ export class NotionApiFacade { } async queryDatabase(query: DatabasesQueryParameters) { - const result = await this.withRetry( - async () => await this.client.databases.query(query) - ); // todo: paging + const results = []; - if (result.next_cursor) { - throw new Error( - `Paging not implemented, db ${query.database_id} has more than 100 entries` - ); - } + let next_cursor: string | null = null; - return result; + do { + const response = await this.withRetry( + async () => + await this.client.databases.query({ + ...query, + start_cursor: next_cursor || undefined, + }) + ); + + results.push(...response.results); + + next_cursor = response.next_cursor; + } while (next_cursor); + + return results; } async retrievePage(pageId: string) {