release: WRNexusJS 0.5.0
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user