Some checks failed
Build & Deploy / 🔍 Prepare (push) Failing after 21s
Build & Deploy / 🧪 QA (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
157 lines
4.7 KiB
TypeScript
157 lines
4.7 KiB
TypeScript
import { getPayload } from "payload";
|
|
import configPromise from "../payload.config";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import { parseMarkdownToLexical } from "@mintel/payload-ai";
|
|
|
|
function parseMatter(content: string) {
|
|
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
if (!match) return { data: {}, content };
|
|
const data: Record<string, any> = {};
|
|
match[1].split("\n").forEach((line) => {
|
|
const [key, ...rest] = line.split(":");
|
|
if (key && rest.length) {
|
|
const field = key.trim();
|
|
let val = rest.join(":").trim();
|
|
if (val.startsWith("[")) {
|
|
// basic array parsing
|
|
data[field] = val
|
|
.slice(1, -1)
|
|
.split(",")
|
|
.map((s) => s.trim().replace(/^["']|["']$/g, ""));
|
|
} else {
|
|
data[field] = val.replace(/^["']|["']$/g, "");
|
|
}
|
|
}
|
|
});
|
|
return { data, content: match[2].trim() };
|
|
}
|
|
|
|
async function run() {
|
|
const payload = await getPayload({ config: configPromise });
|
|
const contentDir = path.join(process.cwd(), "content", "blog");
|
|
const files = fs.readdirSync(contentDir).filter((f) => f.endsWith(".mdx"));
|
|
|
|
for (const file of files) {
|
|
const filePath = path.join(contentDir, file);
|
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
const { data, content: body } = parseMatter(content);
|
|
|
|
const slug = file.replace(/\.mdx$/, "");
|
|
console.log(`Migrating ${slug}...`);
|
|
|
|
try {
|
|
const existing = await payload.find({
|
|
collection: "posts",
|
|
where: { slug: { equals: slug } },
|
|
});
|
|
|
|
const lexicalBlocks = parseMarkdownToLexical(body);
|
|
const lexicalAST = {
|
|
root: {
|
|
type: "root",
|
|
format: "",
|
|
indent: 0,
|
|
version: 1,
|
|
children: lexicalBlocks,
|
|
direction: "ltr",
|
|
},
|
|
};
|
|
|
|
// Handle thumbnail mapping
|
|
let featuredImageId = null;
|
|
if (data.thumbnail) {
|
|
try {
|
|
// Remove leading slash and find local file
|
|
const localPath = path.join(
|
|
process.cwd(),
|
|
"public",
|
|
data.thumbnail.replace(/^\//, ""),
|
|
);
|
|
const fileName = path.basename(localPath);
|
|
|
|
if (fs.existsSync(localPath)) {
|
|
// Check if media already exists in Payload
|
|
const existingMedia = await payload.find({
|
|
collection: "media",
|
|
where: { filename: { equals: fileName } },
|
|
});
|
|
|
|
if (existingMedia.docs.length > 0) {
|
|
featuredImageId = existingMedia.docs[0].id;
|
|
} else {
|
|
// Upload new media item
|
|
const fileData = fs.readFileSync(localPath);
|
|
const { size } = fs.statSync(localPath);
|
|
|
|
const newMedia = await payload.create({
|
|
collection: "media",
|
|
data: {
|
|
alt: data.title || fileName,
|
|
},
|
|
file: {
|
|
data: fileData,
|
|
name: fileName,
|
|
mimetype: fileName.endsWith(".png")
|
|
? "image/png"
|
|
: fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")
|
|
? "image/jpeg"
|
|
: "image/webp",
|
|
size,
|
|
},
|
|
});
|
|
featuredImageId = newMedia.id;
|
|
console.log(` ↑ Uploaded thumbnail: ${fileName}`);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.warn(
|
|
` ⚠ Warning: Could not process thumbnail ${data.thumbnail}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (existing.docs.length === 0) {
|
|
await payload.create({
|
|
collection: "posts",
|
|
data: {
|
|
title: data.title || slug,
|
|
slug,
|
|
description: data.description || "",
|
|
date: data.date
|
|
? new Date(data.date).toISOString()
|
|
: new Date().toISOString(),
|
|
tags: (data.tags || []).map((t: string) => ({ tag: t })),
|
|
content: lexicalAST as any,
|
|
featuredImage: featuredImageId,
|
|
},
|
|
});
|
|
console.log(`✔ Inserted ${slug}`);
|
|
} else {
|
|
await payload.update({
|
|
collection: "posts",
|
|
id: existing.docs[0].id,
|
|
data: {
|
|
content: lexicalAST as any,
|
|
featuredImage: featuredImageId,
|
|
},
|
|
});
|
|
console.log(`✔ Updated AST and thumbnail for ${slug}`);
|
|
}
|
|
} catch (err: any) {
|
|
console.error(`✘ FAILED ${slug}: ${err.message}`);
|
|
if (err.data?.errors) {
|
|
console.error(
|
|
` Validation errors:`,
|
|
JSON.stringify(err.data.errors, null, 2),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log("Migration complete.");
|
|
process.exit(0);
|
|
}
|
|
|
|
run().catch(console.error);
|