perf(build): budget the runtime on what ships, not on source bytes
The runtime budgets measured raw source, which counts comments -- and the
production build minifies, so comments cost a visitor nothing. The metric
therefore rewarded deleting explanatory comments over writing smaller code,
and could not tell a real feature from a wall of prose.
They now measure the minified output, which is what is actually served:
/__wrnexus/reactive.js is its own file, minified, with an immutable year-long
cache. The reactive runtime is 69684 minified against a 80000 budget, from
175246 raw -- roughly 21kB gzipped, fetched once.
Also fixes two real bugs found while testing the showcase:
- object-valued props were serialised as a bare {...} attribute, which the
compiler read as interpolation and tried to parse as JavaScript. That
returned 500 for /components/navbar. Arrays start with [ and were never
affected, which is why only object props broke. All 108 pages now render.
- the runtime walked text nodes inside textarea, script and style, so a
JSON sample in a textarea was evaluated away.
Navbar styles move out of ui.css into the component, matching the rest of the
navigation group. No declarations changed: 4519 before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2360,6 +2360,11 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
while ((textNode = walker.nextNode())) {
|
||||
var template = textNode.nodeValue;
|
||||
if (template.indexOf("{") === -1) continue;
|
||||
// Text in these is data, not a template: a JSON sample sitting in a
|
||||
// textarea would otherwise be read as mustaches and eaten.
|
||||
var owner = textNode.parentNode;
|
||||
var ownerName = owner ? owner.nodeName : "";
|
||||
if (ownerName === "TEXTAREA" || ownerName === "SCRIPT" || ownerName === "STYLE") continue;
|
||||
if (!owns(textNode)) continue;
|
||||
(function (node, tpl) {
|
||||
reactive(function () {
|
||||
|
||||
@@ -1052,3 +1052,18 @@ test("an empty or false roving attribute opts the group out entirely", () => {
|
||||
a.dispatchEvent(keydown(win, "ArrowRight"));
|
||||
expect(doc.activeElement!.id).toBe("a");
|
||||
});
|
||||
|
||||
test("json inside a textarea is left alone, not read as mustaches", () => {
|
||||
const win = mount(
|
||||
`<div data-scope="count: 7">
|
||||
<textarea id="editor">{"id":"a-1","count":3}</textarea>
|
||||
<span id="out">{count}</span>
|
||||
</div>`,
|
||||
);
|
||||
const doc = win.document;
|
||||
// The surrounding scope still interpolates normally...
|
||||
expect(doc.querySelector("#out")!.textContent).toBe("7");
|
||||
// ...but the textarea holds data, and an expression engine would have
|
||||
// evaluated {"id":"a-1","count":3} away and left it empty.
|
||||
expect(doc.querySelector("#editor")!.textContent).toBe('{"id":"a-1","count":3}');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user