release: WRNexusJS 0.5.0

This commit is contained in:
2026-07-29 12:51:10 +05:30
parent 76c768099d
commit 6afe32f63f
456 changed files with 40879 additions and 8850 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.4.0",
"version": "0.5.0",
"type": "module",
"main": "src/index.ts",
"exports": {
+62
View File
@@ -170,6 +170,45 @@ export const REACTIVE_RUNTIME = String.raw`
var start = 0;
var depth = 0;
var quote = "";
function continuesOnNextLine(index) {
var previousIndex = index - 1;
var nextIndex = index + 1;
while (
previousIndex >= start &&
/\s/.test(input[previousIndex])
) {
previousIndex--;
}
while (
nextIndex < input.length &&
/\s/.test(input[nextIndex])
) {
nextIndex++;
}
var previous =
previousIndex >= start
? input[previousIndex]
: "";
var next =
nextIndex < input.length
? input[nextIndex]
: "";
// Newlines are formatting whitespace while either side is an
// incomplete expression. This covers multiline assignments and
// ternaries emitted by formatted .wrn component functions.
return (
/[=+\-*/%?:,.!&|<>]/.test(
previous,
) ||
/[.?+:*/%&|<>=]/.test(next)
);
}
for (var i = 0; i < input.length; i++) {
var ch = input[i];
if (quote) {
@@ -191,6 +230,13 @@ export const REACTIVE_RUNTIME = String.raw`
) &&
depth === 0
) {
if (
ch !== ";" &&
continuesOnNextLine(i)
) {
continue;
}
var nextIndex = i + 1;
while (
@@ -409,6 +455,19 @@ export const REACTIVE_RUNTIME = String.raw`
return;
}
if (
lowerName.startsWith("data-") ||
lowerName.startsWith("aria-")
) {
if (value == null) {
node.removeAttribute(name);
} else {
node.setAttribute(name, String(value));
}
return;
}
if (value === false || value == null) {
node.removeAttribute(name);
} else {
@@ -646,6 +705,7 @@ export const REACTIVE_RUNTIME = String.raw`
if (name === "console") return console;
if (name === "Array") return Array;
if (name === "Number") return Number;
if (name === "String") return String;
if (name === "Math") return Math;
if (name === "JSON") return JSON;
if (name === "Date") return Date;
@@ -655,6 +715,8 @@ export const REACTIVE_RUNTIME = String.raw`
if (name === "navigator") return window.navigator;
if (name === "setTimeout") return window.setTimeout.bind(window);
if (name === "clearTimeout") return window.clearTimeout.bind(window);
if (name === "setInterval") return window.setInterval.bind(window);
if (name === "clearInterval") return window.clearInterval.bind(window);
if (name === "requestAnimationFrame") return window.requestAnimationFrame.bind(window);
if (name === "cancelAnimationFrame") return window.cancelAnimationFrame.bind(window);
return undefined;
+63
View File
@@ -41,6 +41,45 @@ test("@event (data-on-click) mutates a signal and re-renders", () => {
expect(btn.textContent).toBe("2");
});
test("component functions support formatted multiline assignments and ternaries", () => {
const behavior = Buffer.from(
JSON.stringify({
functions: `
function increment(event, nextValue) {
nextValue =
Number(count) +
(event.shiftKey ? 10 : 1)
count = nextValue
}
function normalize(useMinimum) {
count = useMinimum
? 0
: Number(count)
}
`,
watches: [],
lifecycle: {},
}),
).toString("base64");
const win = mount(
`<div data-scope="count: 0" data-wrn-behavior="${behavior}">
<button id="increment" data-on-click="increment(event)">{count}</button>
<button id="normalize" data-on-click="normalize(true)">normalize</button>
</div>`,
);
const increment = win.document.getElementById("increment") as unknown as HTMLElement;
increment.click();
expect(increment.textContent).toBe("1");
increment.dispatchEvent(new win.MouseEvent("click", { shiftKey: true }) as unknown as Event);
expect(increment.textContent).toBe("11");
(win.document.getElementById("normalize") as unknown as HTMLElement).click();
expect(increment.textContent).toBe("0");
});
test("declared component events emit through the generic $emit function", () => {
const win = mount(
`<div data-scope="" data-wrn-events="complete">
@@ -148,6 +187,16 @@ test("expression evaluator: member access, ternary, comparison, calls", () => {
expect(win.document.getElementById("c")!.textContent).toBe("3");
});
test("expression evaluator exposes safe primitive conversion globals", () => {
const win = mount(
`<div data-scope="count: 12">
<span data-text="String(count)"></span>
</div>`,
);
expect(win.document.querySelector("span")!.textContent).toBe("12");
});
test("logical expressions consume their right-hand side when the result short-circuits", () => {
const win = mount(
`<div data-scope="leftFalse: false, leftTrue: true">
@@ -189,6 +238,20 @@ test("data-show toggles visibility on a reactive expression (tabs pattern)", ()
expect(win.document.getElementById("b")!.getAttribute("data-show")).toBe("true");
});
test("reactive data attributes preserve explicit boolean strings", () => {
const win = mount(
`<div data-scope="ready: true">
<section
id="carousel-layout"
data-show="true"
data-wrn-bind-0='["data-show","{ready}"]'
>Slides</section>
</div>`,
);
expect(win.document.getElementById("carousel-layout")!.getAttribute("data-show")).toBe("true");
});
test("reactive attribute bindings update input and accessibility attributes", () => {
const win = mount(
`<div data-scope="show: false">