Incognito-Wiki/scripts/lib/migration.mjs

75 lines
2.4 KiB
JavaScript

import { readFile } from 'node:fs/promises';
import { access } from 'node:fs/promises';
import { join } from 'node:path';
const SOURCE_COUNT = 29;
const DOCS_ROOT = 'src/content/docs/';
export async function loadManifest(path) {
return JSON.parse(await readFile(path, 'utf8'));
}
export function validateManifest(manifest) {
if (!Array.isArray(manifest?.entries)) {
throw new TypeError('Migration manifest entries must be an array.');
}
if (manifest.entries.length !== SOURCE_COUNT) {
throw new Error(`Migration manifest must contain exactly ${SOURCE_COUNT} entries.`);
}
const sources = new Set();
const destinations = new Set();
for (const entry of manifest.entries) {
if (!entry || typeof entry !== 'object') {
throw new TypeError('Each migration manifest entry must be an object.');
}
const { source, destination, mode } = entry;
if (typeof source !== 'string' || typeof destination !== 'string') {
throw new TypeError('Each migration manifest entry needs string source and destination paths.');
}
if (!['page', 'merge'].includes(mode)) {
throw new TypeError('Each migration manifest entry mode must be page or merge.');
}
if (source.startsWith('pages/wiki/')) {
throw new Error(`Generic DokuWiki source is excluded: ${source}`);
}
if (!(source === 'pages/start.txt' || source === 'pages/study.txt' || source.startsWith('pages/study/'))) {
throw new Error(`Source is outside the selected migration scope: ${source}`);
}
if (!destination.startsWith(DOCS_ROOT)) {
throw new Error(`Destination is outside ${DOCS_ROOT}: ${destination}`);
}
if (sources.has(source)) {
throw new Error(`Duplicate migration source: ${source}`);
}
if (destinations.has(destination) && mode !== 'merge') {
throw new Error(`Duplicate destination requires a later merge entry: ${destination}`);
}
sources.add(source);
destinations.add(destination);
}
return { entries: manifest.entries };
}
export async function checkDestinations(manifest, root) {
const destinations = [...new Set(manifest.entries.map(({ destination }) => destination))];
const missing = [];
for (const destination of destinations) {
try {
await access(join(root, destination));
} catch {
missing.push(destination);
}
}
return {
total: manifest.entries.length,
missing,
};
}