Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"singleQuote": false,
"trailingComma": "es5",
"tabWidth": 2,
"semi": true
}
19 changes: 17 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
14 changes: 7 additions & 7 deletions src/lib/download-image.helper.ts
Original file line number Diff line number Diff line change
@@ -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" }
Expand All @@ -21,7 +21,7 @@ function isExternalImage(
}

function isFileImage(
image: ImageBlockObjectResponseExtended["image"],
image: ImageBlockObjectResponseExtended["image"]
): image is Extract<
ImageBlockObjectResponseExtended["image"],
{ type: "file" }
Expand All @@ -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;
Expand All @@ -49,4 +49,4 @@ export function updateImageUrl(
} else {
throw new Error("Invalid image type");
}
}
}
2 changes: 1 addition & 1 deletion src/lib/download-image.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ export const fileImageBlockFactory =
*/
export const externalImageBlockFactory = fileImageBlockFactory.extend({
image: externalImageFactory.build(),
});
});
8 changes: 4 additions & 4 deletions src/lib/download-image.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -32,7 +32,7 @@ async function updateImageOnBlock(
imageDir: string;
pageId: string;
},
imageCounter: { count: number },
imageCounter: { count: number }
): Promise<void> {
if (isImageBlock(block)) {
const originalUrl = getImageUrl(block);
Expand Down Expand Up @@ -83,8 +83,8 @@ export async function updateImageOnBlocks({
imageCounter?: { count: number };
}): Promise<void> {
const updatePromises = blocks.map((block) =>
updateImageOnBlock({ block, imageDir, pageId }, imageCounter),
updateImageOnBlock({ block, imageDir, pageId }, imageCounter)
);

await Promise.all(updatePromises);
}
}
49 changes: 47 additions & 2 deletions src/lib/dump-page.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
// 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 });

Expand All @@ -37,4 +82,4 @@ export async function fetchAndSavePageData({

console.log(`Page data saved to ${outputFile}`);
console.log(`Images saved to ${imageOutDir}`);
}
}
8 changes: 4 additions & 4 deletions src/lib/file-extension-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const mimeTypeToExtensionMap: Record<
};

export function getFileExtensionFromContentType(
contentType: string,
contentType: string
): SupportedImageExtension | undefined {
return mimeTypeToExtensionMap[contentType as SupportedImageMimeType];
}
Expand All @@ -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;
Expand All @@ -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;
}
}
38 changes: 31 additions & 7 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
#!/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";
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();
Expand All @@ -39,6 +42,15 @@ program
'notion-data'
)
.option(

.option(
"--meta",
"Fetch bookmark metadata (includes only title, url, description, favicon, image by default)"
)
.option(
"--fields <fields>",
"List of fields to include in bookmark (comma separated, optional)"
);
'--image-dir <dir>',
'Directory where the page images will be saved',
'public/notion-data'
Expand All @@ -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);
}
})();
})();
2 changes: 1 addition & 1 deletion src/lib/page-id-extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ export function extractPageIdFromUrl(url: string): string {
].join("-");

return formattedId;
}
}
22 changes: 11 additions & 11 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -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',
},
},
},
Expand Down