diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..5a7704c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": false, + "trailingComma": "es5", + "tabWidth": 2, + "semi": true +} diff --git a/package-lock.json b/package-lock.json index 1030756..91c8712 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@notionpresso/cli", - "version": "0.0.2", + "version": "0.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@notionpresso/cli", - "version": "0.0.2", + "version": "0.0.3", "dependencies": { "@cozy-blog/notion-client": "^0.0.22", "commander": "^12.1.0", @@ -25,6 +25,21 @@ "vitest": "^2.1.1" } }, + "../api-sdk/package": { + "name": "@notionpresso/api-sdk", + "version": "0.0.1", + "extraneous": true, + "license": "ISC", + "dependencies": { + "@notionhq/client": "^2.2.3", + "@types/cheerio": "^0.22.35", + "cheerio": "1.0.0-rc.12" + }, + "devDependencies": { + "cp": "^0.2.0", + "typescript": "^4.9.4" + } + }, "node_modules/@cozy-blog/notion-client": { "version": "0.0.22", "resolved": "https://registry.npmjs.org/@cozy-blog/notion-client/-/notion-client-0.0.22.tgz", diff --git a/package.json b/package.json index 76764b0..3c03d2a 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,15 @@ { "name": "@notionpresso/cli", "private": false, - "version": "0.0.2", + "version": "0.0.3", "type": "module", "scripts": { "dev": "vite", "build": "tsc && vite build", "preview": "vite preview", "test": "vitest", - "prepare": "ts-patch install && typia patch" + "prepare": "ts-patch install && typia patch", + "cli": "node ./dist/notionpresso.es.js" }, "devDependencies": { "@ryoppippi/unplugin-typia": "^1.0.6", diff --git a/src/lib/download-image.helper.ts b/src/lib/download-image.helper.ts index 7720519..9fe51ac 100644 --- a/src/lib/download-image.helper.ts +++ b/src/lib/download-image.helper.ts @@ -1,18 +1,18 @@ -import { Block } from "@cozy-blog/notion-client"; -import { ImageBlockObjectResponse } from "@notionhq/client/build/src/api-endpoints"; +import type { Block } from "@notionpresso/api-sdk"; +import type { ImageBlockObjectResponse } from "@notionhq/client/build/src/api-endpoints"; export type ImageBlockObjectResponseExtended = ImageBlockObjectResponse & { blocks: Block[]; }; export function isImageBlock( - block: Block, + block: Block ): block is ImageBlockObjectResponseExtended { return block.type === "image"; } function isExternalImage( - image: ImageBlockObjectResponseExtended["image"], + image: ImageBlockObjectResponseExtended["image"] ): image is Extract< ImageBlockObjectResponseExtended["image"], { type: "external" } @@ -21,7 +21,7 @@ function isExternalImage( } function isFileImage( - image: ImageBlockObjectResponseExtended["image"], + image: ImageBlockObjectResponseExtended["image"] ): image is Extract< ImageBlockObjectResponseExtended["image"], { type: "file" } @@ -40,7 +40,7 @@ export function getImageUrl(block: ImageBlockObjectResponseExtended): string { export function updateImageUrl( block: ImageBlockObjectResponseExtended, - newUrl: string, + newUrl: string ): void { if (isExternalImage(block.image)) { block.image.external.url = newUrl; @@ -49,4 +49,4 @@ export function updateImageUrl( } else { throw new Error("Invalid image type"); } -} \ No newline at end of file +} diff --git a/src/lib/download-image.mock.ts b/src/lib/download-image.mock.ts index e2e1154..7cab83a 100644 --- a/src/lib/download-image.mock.ts +++ b/src/lib/download-image.mock.ts @@ -76,4 +76,4 @@ export const fileImageBlockFactory = */ export const externalImageBlockFactory = fileImageBlockFactory.extend({ image: externalImageFactory.build(), -}); \ No newline at end of file +}); diff --git a/src/lib/download-image.ts b/src/lib/download-image.ts index 08718c1..68cd751 100644 --- a/src/lib/download-image.ts +++ b/src/lib/download-image.ts @@ -1,7 +1,7 @@ import * as fs from "fs"; import * as path from "path"; import { getFileExtension } from "./file-extension-utils"; -import { Block } from "@cozy-blog/notion-client"; +import type { Block } from "@notionpresso/api-sdk"; import { getImageUrl, isImageBlock, @@ -32,7 +32,7 @@ async function updateImageOnBlock( imageDir: string; pageId: string; }, - imageCounter: { count: number }, + imageCounter: { count: number } ): Promise { if (isImageBlock(block)) { const originalUrl = getImageUrl(block); @@ -83,8 +83,8 @@ export async function updateImageOnBlocks({ imageCounter?: { count: number }; }): Promise { const updatePromises = blocks.map((block) => - updateImageOnBlock({ block, imageDir, pageId }, imageCounter), + updateImageOnBlock({ block, imageDir, pageId }, imageCounter) ); await Promise.all(updatePromises); -} \ No newline at end of file +} diff --git a/src/lib/dump-page.ts b/src/lib/dump-page.ts index 2784661..93fdb81 100644 --- a/src/lib/dump-page.ts +++ b/src/lib/dump-page.ts @@ -1,22 +1,67 @@ -import { Client } from "@cozy-blog/notion-client"; +import Client, { bookmarkPreprocessors } from "@notionpresso/api-sdk"; import * as fs from "fs"; import * as path from "path"; import { updateImageOnBlocks } from "./download-image"; +interface Meta { + meta?: boolean; + fields?: string[]; +} + +function removeOriginalBookmarkProperty(blocks: any[]): any[] { + return blocks.map((block) => { + if (block.type === "notionpresso_bookmark" && block.bookmark) { + const { bookmark, ...rest } = block; + return rest; + } + + if (block.blocks?.length) { + return { + ...block, + blocks: removeOriginalBookmarkProperty(block.blocks), + }; + } + + return block; + }); +} + export async function fetchAndSavePageData({ client, pageId, outputDir, imageOutDir, + meta, }: { client: Client; pageId: string; outputDir: string; imageOutDir: string; + meta?: Meta; }): Promise { // Fetch full page data const fullPage = await client.fetchFullPage(pageId); + if (meta?.meta) { + console.log("Fetching bookmark metadata..."); + + try { + fullPage.blocks = await bookmarkPreprocessors.processBlocks( + fullPage.blocks, + { + meta: meta.meta, + fields: meta.fields, + } + ); + + fullPage.blocks = removeOriginalBookmarkProperty(fullPage.blocks); + + console.log("Bookmark metadata fetch completed"); + } catch (error) { + console.error("Error transforming bookmarks:", (error as Error).message); + } + } + // Create image directory fs.mkdirSync(imageOutDir, { recursive: true }); @@ -37,4 +82,4 @@ export async function fetchAndSavePageData({ console.log(`Page data saved to ${outputFile}`); console.log(`Images saved to ${imageOutDir}`); -} \ No newline at end of file +} diff --git a/src/lib/file-extension-utils.ts b/src/lib/file-extension-utils.ts index f7e2a0f..48a1149 100644 --- a/src/lib/file-extension-utils.ts +++ b/src/lib/file-extension-utils.ts @@ -28,7 +28,7 @@ const mimeTypeToExtensionMap: Record< }; export function getFileExtensionFromContentType( - contentType: string, + contentType: string ): SupportedImageExtension | undefined { return mimeTypeToExtensionMap[contentType as SupportedImageMimeType]; } @@ -42,7 +42,7 @@ export function getFileExtensionFromUrl(url: string): string { export function getFileExtension( contentType: string, - originalUrl: string, + originalUrl: string ): SupportedImageExtension { const extensionFromContentType = getFileExtensionFromContentType(contentType); if (extensionFromContentType) return extensionFromContentType; @@ -51,11 +51,11 @@ export function getFileExtension( if ( extensionFromUrl && Object.values(mimeTypeToExtensionMap).includes( - extensionFromUrl as SupportedImageExtension, + extensionFromUrl as SupportedImageExtension ) ) { return extensionFromUrl as SupportedImageExtension; } return DEFAULT_IMAGE_EXTENSION; -} \ No newline at end of file +} diff --git a/src/lib/index.ts b/src/lib/index.ts index ff0c62c..2ed9213 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node import { Command } from "commander"; -import { Client } from "@cozy-blog/notion-client"; +import Client from "@notionpresso/api-sdk"; import { extractPageIdFromUrl } from "./page-id-extractor"; import * as path from "path"; import { fetchAndSavePageData } from "./dump-page"; @@ -9,12 +9,15 @@ import typia from "typia"; const DEFAULT_OUTPUT_DIR = "notion-data"; const DEFAULT_IMAGE_OUT_DIR = "public/notion-data"; +const DEFAULT_FIELDS = ["title", "url", "description", "favicon", "image"]; interface CLIOptions { page: string; auth: string; dir?: string; imageDir?: string; + meta?: boolean; + fields?: string; } const program = new Command(); @@ -39,6 +42,15 @@ program 'notion-data' ) .option( + + .option( + "--meta", + "Fetch bookmark metadata (includes only title, url, description, favicon, image by default)" + ) + .option( + "--fields ", + "List of fields to include in bookmark (comma separated, optional)" + ); '--image-dir ', 'Directory where the page images will be saved', 'public/notion-data' @@ -65,19 +77,31 @@ const outputDir = path.join(process.cwd(), options.dir || DEFAULT_OUTPUT_DIR); const imageOutDir = path.join( process.cwd(), options.imageDir || DEFAULT_IMAGE_OUT_DIR, - pageId, + pageId ); const client = new Client({ auth: options.auth }); -/** - * fetch and save page data - */ +const meta = { + meta: options.meta, + fields: options.fields + ? options.fields.split(",") + : options.meta + ? DEFAULT_FIELDS + : undefined, +}; + (async () => { try { - await fetchAndSavePageData({ client, pageId, outputDir, imageOutDir }); + await fetchAndSavePageData({ + client, + pageId, + outputDir, + imageOutDir, + meta, + }); } catch (error: any) { console.error("Error fetching page data:", error.message); process.exit(1); } -})(); \ No newline at end of file +})(); diff --git a/src/lib/page-id-extractor.ts b/src/lib/page-id-extractor.ts index d34f77f..bcd3d33 100644 --- a/src/lib/page-id-extractor.ts +++ b/src/lib/page-id-extractor.ts @@ -28,4 +28,4 @@ export function extractPageIdFromUrl(url: string): string { ].join("-"); return formattedId; -} \ No newline at end of file +} diff --git a/vite.config.ts b/vite.config.ts index 9d9ed81..9fc1234 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,29 +1,29 @@ -import { defineConfig } from "vite"; -import { resolve } from "path"; -import UnpluginTypia from "@ryoppippi/unplugin-typia/vite"; +import { defineConfig } from 'vite'; +import { resolve } from 'path'; +import UnpluginTypia from '@ryoppippi/unplugin-typia/vite'; // Library name -const LIB_NAME = "NotionPresso"; +const LIB_NAME = 'NotionPresso'; export default defineConfig({ build: { // Library specific build configuration lib: { // Set the entry point of the library - entry: resolve(__dirname, "src/lib/index.ts"), + entry: resolve(__dirname, 'src/lib/index.ts'), name: LIB_NAME, - fileName: (format) => `${LIB_NAME.toLowerCase()}.${format}.js`, + fileName: format => `${LIB_NAME.toLowerCase()}.${format}.js`, }, rollupOptions: { // Specify external dependencies not to be bundled - external: ["@cozy-blog/notion-client", "commander", "path", "fs"], + external: ['@notionpresso/api-sdk', 'commander', 'path', 'fs'], output: { // Global variable mapping for UMD build globals: { - "@cozy-blog/notion-client": "NotionClient", - commander: "commander", - path: "path", - fs: "fs", + '@notionpresso/api-sdk': 'NotionClient', + commander: 'commander', + path: 'path', + fs: 'fs', }, }, },