forked from bai/curriculum-project-hub
57 lines
2.4 KiB
JavaScript
57 lines
2.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { readdir, readFile, realpath } from "node:fs/promises";
|
|
import { dirname, relative, resolve, sep } from "node:path";
|
|
|
|
const rootArgument = process.argv[2];
|
|
if (!rootArgument) throw new Error("usage: build_legacy_project_manifest.mjs <legacy-workspaces-root>");
|
|
const root = await realpath(rootArgument);
|
|
const projectFiles = await findProjectFiles(root);
|
|
const seenIds = new Set();
|
|
const manifest = [];
|
|
for (const projectFile of projectFiles) {
|
|
const metadata = JSON.parse(await readFile(projectFile, "utf8"));
|
|
if (typeof metadata.id !== "string" || metadata.id.trim() === "") {
|
|
throw new Error(`project metadata has no id: ${projectFile}`);
|
|
}
|
|
if (seenIds.has(metadata.id)) throw new Error(`duplicate project id: ${metadata.id}`);
|
|
seenIds.add(metadata.id);
|
|
if (typeof metadata.name !== "string" || metadata.name.trim() === "") {
|
|
throw new Error(`project metadata has no name: ${projectFile}`);
|
|
}
|
|
const projectRoot = dirname(projectFile);
|
|
const sourceRelativePath = relative(root, projectRoot).split(sep).join("/");
|
|
const physicalFolderPath = dirname(sourceRelativePath) === "."
|
|
? []
|
|
: dirname(sourceRelativePath).split("/");
|
|
if (metadata.folderPath !== undefined && (
|
|
!Array.isArray(metadata.folderPath)
|
|
|| metadata.folderPath.some((part) => typeof part !== "string")
|
|
|| JSON.stringify(metadata.folderPath) !== JSON.stringify(physicalFolderPath)
|
|
)) {
|
|
process.stderr.write(`[legacy-manifest] stale metadata folderPath; using physical path: ${projectFile}\n`);
|
|
}
|
|
manifest.push({
|
|
legacyId: metadata.id,
|
|
name: metadata.name,
|
|
folderPath: physicalFolderPath,
|
|
sourceRelativePath,
|
|
});
|
|
}
|
|
manifest.sort((left, right) => left.sourceRelativePath.localeCompare(right.sourceRelativePath, "zh-CN"));
|
|
process.stdout.write(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
|
|
async function findProjectFiles(directory) {
|
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
const projectMetadata = entries.find((entry) => entry.isFile() && entry.name === "project.json");
|
|
if (projectMetadata !== undefined) return [resolve(directory, projectMetadata.name)];
|
|
const found = [];
|
|
for (const entry of entries) {
|
|
if (entry.name === ".trash") continue;
|
|
const path = resolve(directory, entry.name);
|
|
if (entry.isSymbolicLink()) throw new Error(`legacy manifest rejects symbolic link: ${path}`);
|
|
if (entry.isDirectory()) found.push(...await findProjectFiles(path));
|
|
}
|
|
return found;
|
|
}
|