release: WRNexusJS 0.3.0
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { dirname, join, relative, resolve, sep } from "node:path";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(here, "..");
|
||||
|
||||
function loadTypeScript() {
|
||||
const candidates = [process.env.TYPESCRIPT_PATH, "typescript"].filter(Boolean);
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
return require(candidate);
|
||||
} catch {
|
||||
// Try the next local or explicitly supplied compiler path.
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
"TypeScript is required to build the editor compiler. Run `bun install` or set TYPESCRIPT_PATH.",
|
||||
);
|
||||
}
|
||||
|
||||
const ts = loadTypeScript();
|
||||
|
||||
function walk(dir) {
|
||||
const files = [];
|
||||
for (const entry of readdirSync(dir)) {
|
||||
const path = join(dir, entry);
|
||||
const stat = statSync(path);
|
||||
if (stat.isDirectory()) files.push(...walk(path));
|
||||
else if (stat.isFile() && path.endsWith(".ts") && !path.endsWith(".test.ts")) files.push(path);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function moduleId(file) {
|
||||
return relative(root, file).split(sep).join("/");
|
||||
}
|
||||
|
||||
const sourceFiles = [
|
||||
...walk(join(root, "packages", "syntax", "src")),
|
||||
...walk(join(root, "packages", "compiler", "src")),
|
||||
].sort();
|
||||
|
||||
const modules = [];
|
||||
for (const file of sourceFiles) {
|
||||
const source = readFileSync(file, "utf8");
|
||||
const result = ts.transpileModule(source, {
|
||||
fileName: file,
|
||||
reportDiagnostics: true,
|
||||
compilerOptions: {
|
||||
target: ts.ScriptTarget.ES2022,
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
moduleResolution: ts.ModuleResolutionKind.Node10,
|
||||
esModuleInterop: true,
|
||||
skipLibCheck: true,
|
||||
sourceMap: false,
|
||||
inlineSourceMap: false,
|
||||
removeComments: false,
|
||||
rewriteRelativeImportExtensions: true,
|
||||
},
|
||||
});
|
||||
const diagnostics = result.diagnostics ?? [];
|
||||
if (diagnostics.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) {
|
||||
const message = ts.formatDiagnosticsWithColorAndContext(diagnostics, {
|
||||
getCanonicalFileName: (name) => name,
|
||||
getCurrentDirectory: () => root,
|
||||
getNewLine: () => "\n",
|
||||
});
|
||||
throw new Error(message);
|
||||
}
|
||||
modules.push(
|
||||
`${JSON.stringify(moduleId(file))}: function (module, exports, require, __filename, __dirname) {\n${result.outputText}\n}`,
|
||||
);
|
||||
}
|
||||
|
||||
const output = `"use strict";
|
||||
// Generated by scripts/build-editor-compiler.mjs. Do not edit directly.
|
||||
const __nodeRequire = require;
|
||||
const __path = __nodeRequire("node:path");
|
||||
const __modules = {
|
||||
${modules.join(",\n")}
|
||||
};
|
||||
const __aliases = {
|
||||
"@wrnexus/syntax": "packages/syntax/src/index.ts",
|
||||
"@wrnexus/syntax/parser": "packages/syntax/src/parser.ts",
|
||||
"@wrnexus/syntax/tokenizer": "packages/syntax/src/tokenizer.ts",
|
||||
"@wrnexus/syntax/types": "packages/syntax/src/types.ts",
|
||||
"@wrnexus/syntax/diagnostics": "packages/syntax/src/diagnostics.ts",
|
||||
"@wrnexus/syntax/spec": "packages/syntax/src/spec.ts"
|
||||
};
|
||||
const __cache = Object.create(null);
|
||||
function __normalize(id) {
|
||||
const normalized = id.split("\\\\").join("/");
|
||||
return normalized.startsWith("./") ? normalized.slice(2) : normalized;
|
||||
}
|
||||
function __resolve(request, parent) {
|
||||
if (__aliases[request]) return __aliases[request];
|
||||
if (!request.startsWith(".")) return null;
|
||||
const base = __normalize(__path.posix.join(__path.posix.dirname(parent), request));
|
||||
const candidates = [
|
||||
base,
|
||||
base.endsWith(".js") ? base.slice(0, -3) + ".ts" : base,
|
||||
base.endsWith(".ts") ? base : base + ".ts",
|
||||
(base.endsWith("/") ? base.slice(0, -1) : base) + "/index.ts"
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (__modules[candidate]) return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function __load(id) {
|
||||
if (__cache[id]) return __cache[id].exports;
|
||||
const factory = __modules[id];
|
||||
if (!factory) throw new Error("WRN editor compiler module not found: " + id);
|
||||
const module = { exports: {} };
|
||||
__cache[id] = module;
|
||||
const localRequire = (request) => {
|
||||
const resolved = __resolve(request, id);
|
||||
return resolved ? __load(resolved) : __nodeRequire(request);
|
||||
};
|
||||
factory(module, module.exports, localRequire, id, __path.posix.dirname(id));
|
||||
return module.exports;
|
||||
}
|
||||
module.exports = __load("packages/compiler/src/index.ts");
|
||||
`;
|
||||
|
||||
const destination = join(root, "editors", "vscode", "src", "compiler.cjs");
|
||||
writeFileSync(destination, output, "utf8");
|
||||
process.stdout.write(
|
||||
`Built ${relative(root, destination)} from ${sourceFiles.length} TypeScript modules.\n`,
|
||||
);
|
||||
@@ -142,7 +142,7 @@ function publishManifest(m: Record<string, any>, version: string): Record<string
|
||||
main: m.main ? js(m.main) : "./dist/index.js",
|
||||
module: m.main ? js(m.main) : "./dist/index.js",
|
||||
types: m.main ? dts(m.main) : "./dist/index.d.ts",
|
||||
engines: { bun: ">=1.1.0" },
|
||||
engines: { bun: ">=1.3.0" },
|
||||
publishConfig: { registry: REGISTRY, access: ACCESS },
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
||||
import { join, relative } from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
const root = process.cwd();
|
||||
const VERSION = "0.3.0";
|
||||
const errors = [];
|
||||
const notes = [];
|
||||
const readJson = (file) => JSON.parse(readFileSync(file, "utf8"));
|
||||
|
||||
function walk(dir, name = "package.json") {
|
||||
const out = [];
|
||||
if (!existsSync(dir)) return out;
|
||||
for (const entry of readdirSync(dir)) {
|
||||
if (entry === "node_modules" || entry === ".git" || entry === "dist") continue;
|
||||
const path = join(dir, entry);
|
||||
const stat = statSync(path);
|
||||
if (stat.isDirectory()) out.push(...walk(path, name));
|
||||
else if (entry === name) out.push(path);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const packageFiles = [join(root, "package.json"), ...walk(join(root, "packages"))];
|
||||
const packages = new Map();
|
||||
for (const file of packageFiles) {
|
||||
const pkg = readJson(file);
|
||||
if (pkg.name) packages.set(pkg.name, { file, pkg });
|
||||
if (file.includes(`${join(root, "packages")}/`) && pkg.version !== VERSION) {
|
||||
errors.push(`${relative(root, file)} has version ${pkg.version ?? "<missing>"}`);
|
||||
}
|
||||
}
|
||||
if (readJson(join(root, "package.json")).version !== VERSION) {
|
||||
errors.push(`root package version is not ${VERSION}`);
|
||||
}
|
||||
|
||||
for (const { file, pkg } of packages.values()) {
|
||||
for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
|
||||
for (const [name, range] of Object.entries(pkg[field] ?? {})) {
|
||||
if (String(range).startsWith("workspace:") && !packages.has(name)) {
|
||||
errors.push(`${relative(root, file)} references missing workspace ${name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const required of [
|
||||
"packages/syntax/src/index.ts",
|
||||
"packages/plugin/src/index.ts",
|
||||
"scripts/build-editor-compiler.mjs",
|
||||
"docs/WRN-LANGUAGE-SPEC-1.0.md",
|
||||
"docs/ARCHITECTURE-0.3.md",
|
||||
"docs/UPGRADE-0.3.md",
|
||||
"docs/40-POINT-IMPLEMENTATION-0.3.md",
|
||||
"docs/TEST-CHECKLIST-0.3.md",
|
||||
"docs/AUDIT-0.3.md",
|
||||
]) {
|
||||
if (!existsSync(join(root, required))) errors.push(`missing ${required}`);
|
||||
}
|
||||
|
||||
const updateSource = readFileSync(join(root, "packages/cli/src/update.ts"), "utf8");
|
||||
const migrationPairs = [
|
||||
...updateSource.matchAll(/version:\s*"([^"]+)"[\s\S]*?id:\s*"([^"]+)"/g),
|
||||
].map((match) => ({ version: match[1], id: match[2] }));
|
||||
const ids = new Set();
|
||||
for (const migration of migrationPairs) {
|
||||
if (ids.has(migration.id)) errors.push(`duplicate migration id ${migration.id}`);
|
||||
ids.add(migration.id);
|
||||
}
|
||||
if (!migrationPairs.some((migration) => migration.version === VERSION)) {
|
||||
errors.push(`missing ${VERSION} updater migration`);
|
||||
}
|
||||
|
||||
for (const jsonFile of [
|
||||
"editors/vscode/package.json",
|
||||
"editors/vscode/package-lock.json",
|
||||
"editors/vscode/snippets/wrn.json",
|
||||
"editors/vscode/syntaxes/wrn.tmLanguage.json",
|
||||
]) {
|
||||
try {
|
||||
readJson(join(root, jsonFile));
|
||||
} catch (error) {
|
||||
errors.push(`${jsonFile} is invalid JSON: ${error instanceof Error ? error.message : error}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const lockText = readFileSync(join(root, "bun.lock"), "utf8").replace(/,\s*([}\]])/g, "$1");
|
||||
const lock = JSON.parse(lockText);
|
||||
for (const workspace of ["packages/syntax", "packages/plugin"]) {
|
||||
if (!lock.workspaces?.[workspace]) errors.push(`bun.lock missing ${workspace}`);
|
||||
}
|
||||
for (const name of ["@wrnexus/syntax", "@wrnexus/plugin"]) {
|
||||
if (!lock.packages?.[name]) errors.push(`bun.lock missing workspace link ${name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
errors.push(`bun.lock structure is invalid: ${error instanceof Error ? error.message : error}`);
|
||||
}
|
||||
|
||||
const compilerParser = readFileSync(join(root, "packages/compiler/src/parser.ts"), "utf8");
|
||||
if (!compilerParser.includes("@wrnexus/syntax")) {
|
||||
errors.push("compiler parser is not a compatibility re-export of @wrnexus/syntax");
|
||||
}
|
||||
|
||||
const tsconfig = readJson(join(root, "tsconfig.json"));
|
||||
for (const name of packages.keys()) {
|
||||
if (name.startsWith("@wrnexus/") && !tsconfig.compilerOptions?.paths?.[name]) {
|
||||
errors.push(`tsconfig paths missing ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
notes.push(`${packages.size} named packages checked`);
|
||||
notes.push(`${migrationPairs.length} migration entries checked`);
|
||||
notes.push("editor JSON and Bun workspace lock structure checked");
|
||||
|
||||
if (errors.length) {
|
||||
process.stderr.write(
|
||||
[
|
||||
`WRNexusJS ${VERSION} verification failed:`,
|
||||
...errors.map((error) => ` - ${error}`),
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
process.stdout.write(
|
||||
[
|
||||
`WRNexusJS ${VERSION} structural verification passed.`,
|
||||
...notes.map((note) => ` ✓ ${note}`),
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user