release: WRNexusJS 0.6.0

This commit is contained in:
2026-08-01 01:09:58 +05:30
parent 3e565e8d03
commit 687d345882
502 changed files with 33038 additions and 11358 deletions
@@ -0,0 +1,38 @@
import { expect, test } from "bun:test";
import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
function outputBlocks(source: string): string[] {
const blocks: string[] = [];
for (const match of source.matchAll(/\boutputs\s*\{/g)) {
const brace = source.indexOf("{", match.index);
let depth = 0;
let quote = "";
for (let index = brace; index < source.length; index++) {
const char = source[index]!;
if (quote) {
if (char === "\\") index++;
else if (char === quote) quote = "";
continue;
}
if (char === '"' || char === "'" || char === "`") quote = char;
else if (char === "{") depth++;
else if (char === "}" && --depth === 0) {
blocks.push(source.slice(brace + 1, index));
break;
}
}
}
return blocks;
}
test("all UI output payload declarations are concrete", () => {
const root = join(import.meta.dir, "../components");
const offenders: string[] = [];
for (const file of readdirSync(root).filter((name) => name.endsWith(".wrn"))) {
for (const block of outputBlocks(readFileSync(join(root, file), "utf8"))) {
if (/\bunknown\b/.test(block)) offenders.push(file);
}
}
expect(offenders).toEqual([]);
});
@@ -1,6 +1,7 @@
import { describe, expect, test } from "bun:test";
import { readdir, readFile } from "node:fs/promises";
import { join } from "node:path";
import { parse } from "../../compiler/src/index.ts";
const componentsDir = join(import.meta.dir, "..", "components");
@@ -166,7 +167,13 @@ describe("@wrnexus/ui redesign component pack", () => {
for (const [name, tokens] of Object.entries(expectations)) {
const source = await readFile(join(componentsDir, `${name}.wrn`), "utf8");
for (const token of tokens) expect(source).toContain(token);
const ast = parse(source);
const defaults = new Map(ast.props.map((prop) => [prop.name, prop.default]));
for (const token of tokens) {
const match = /^([A-Za-z_$][\w$]*)\s*=\s*([\s\S]+)$/.exec(token);
expect(match).not.toBeNull();
expect(defaults.get(match![1]!)).toBe(match![2]!);
}
}
});
});
+22 -15
View File
@@ -1,7 +1,7 @@
import { expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { compileWireFile } from "../../compiler/src/index.ts";
import { compileWireFile, parse } from "../../compiler/src/index.ts";
import { mountHtml, renderComponent } from "../../test/src/index.ts";
import { uiComponentNames, uiComponentsDir, uiComponentPath, uiCss } from "../src/index.ts";
@@ -83,9 +83,11 @@ test("component reference contains only explicitly declared public events", () =
const componentPath = uiComponentPath(component.name);
const source = readFileSync(componentPath, "utf8");
const ast = parse(source);
const declaredEvents = [
...source.matchAll(/@event\s+([A-Za-z][A-Za-z0-9_]*)\s*=\s*function/g),
].map((match) => match[1]);
...ast.events.map((event) => event.name),
...ast.outputs.map((output) => output.name),
];
const expectedEvents = [...new Set(declaredEvents)];
@@ -178,15 +180,18 @@ test("navbar supports brands, nested menus, actions, utility slots, and public e
const source = readFileSync(uiComponentPath("Navbar"), "utf8");
expect(source).toContain('slot name="topbar"');
expect(source).toContain('slot name="actions"');
expect(source).toContain("brand = {}");
expect(source).toContain("actions = []");
expect(source).toContain("openOnHover = false");
expect(source).toContain('maxWidth = "full"');
const contract = parse(source);
const defaults = new Map(contract.props.map((prop) => [prop.name, prop.default]));
expect(defaults.get("brand")).toBe("{}");
expect(defaults.get("actions")).toBe("[]");
expect(defaults.get("openOnHover")).toBe("false");
expect(defaults.get("maxWidth")).toBe('"full"');
expect(source).toContain("item.children");
expect(source).toContain("child.children");
expect(source).toContain("wire-navbar__dropdown--{item.type || 'dropdown'}");
const outputs = new Set(contract.outputs.map((output) => output.name));
for (const event of ["toggle", "open", "close", "select", "action"]) {
expect(source).toContain(`@event ${event} = function`);
expect(outputs.has(event)).toBe(true);
}
});
@@ -214,8 +219,9 @@ test("footer supports typed entries, responsive columns, pre/post slots, and eve
expect(source).toMatch(/<slot\s+name="copyright-right"\s*>/);
expect(source).toContain('item.type === "header"');
expect(source).toContain("wire-footer--columns-{columns}");
expect(source).toContain("@event select = function");
expect(source).toContain("@event action = function");
const outputs = new Set(parse(source).outputs.map((output) => output.name));
expect(outputs.has("select")).toBe(true);
expect(outputs.has("action")).toBe(true);
});
test("navbar renders a brand, nested dropdowns, mega groups, and actions", async () => {
@@ -377,10 +383,10 @@ test("dropdown renders slotted account triggers, menu structure, and public even
});
expect(source).toContain('slot name="trigger"');
expect(source).toContain("@event select = function");
expect(parse(source).outputs.map((output) => output.name)).toContain("select");
expect(html).toContain("wire-dropdown__panel");
expect(html).toContain("Profile");
expect(html).toContain("wire-dropdown__item--danger");
expect(html).toContain('data-danger="true"');
});
test("footer renders header and link entries with the requested column count", async () => {
@@ -452,9 +458,10 @@ test("component boundaries have no default margins and expose the canonical clas
test("every component exposes universal color and size configuration", () => {
for (const name of uiComponentNames()) {
const source = readFileSync(uiComponentPath(name), "utf8");
expect(source).toMatch(/^\s+color\s*=/m);
expect(source).toMatch(/^\s+size\s*=/m);
const ast = parse(readFileSync(uiComponentPath(name), "utf8"));
const props = new Set(ast.props.map((prop) => prop.name));
expect(props.has("color")).toBe(true);
expect(props.has("size")).toBe(true);
}
});