import path from 'node:path' import { fileURLToPath } from 'node:url' import { discoverAll } from './import/discover' import { collectUniqueTags, upsertTags } from './import/tags' import { scanImageRefs } from './import/scan-image-refs' import { preprocessMdx } from './import/preprocess-mdx' import { markdownToLexical } from './import/markdown-to-lexical' import { loadImageCache, persistImageCache, uploadImage } from './import/images' import { loadOgCache, persistOgCache } from './import/og-fetch' import { upsertRecord } from './import/upsert-record' import { slugify } from '@/lib/slugify' const args = new Map( process.argv.slice(2).map((a) => { const m = a.match(/^--([^=]+)(?:=(.*))?$/) return m ? [m[1], m[2] ?? true] : ['_', a] }), ) const dryRun = args.get('dry-run') === true const sourceRoot = (args.get('source') as string | undefined) ?? path.resolve(fileURLToPath(import.meta.url), '../../../arter-blog') async function main() { console.log(`[import] source=${sourceRoot} dryRun=${dryRun}`) const { posts, projects, pages } = await discoverAll(sourceRoot) console.log(`[import] discovered: ${posts.length} posts, ${projects.length} projects, ${pages.length} pages`) const tags = collectUniqueTags(posts.map((p) => ({ tags: p.frontmatter.tags ?? [] }))) console.log(`[import] unique tags: ${tags.length}`) const allRefs = new Set() for (const p of [...posts, ...projects, ...pages]) { for (const ref of scanImageRefs(p.body)) allRefs.add(ref) const seoSrc = (p.frontmatter as { seo?: { image?: { src?: string } } }).seo?.image?.src if (seoSrc?.startsWith('/images/')) allRefs.add(seoSrc) } console.log(`[import] unique image refs: ${allRefs.size}`) const imageCache = await loadImageCache() const ogCache = await loadOgCache() if (dryRun) { // Dry-run only validates discovery + counts; conversion now requires Payload. console.log('[import] dry-run: skipping conversion (requires Payload).') await persistImageCache(imageCache) await persistOgCache(ogCache) return } const { getPayload } = await import('payload') const { default: config } = await import('@/payload.config') const payload = await getPayload({ config }) const tagIdBySlug = await upsertTags(payload, tags) console.log(`[import] tags upserted: ${tagIdBySlug.size}`) // upload all unique image refs first for (const ref of allRefs) { const id = await uploadImage(payload, ref, sourceRoot, ref.split('/').pop() ?? ref, imageCache) if (id !== undefined) imageCache[ref] = id } const imageMap = new Map(Object.entries(imageCache)) console.log(`[import] images uploaded/cached: ${imageMap.size}`) // write posts let created = 0, updated = 0 for (const file of posts) { const pre = preprocessMdx(file.body, imageMap) const body = await markdownToLexical(payload, pre) const fm = file.frontmatter const tagIds = (fm.tags ?? []) .map((t) => tagIdBySlug.get(slugify(t))) .filter((id): id is string | number => id !== undefined) const seoSrc = fm.seo?.image?.src const coverImage = seoSrc?.startsWith('/images/') ? imageCache[seoSrc] : undefined const data = { title: fm.title, subtitle: fm.subtitle, excerpt: fm.excerpt, publishDate: fm.publishDate, updatedDate: fm.updatedDate, isFeatured: fm.isFeatured, tags: tagIds, coverImage, seo: { title: fm.seo?.title, description: fm.seo?.description, pageType: fm.seo?.pageType ?? 'article', image: coverImage, }, body, _status: 'published', } const r = await upsertRecord(payload, 'posts', file.slug, data) r.action === 'created' ? created++ : updated++ } console.log(`[import] posts: ${created} created, ${updated} updated`) // write projects let pCreated = 0, pUpdated = 0 for (const file of projects) { const pre = preprocessMdx(file.body, imageMap) const body = await markdownToLexical(payload, pre) const fm = file.frontmatter const seoSrc = fm.seo?.image?.src const coverImage = seoSrc?.startsWith('/images/') ? imageCache[seoSrc] : undefined const data = { title: fm.title, description: fm.description, publishDate: fm.publishDate, isFeatured: fm.isFeatured, coverImage, seo: { title: fm.seo?.title, description: fm.seo?.description, pageType: fm.seo?.pageType ?? 'article', image: coverImage, }, body, _status: 'published', } const r = await upsertRecord(payload, 'projects', file.slug, data) r.action === 'created' ? pCreated++ : pUpdated++ } console.log(`[import] projects: ${pCreated} created, ${pUpdated} updated`) // write pages let pgCreated = 0, pgUpdated = 0 for (const file of pages) { const pre = preprocessMdx(file.body, imageMap) const body = await markdownToLexical(payload, pre) const fm = file.frontmatter const seoSrc = fm.seo?.image?.src const coverImage = seoSrc?.startsWith('/images/') ? imageCache[seoSrc] : undefined const data = { title: fm.title, subtitle: fm.subtitle, description: fm.description, seo: { title: fm.seo?.title, description: fm.seo?.description, pageType: fm.seo?.pageType ?? 'website', image: coverImage, }, body, _status: 'published', } const r = await upsertRecord(payload, 'pages', file.slug, data) r.action === 'created' ? pgCreated++ : pgUpdated++ } console.log(`[import] pages: ${pgCreated} created, ${pgUpdated} updated`) await persistImageCache(imageCache) await persistOgCache(ogCache) } main().catch((err) => { console.error(err) process.exit(1) })