release: WRNexusJS 0.8.3
Quality / quality (ubuntu-latest) (push) Failing after 12m9s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-03 19:47:30 +05:30
parent e8f630f12d
commit 4cebacadfe
156 changed files with 2608 additions and 473 deletions
+46 -5
View File
@@ -1,3 +1,4 @@
import { createHash } from "node:crypto";
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
import { createRequire } from "node:module";
import { dirname, join, relative, resolve, sep } from "node:path";
@@ -7,6 +8,15 @@ import { fileURLToPath } from "node:url";
const require = createRequire(import.meta.url);
const here = dirname(fileURLToPath(import.meta.url));
const root = resolve(here, "..");
const scriptPath = fileURLToPath(import.meta.url);
function normalizeText(value) {
return value.replace(/\r\n?/g, "\n");
}
function sha256(value) {
return createHash("sha256").update(value).digest("hex");
}
function loadTypeScript() {
const candidates = [process.env.TYPESCRIPT_PATH, "typescript"].filter(Boolean);
@@ -44,9 +54,15 @@ const sourceFiles = [
...walk(join(root, "packages", "compiler", "src")),
].sort();
const sourceHashBuilder = createHash("sha256");
const modules = [];
for (const file of sourceFiles) {
const source = readFileSync(file, "utf8");
const id = moduleId(file);
const source = normalizeText(readFileSync(file, "utf8"));
sourceHashBuilder.update(id);
sourceHashBuilder.update("\0");
sourceHashBuilder.update(source);
sourceHashBuilder.update("\0");
const result = ts.transpileModule(source, {
fileName: file,
reportDiagnostics: true,
@@ -60,6 +76,7 @@ for (const file of sourceFiles) {
inlineSourceMap: false,
removeComments: false,
rewriteRelativeImportExtensions: true,
newLine: ts.NewLineKind.LineFeed,
},
});
const diagnostics = result.diagnostics ?? [];
@@ -72,12 +89,18 @@ for (const file of sourceFiles) {
throw new Error(message);
}
modules.push(
`${JSON.stringify(moduleId(file))}: function (module, exports, require, __filename, __dirname) {\n${result.outputText}\n}`,
`${JSON.stringify(id)}: function (module, exports, require, __filename, __dirname) {\n${normalizeText(result.outputText)}\n}`,
);
}
const sourceHash = sourceHashBuilder.digest("hex");
const generatorHash = sha256(normalizeText(readFileSync(scriptPath, "utf8")));
const output = `"use strict";
// Generated by scripts/build-editor-compiler.mjs. Do not edit directly.
// WRN editor compiler source hash: ${sourceHash}
// WRN editor compiler generator hash: ${generatorHash}
// Generated with TypeScript: ${ts.version}
const __nodeRequire = require;
const __path = __nodeRequire("node:path");
const __modules = {
@@ -129,17 +152,35 @@ module.exports = __load("packages/compiler/src/index.ts");
const destination = join(root, "editors", "vscode", "src", "compiler.cjs");
if (process.argv.includes("--check")) {
if (!existsSync(destination) || readFileSync(destination, "utf8") !== output) {
const existing = existsSync(destination) ? normalizeText(readFileSync(destination, "utf8")) : "";
const embeddedSourceHash = existing.match(
/^\/\/ WRN editor compiler source hash: ([a-f0-9]{64})$/m,
)?.[1];
const embeddedGeneratorHash = existing.match(
/^\/\/ WRN editor compiler generator hash: ([a-f0-9]{64})$/m,
)?.[1];
const embeddedTypeScriptVersion = existing.match(/^\/\/ Generated with TypeScript: (.+)$/m)?.[1];
const hashesMatch = embeddedSourceHash === sourceHash && embeddedGeneratorHash === generatorHash;
const exactMatch = existing === normalizeText(output);
const compatibleCompilerDifference =
hashesMatch && Boolean(embeddedTypeScriptVersion) && embeddedTypeScriptVersion !== ts.version;
if (!hashesMatch || (!exactMatch && !compatibleCompilerDifference)) {
throw new Error(
`${relative(root, destination)} is stale. Run bun run --cwd editors/vscode build and commit the result.`,
);
}
const versionDetail = exactMatch
? `TypeScript ${ts.version}`
: `source-compatible output generated with TypeScript ${embeddedTypeScriptVersion}; current TypeScript is ${ts.version}`;
process.stdout.write(
`Verified ${relative(root, destination)} matches ${sourceFiles.length} TypeScript modules.\n`,
`Verified ${relative(root, destination)} matches ${sourceFiles.length} TypeScript modules (${versionDetail}).\n`,
);
} else {
writeFileSync(destination, output, "utf8");
process.stdout.write(
`Built ${relative(root, destination)} from ${sourceFiles.length} TypeScript modules.\n`,
`Built ${relative(root, destination)} from ${sourceFiles.length} TypeScript modules with TypeScript ${ts.version}.\n`,
);
}
+123
View File
@@ -0,0 +1,123 @@
#!/usr/bin/env node
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
import { join, relative, resolve, sep } from "node:path";
import console from "node:console";
import process from "node:process";
import { URL, fileURLToPath } from "node:url";
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
const packagesDir = join(root, "packages");
const BUILT_INS = new Set([
"Async",
"Loading",
"Success",
"Error",
"Static",
"Dynamic",
"Portal",
"Transition",
"Component",
"KeepAlive",
"Slot",
]);
const IGNORED_DIRECTORIES = new Set([
"node_modules",
"dist",
"coverage",
".git",
".wrnexus",
".publish",
]);
function walk(directory, extension, output = []) {
if (!existsSync(directory)) return output;
for (const name of readdirSync(directory)) {
if (IGNORED_DIRECTORIES.has(name)) continue;
const file = join(directory, name);
const stat = statSync(file);
if (stat.isDirectory()) walk(file, extension, output);
else if (file.endsWith(extension)) output.push(file);
}
return output;
}
const packageManifests = new Map();
for (const name of readdirSync(packagesDir)) {
const manifestPath = join(packagesDir, name, "package.json");
if (!existsSync(manifestPath)) continue;
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
packageManifests.set(name, {
name: manifest.name,
dependencies: new Set([
...Object.keys(manifest.dependencies ?? {}),
...Object.keys(manifest.peerDependencies ?? {}),
...Object.keys(manifest.optionalDependencies ?? {}),
]),
});
}
const files = walk(packagesDir, ".wrn");
const declared = new Map();
for (const file of files) {
const source = readFileSync(file, "utf8");
const match = /\b(?:component|layout|page)\s+([A-Z][A-Za-z0-9_$]*)\b/.exec(source);
if (match) declared.set(match[1], file);
}
const failures = [];
for (const file of files) {
const source = readFileSync(file, "utf8");
const packageName = relative(packagesDir, file).split(sep)[0];
const manifest = packageManifests.get(packageName);
for (const match of source.matchAll(/\bfrom\s+["'](@wrnexus\/[^"']+)["']/g)) {
const dependency = match[1].split("/").slice(0, 2).join("/");
if (dependency === manifest?.name || manifest?.dependencies.has(dependency)) continue;
failures.push(
`${relative(root, file).replace(/\\/g, "/")}: ${dependency} must be declared as a package dependency`,
);
}
const imported = new Set();
for (const match of source.matchAll(
/^\s*import\s+([A-Z][A-Za-z0-9_$]*)\s+from\s+["'][^"']+["']/gm,
)) {
imported.add(match[1]);
}
for (const match of source.matchAll(/^\s*import\s*\{([^}]+)\}\s*from\s*["'][^"']+["']/gm)) {
for (const part of match[1].split(",")) {
const binding = part
.trim()
.replace(/^type\s+/, "")
.split(/\s+as\s+/)
.pop()
?.trim();
if (binding) imported.add(binding);
}
}
const own = /\b(?:component|layout|page)\s+([A-Z][A-Za-z0-9_$]*)\b/.exec(source)?.[1];
const used = new Set([...source.matchAll(/<([A-Z][A-Za-z0-9_$]*)\b/g)].map((match) => match[1]));
for (const name of used) {
if (name === own || BUILT_INS.has(name) || !declared.has(name) || imported.has(name)) continue;
failures.push(
`${relative(root, file).replace(/\\/g, "/")}: <${name}> requires an explicit import`,
);
}
const layoutSymbol = /\blayout\s*=\s*([A-Z][A-Za-z0-9_$]*)\b/.exec(source)?.[1];
if (
layoutSymbol &&
layoutSymbol !== own &&
declared.has(layoutSymbol) &&
!imported.has(layoutSymbol)
) {
failures.push(
`${relative(root, file).replace(/\\/g, "/")}: layout ${layoutSymbol} requires an explicit import`,
);
}
}
if (failures.length) {
console.error(`WRN-IMPORT-PACKAGE: ${failures.length} package component import error(s):`);
for (const failure of failures) console.error(`- ${failure}`);
process.exit(1);
}
console.log(`✓ Package component imports are explicit (${files.length} WRN files checked)`);
+6
View File
@@ -230,6 +230,9 @@ function prepare(all: PackageInfo[], version: string) {
run(process.execPath, ["run", "sbom"]);
run(process.execPath, ["run", "benchmark:framework"]);
run(process.execPath, ["run", "check"]);
run(process.execPath, ["run", "check:editor-compiler"]);
run(process.execPath, ["run", "check:editor-language-server"]);
run(process.execPath, ["run", "check:editor-extension"]);
run(process.execPath, ["run", "scripts/publish-packages.ts"]);
run(process.execPath, ["run", "test:staged-consumers"]);
validateStaging(all, version);
@@ -305,6 +308,9 @@ function publish(all: PackageInfo[], version: string) {
run(process.execPath, ["run", "check:workspace"]);
run(process.execPath, ["run", "validate:0.8"]);
run(process.execPath, ["run", "security:framework"]);
run(process.execPath, ["run", "check:editor-compiler"]);
run(process.execPath, ["run", "check:editor-language-server"]);
run(process.execPath, ["run", "check:editor-extension"]);
requireCleanAndPushed(root, "Framework");
requireCleanAndPushed(docsRoot, "Docs");
+14 -1
View File
@@ -62,6 +62,16 @@ check(
`all framework packages use ${releaseVersion}`,
packageManifests.every((manifest) => manifest.version === releaseVersion),
);
const bunLockPackageVersions = [
...text("bun.lock").matchAll(/^ {4}"packages\/[^"\r\n]+": \{\r?\n([\s\S]*?)^ {4}\},?$/gm),
].map((match) => /^ {6}"version": "([^"]+)"/m.exec(match[1])?.[1]);
check(
`bun.lock framework workspaces use ${releaseVersion}`,
bunLockPackageVersions.length === packageManifests.length &&
bunLockPackageVersions.every((version) => version === releaseVersion),
);
const editorManifest = JSON.parse(text("editors/vscode/package.json"));
check(`VS Code extension uses ${releaseVersion}`, editorManifest.version === releaseVersion);
check(
"standalone realtime package exists",
existsSync(join(root, "packages/realtime/src/index.ts")) &&
@@ -172,7 +182,10 @@ check(
has("packages/validation/src/index.ts", "TValues extends readonly [string, ...string[]]") &&
has("packages/validation/test/helpers.test.ts", "const schema: ObjectSchema<ContactInput>"),
);
check("0.8 migration exists", has("packages/cli/src/update.ts", 'id: "0.8.0-01-package-kits"'));
check(
`current ${releaseVersion} migration exists`,
has("packages/cli/src/update.ts", `version: ${JSON.stringify(releaseVersion)}`),
);
check(
"package kit audit exists",
existsSync(join(root, "scripts/audit-package-kits.mjs")) &&