|
| 1 | +require('dotenv').config({ path: '../.env' }); |
| 2 | +const { Client } = require('@notionhq/client'); |
| 3 | +const { NotionToMarkdown } = require('notion-to-md'); |
| 4 | +const fs = require('fs'); |
| 5 | +const path = require('path'); |
| 6 | + |
| 7 | +const notion = new Client({ auth: process.env.NOTION_TOKEN }); |
| 8 | +const n2m = new NotionToMarkdown({ notionClient: notion }); |
| 9 | + |
| 10 | +function getPageTitle(page) { |
| 11 | + const titleProp = Object.values(page.properties).find(p => p.type === 'title'); |
| 12 | + return titleProp?.title?.[0]?.plain_text || 'Untitled Page'; |
| 13 | +} |
| 14 | + |
| 15 | +async function processPage(pageId, parentFolder, childPages = []) { |
| 16 | + try { |
| 17 | + const page = await notion.pages.retrieve({ page_id: pageId }); |
| 18 | + |
| 19 | + const mdBlocks = await n2m.pageToMarkdown(pageId); |
| 20 | + let finalMarkdown = n2m.toMarkdownString(mdBlocks)?.parent || ''; |
| 21 | + |
| 22 | + let title = getPageTitle(page); |
| 23 | + |
| 24 | + if ((title === "Untitled Page" || !title) && mdBlocks.length > 0) { |
| 25 | + const firstBlock = mdBlocks.find(b => { |
| 26 | + const text = b.plain_text || (b[b.type]?.text?.map(t => t.plain_text).join("") || ""); |
| 27 | + return text && text.trim() !== ""; |
| 28 | + }); |
| 29 | + if (firstBlock) { |
| 30 | + title = firstBlock.plain_text || (firstBlock[firstBlock.type]?.text?.map(t => t.plain_text).join("") || ""); |
| 31 | + } |
| 32 | + } |
| 33 | + |
| 34 | + if (!title) title = "Untitled Page"; |
| 35 | + if (!finalMarkdown || finalMarkdown.trim() === "") { |
| 36 | + finalMarkdown = "*This page has no content*"; |
| 37 | + } |
| 38 | + |
| 39 | + const safeTitle = title.replace(/[/\\?%*:|"<>]/g, "-"); |
| 40 | + |
| 41 | + const pageFolder = path.join(parentFolder, safeTitle); |
| 42 | + if (fs.existsSync(pageFolder)) { |
| 43 | + fs.rmSync(pageFolder, { recursive: true, force: true }); |
| 44 | + } |
| 45 | + fs.mkdirSync(pageFolder, { recursive: true }); |
| 46 | + |
| 47 | + const children = await notion.blocks.children.list({ block_id: pageId }); |
| 48 | + const childPageBlocks = []; |
| 49 | + |
| 50 | + for (const block of children.results) { |
| 51 | + if (block.type === "child_page") { |
| 52 | + childPageBlocks.push(block); |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + const processedChildren = []; |
| 57 | + for (const block of childPageBlocks) { |
| 58 | + const childTitle = block.child_page?.title || "Untitled"; |
| 59 | + const childSafeTitle = childTitle.replace(/[/\\?%*:|"<>]/g, "-"); |
| 60 | + processedChildren.push({ id: block.id, title: childTitle, safeTitle: childSafeTitle }); |
| 61 | + await processPage(block.id, pageFolder); |
| 62 | + } |
| 63 | + |
| 64 | + const fileUrlPattern = /\[file\]\(https:\/\/prod-files-secure\.s3[^)]+\)/g; |
| 65 | + let matchIndex = 0; |
| 66 | + finalMarkdown = finalMarkdown.replace(fileUrlPattern, () => { |
| 67 | + if (matchIndex < processedChildren.length) { |
| 68 | + const child = processedChildren[matchIndex]; |
| 69 | + matchIndex++; |
| 70 | + return `[${child.title}](./${child.safeTitle}/${child.safeTitle}.md)`; |
| 71 | + } |
| 72 | + return '[file]()'; |
| 73 | + }); |
| 74 | + |
| 75 | + fs.writeFileSync(path.join(pageFolder, `${safeTitle}.md`), finalMarkdown, "utf8"); |
| 76 | + } catch (err) { |
| 77 | + console.error(`Error processing page ${pageId}:`, err.message); |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +async function main() { |
| 82 | + try { |
| 83 | + const pageIds = JSON.parse(fs.readFileSync(path.join(__dirname, 'pages.json'), 'utf8')); |
| 84 | + |
| 85 | + if (!pageIds || pageIds.length === 0) { |
| 86 | + console.error('No page IDs found in pages.json'); |
| 87 | + process.exit(1); |
| 88 | + } |
| 89 | + |
| 90 | + const outputDir = path.join(__dirname, '..', 'notes'); |
| 91 | + |
| 92 | + for (const pageId of pageIds) { |
| 93 | + console.log(`Processing page: ${pageId}`); |
| 94 | + await processPage(pageId, outputDir); |
| 95 | + } |
| 96 | + |
| 97 | + console.log('✅ Notion sync completed successfully'); |
| 98 | + } catch (err) { |
| 99 | + console.error('❌ Sync failed:', err.message); |
| 100 | + process.exit(1); |
| 101 | + } |
| 102 | +} |
| 103 | + |
| 104 | +main(); |
0 commit comments