aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/migrate-to-question-summary.mjs187
1 files changed, 187 insertions, 0 deletions
diff --git a/scripts/migrate-to-question-summary.mjs b/scripts/migrate-to-question-summary.mjs
new file mode 100644
index 0000000..75cabd2
--- /dev/null
+++ b/scripts/migrate-to-question-summary.mjs
@@ -0,0 +1,187 @@
+import { readFileSync, writeFileSync } from "node:fs";
+import { readdir } from "node:fs/promises";
+import { join, extname, relative } from "node:path";
+
+const DOCS_DIR = join(import.meta.dirname, "..", "src", "content", "docs");
+
+/**
+ * Recursively collect all .mdx files, excluding files starting with "_" or "index".
+ */
+async function collectMdxFiles(dir) {
+ const entries = await readdir(dir, { withFileTypes: true });
+ const files = [];
+
+ for (const entry of entries) {
+ if (entry.name.startsWith("_")) continue;
+ const fullPath = join(dir, entry.name);
+ if (entry.isDirectory()) {
+ const subFiles = await collectMdxFiles(fullPath);
+ files.push(...subFiles);
+ } else if (extname(entry.name) === ".mdx" && entry.name !== "index.mdx") {
+ files.push(fullPath);
+ }
+ }
+
+ return files;
+}
+
+/**
+ * Extract Q&A rows from the summary table in the MDX content.
+ * Table format:
+ * | 質問 | 答弁概要(クリックで詳細) |
+ * |---|---|
+ * | ① question text | [answer text](#anchor) |
+ */
+function extractQATable(content) {
+ // Find the summary table: starts with "| 質問 | 答弁概要"
+ const tableStartRegex =
+ /\| 質問 \| 答弁概要(クリックで詳細) \|\r?\n\|[-| ]+\|\r?\n/;
+ const tableStartMatch = content.match(tableStartRegex);
+ if (!tableStartMatch) return null;
+
+ const tableStart = tableStartMatch.index;
+ const afterHeader = content.slice(tableStart + tableStartMatch[0].length);
+
+ // Collect table rows
+ const rows = [];
+ const lines = afterHeader.split(/\r?\n/);
+ for (const line of lines) {
+ const rowMatch = line.match(/^\| (.+?) \| \[(.+?)\]\((#.+?)\)\s*\|$/);
+ if (!rowMatch) break;
+ rows.push({
+ question: rowMatch[1].trim(),
+ answer: rowMatch[2].trim(),
+ anchor: rowMatch[3].replace(/^#/, "").trim(),
+ });
+ }
+
+ if (rows.length === 0) return null;
+
+ // Calculate the end of the table (header + separator + all rows)
+ const tableText = content.slice(tableStart);
+ const tableEndMatch = tableText.match(
+ new RegExp(
+ `(?:^\\| .+? \\| \\[.+?\\]\\(#.+?\\)\\s*\\|$(?:\\r?\\n)?){${rows.length}}`,
+ "m",
+ ),
+ );
+ if (!tableEndMatch) return null;
+
+ const tableEnd =
+ tableStart + tableStartMatch[0].length + tableEndMatch[0].length;
+
+ return {
+ rows,
+ tableStart,
+ tableEnd,
+ };
+}
+
+/**
+ * Extract frontmatter fields from MDX content.
+ */
+function extractFrontmatter(content) {
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
+ if (!match) return {};
+ const fm = match[1];
+
+ const titleMatch = fm.match(/^title:\s*["']?(.+?)["']?\s*$/m);
+ const dateMatch = fm.match(
+ /^(?:first|date):\s*["']?(\d{4}-\d{2}-\d{2})["']?\s*$/m,
+ );
+
+ return {
+ title: titleMatch ? titleMatch[1].trim() : null,
+ date: dateMatch ? dateMatch[1].trim() : null,
+ };
+}
+
+/**
+ * Ensure the import line for QuestionSummary exists in the import block.
+ */
+function addImport(content, importPath) {
+ const importLine = `import QuestionSummary from '${importPath}';`;
+
+ if (content.includes("QuestionSummary")) {
+ return content; // Already imported
+ }
+
+ // Find the last import statement and insert after it
+ const importRegex = /^(import .+)$/gm;
+ const imports = [...content.matchAll(importRegex)];
+ if (imports.length > 0) {
+ const lastImport = imports[imports.length - 1];
+ const insertPos = lastImport.index + lastImport[0].length;
+ return (
+ content.slice(0, insertPos) + "\n" + importLine + content.slice(insertPos)
+ );
+ }
+
+ return content; // No existing imports
+}
+
+/**
+ * Generate the QuestionSummary component JSX string.
+ */
+function generateQuestionSummary(qaRows, headline, datePublished) {
+ const qaEntries = qaRows
+ .map((row) => {
+ return ` { question: ${JSON.stringify(row.question)}, answer: ${JSON.stringify(row.answer)}, anchor: ${JSON.stringify(row.anchor)} },`;
+ })
+ .join("\n");
+
+ const props = [];
+ if (headline) props.push(` headline=${JSON.stringify(headline)}`);
+ if (datePublished)
+ props.push(` datePublished=${JSON.stringify(datePublished)}`);
+
+ return `<QuestionSummary\n${props.join("\n")}\n qa={[\n${qaEntries}\n ]}\n/>`;
+}
+
+async function main() {
+ const files = await collectMdxFiles(DOCS_DIR);
+ console.log(`Found ${files.length} MDX files to check.`);
+
+ let migrated = 0;
+ let skipped = 0;
+
+ for (const file of files) {
+ const content = readFileSync(file, "utf-8");
+ const tableData = extractQATable(content);
+
+ if (!tableData) {
+ skipped++;
+ continue;
+ }
+
+ const fm = extractFrontmatter(content);
+
+ // Generate the replacement component
+ const component = generateQuestionSummary(
+ tableData.rows,
+ fm.title,
+ fm.date,
+ );
+
+ // Replace the table with the component
+ let newContent =
+ content.slice(0, tableData.tableStart) +
+ component +
+ content.slice(tableData.tableEnd);
+
+ // Ensure the import exists
+ newContent = addImport(newContent, "@/components/QuestionSummary.astro");
+
+ writeFileSync(file, newContent, "utf-8");
+ const relPath = relative(DOCS_DIR, file);
+ console.log(` Migrated: ${relPath} (${tableData.rows.length} QA pairs)`);
+ migrated++;
+ }
+
+ console.log(`\nDone: ${migrated} migrated, ${skipped} skipped.`);
+}
+
+main().catch((err) => {
+ console.error("Error:", err);
+ process.exit(1);
+});