Files
ST-Bionic-Memory-Ecology/scripts/check-syntax.mjs
youzini c7cc1bc4da fix: repair invalid ESM syntax that broke plugin load
Three syntax errors from earlier refactors broke index.js module load,
removing the wand menu entry and floating panel:
- arrow function pasted into an import block (line 213, cloneRuntimeDebugValue)
- arrow function pasted into an import block (line 451, shouldRunRecallForTransaction)
- missing closing brace on applyGraphLoadState (Phase 5f extraction)

Root cause the guard missed it: package.json has no type:module, so
node --check parsed .js as CommonJS/script and accepted invalid ESM.
check-syntax now pipes each file through node --check --input-type=module
so syntax validation matches how the browser actually loads these modules.
2026-05-31 19:12:30 +00:00

114 lines
3.2 KiB
JavaScript

import { readdir, readFile, stat } from "node:fs/promises";
import path from "node:path";
import { spawn } from "node:child_process";
const SOURCE_ROOTS = [
"index.js",
"ena-planner",
"graph",
"host",
"llm",
"maintenance",
"prompting",
"retrieval",
"runtime",
"scripts",
"sync",
"ui",
"vector",
"vendor/wasm",
"native",
];
async function collectFiles(targetPath) {
const absolutePath = path.resolve(process.cwd(), targetPath);
const fileStat = await stat(absolutePath);
if (fileStat.isFile()) {
return [absolutePath];
}
const files = [];
const entries = await readdir(absolutePath, { withFileTypes: true });
for (const entry of entries) {
const nextRelative = path.join(targetPath, entry.name);
if (entry.isDirectory()) {
files.push(...(await collectFiles(nextRelative)));
continue;
}
if (entry.isFile() && /\.(js|mjs)$/.test(entry.name)) {
files.push(path.resolve(process.cwd(), nextRelative));
}
}
return files;
}
function toPosixPath(filePath) {
return path.relative(process.cwd(), filePath).split(path.sep).join("/");
}
async function runNodeCheck(filePath) {
// Force ES module parsing. This repo's `.js` files are ESM (import/export,
// and the browser loads them as modules), but package.json has no
// `"type": "module"`, so `node --check file.js` parses them as CommonJS/script
// and silently accepts invalid ESM syntax (e.g. an arrow function pasted inside
// an `import { ... }` block — which shipped a broken index.js once). Node only
// allows `--input-type=module` with stdin input, not a file argument, so we
// pipe the file content through stdin. `--check` validates syntax only and does
// not resolve imports, so no module-resolution hook is needed here.
const source = await readFile(filePath, "utf8");
return await new Promise((resolve, reject) => {
const child = spawn(
process.execPath,
["--check", "--input-type=module", "-"],
{
cwd: process.cwd(),
stdio: ["pipe", "inherit", "inherit"],
windowsHide: true,
},
);
child.on("error", reject);
child.on("exit", (code, signal) => {
if (signal) {
reject(new Error(`${filePath} terminated by signal ${signal}`));
return;
}
if (code !== 0) {
reject(new Error(`${filePath} exited with code ${code}`));
return;
}
resolve();
});
child.stdin.on("error", reject);
child.stdin.end(source);
});
}
async function main() {
const files = [];
for (const root of SOURCE_ROOTS) {
files.push(...(await collectFiles(root)));
}
const uniqueFiles = Array.from(new Set(files)).sort((left, right) =>
toPosixPath(left).localeCompare(toPosixPath(right), "en"),
);
console.log(`[ST-BME][check] syntax-checking ${uniqueFiles.length} files`);
for (const filePath of uniqueFiles) {
console.log(`[ST-BME][check] -> ${toPosixPath(filePath)}`);
await runNodeCheck(filePath);
}
console.log("[ST-BME][check] syntax checks passed");
}
main().catch((error) => {
console.error(
"[ST-BME][check] failed:",
error instanceof Error ? error.message : String(error),
);
process.exitCode = 1;
});