release: WRNexusJS 0.2.44

This commit is contained in:
2026-07-15 19:41:42 +05:30
parent 0f915c3c98
commit 8a8328a308
57 changed files with 642 additions and 149 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.2.44",
"version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
+477 -54
View File
@@ -335,6 +335,59 @@ export const REACTIVE_RUNTIME = String.raw`
return run;
}
function renderAll() { renderers.forEach(function (f) { f(); }); }
function decodeLoopLocals(node) {
var owner =
node &&
node.closest &&
node.closest(
"[data-wrn-loop-locals]",
);
if (!owner) {
return {};
}
var encoded =
owner.getAttribute(
"data-wrn-loop-locals",
);
if (!encoded) {
return {};
}
try {
var binary =
window.atob(encoded);
var bytes =
new Uint8Array(
binary.length,
);
for (
var index = 0;
index < binary.length;
index++
) {
bytes[index] =
binary.charCodeAt(index);
}
return JSON.parse(
new TextDecoder(
"utf-8",
).decode(bytes),
);
} catch (error) {
console.error(
"[wrnexus] failed to decode loop locals",
error,
);
return {};
}
}
var behaviorFunctions = {};
var stateWatchers = {};
@@ -537,44 +590,338 @@ export const REACTIVE_RUNTIME = String.raw`
try { return String(itemEval(e)); } catch (err) { return ""; }
});
}
function hydrateItem(root, locals) {
function hydrateItem(
root,
locals,
) {
function localRead(name) {
return Object.prototype.hasOwnProperty.call(locals, name) ? locals[name] : peekScope(name);
}
function itemEval(expr) { return evaluateExpression(expr, localRead); }
if (
Object.prototype.hasOwnProperty.call(
locals,
name,
)
) {
return locals[name];
}
var els = [root];
if (root.querySelectorAll) Array.prototype.push.apply(els, root.querySelectorAll("*"));
els.forEach(function (n) {
if (n.nodeType !== 1) return;
Array.prototype.slice.call(n.attributes).forEach(function (attr) {
if (attr.name === "data-text") {
try { n.textContent = String(itemEval(attr.value)); } catch (e) { /* ignore */ }
} else if (attr.name.indexOf("data-on-") === 0) {
var evt = attr.name.slice("data-on-".length);
var stmt = attr.value;
n.addEventListener(evt, function () {
return readScope(name);
}
function itemEval(expression) {
return evaluateExpression(
expression,
localRead,
);
}
function applyItemTemplate(
template,
) {
return String(template).replace(
/\{\{\s*([^}]+?)\s*\}\}|\{([^{}]+)\}/g,
function (
_,
doubleExpression,
singleExpression,
) {
var expression = (
doubleExpression ||
singleExpression
).trim();
try {
var value =
itemEval(expression);
return value == null
? ""
: String(value);
} catch (_) {
return "";
}
},
);
}
var elements = [root];
if (root.querySelectorAll) {
Array.prototype.push.apply(
elements,
root.querySelectorAll("*"),
);
}
elements.forEach(function (node) {
if (
!node ||
node.nodeType !== 1
) {
return;
}
var attributes =
Array.prototype.slice.call(
node.attributes,
);
attributes.forEach(function (
attribute,
) {
/*
* data-text must be handled using itemEval(),
* because item/index only exist in loop locals.
*/
if (
attribute.name === "data-text"
) {
try {
var textValue =
itemEval(attribute.value);
node.textContent =
textValue == null
? ""
: String(textValue);
} catch (error) {
console.error(
"[wrnexus] data-for text binding failed for '" +
attribute.value +
"'",
error,
);
node.textContent = "";
}
return;
}
/*
* Conditional classes:
* data-wrn-class-*='["class","expression"]'
*/
if (
attribute.name.indexOf(
"data-wrn-class-",
) === 0
) {
var classBinding;
try {
classBinding = JSON.parse(
attribute.value,
);
} catch (_) {
return;
}
if (
!classBinding ||
classBinding.length !== 2
) {
return;
}
var className =
classBinding[0];
var classExpression =
classBinding[1];
var classEnabled = false;
try {
classEnabled = !!itemEval(
classExpression,
);
} catch (_) {
classEnabled = false;
}
node.classList.toggle(
className,
classEnabled,
);
return;
}
/*
* Reactive ordinary attributes:
* data-wrn-bind-*='["aria-expanded","{isOpen(index)}"]'
*/
if (
attribute.name.indexOf(
"data-wrn-bind-",
) === 0
) {
var binding;
try {
binding = JSON.parse(
attribute.value,
);
} catch (_) {
return;
}
if (
!binding ||
binding.length !== 2
) {
return;
}
var attributeName =
binding[0];
var attributeTemplate =
binding[1];
var exact =
/^\{([^{}]+)\}$/.exec(
attributeTemplate,
);
var rawValue;
if (exact) {
try {
splitTopLevel(stmt, ";").forEach(function (part) {
runStatement(part, itemEval, localRead, function (name, value) {
if (Object.prototype.hasOwnProperty.call(locals, name)) locals[name] = value;
else if (!signals[name]) { signals[name] = signal(value); renderAll(); }
else signals[name].set(value);
});
rawValue = itemEval(
exact[1].trim(),
);
} catch (_) {
return;
}
} else {
rawValue =
attributeTemplate.replace(
/\{([^{}]+)\}/g,
function (
_,
expression,
) {
try {
var value =
itemEval(
expression.trim(),
);
return value == null
? ""
: String(value);
} catch (_) {
return "";
}
},
);
}
applyReactiveAttribute(
node,
attributeName,
rawValue,
);
return;
}
/*
* Event handlers inside data-for.
*/
if (
attribute.name.indexOf(
"data-on-",
) === 0
) {
var eventName =
attribute.name.slice(
"data-on-".length,
);
var statement =
attribute.value;
node.addEventListener(
eventName,
function (event) {
var eventLocals = {};
Object.keys(
locals,
).forEach(function (key) {
eventLocals[key] =
locals[key];
});
} catch (e) { console.error("[wrnexus] data-for handler error", e); }
});
} else if (attr.value.indexOf("{") !== -1) {
attr.value = fillMustache(attr.value, itemEval);
eventLocals.event = event;
eventLocals.$event = event;
try {
runStmt(
statement,
eventLocals,
);
} catch (error) {
console.error(
"[wrnexus] data-for handler error in '" +
statement +
"'",
error,
);
}
},
);
return;
}
/*
* Static attributes containing interpolation.
*/
if (
attribute.value &&
attribute.value.indexOf("{") !== -1
) {
node.setAttribute(
attribute.name,
applyItemTemplate(
attribute.value,
),
);
}
});
});
var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
var tn;
while ((tn = walker.nextNode())) {
if (tn.nodeValue.indexOf("{") === -1) continue;
tn.nodeValue = fillMustache(tn.nodeValue, itemEval);
/*
* Text interpolation inside loop content:
*
* <span>{item.title}</span>
*/
var walker =
document.createTreeWalker(
root,
NodeFilter.SHOW_TEXT,
null,
);
var textNode;
while (
(textNode = walker.nextNode())
) {
var template =
textNode.nodeValue || "";
if (
template.indexOf("{") === -1
) {
continue;
}
textNode.nodeValue =
applyItemTemplate(template);
}
}
@@ -658,7 +1005,10 @@ export const REACTIVE_RUNTIME = String.raw`
var enabled = false;
try {
enabled = !!evalExpr(expression);
enabled = !!evalExpr(
expression,
decodeLoopLocals(node),
);
} catch (e) {
enabled = false;
}
@@ -671,32 +1021,97 @@ export const REACTIVE_RUNTIME = String.raw`
// Reactive ordinary attributes emitted by the compiler. Each marker stores
// [attributeName, originalTemplate], preserving an SSR value while allowing
// state changes to update type, aria-*, class, href, and other attributes.
var bindNodes = [el].concat(Array.prototype.slice.call(el.querySelectorAll("*")));
var bindNodes = [el].concat(
Array.prototype.slice.call(
el.querySelectorAll("*"),
),
);
bindNodes.forEach(function (node) {
if (!owns(node)) return;
Array.prototype.slice.call(node.attributes).forEach(function (marker) {
if (marker.name.indexOf("data-wrn-bind-") !== 0) return;
var binding;
try { binding = JSON.parse(marker.value); } catch (e) { return; }
if (!binding || binding.length !== 2) return;
var name = binding[0];
var template = binding[1];
reactive(function () {
var exact = /^\{([^{}]+)\}$/.exec(template);
var raw;
if (exact) {
try { raw = evalExpr(exact[1].trim()); } catch (e) { return; }
} else {
raw = template.replace(/\{([^{}]+)\}/g, function (_, expr) {
try {
var value = evalExpr(expr.trim());
return value == null ? "" : String(value);
} catch (e) { return ""; }
});
Array.prototype.slice
.call(node.attributes)
.forEach(function (marker) {
if (
marker.name.indexOf(
"data-wrn-bind-",
) !== 0
) {
return;
}
applyReactiveAttribute(node, name, raw);
var binding;
try {
binding = JSON.parse(
marker.value,
);
} catch (error) {
return;
}
if (
!binding ||
binding.length !== 2
) {
return;
}
var name = binding[0];
var template = binding[1];
reactive(function () {
var locals =
decodeLoopLocals(node);
var exact =
/^\{([^{}]+)\}$/.exec(
template,
);
var raw;
if (exact) {
try {
raw = evalExpr(
exact[1].trim(),
locals,
);
} catch (error) {
return;
}
} else {
raw = template.replace(
/\{([^{}]+)\}/g,
function (
_,
expression,
) {
try {
var value =
evalExpr(
expression.trim(),
locals,
);
return value == null
? ""
: String(value);
} catch (error) {
return "";
}
},
);
}
applyReactiveAttribute(
node,
name,
raw,
);
});
});
});
});
// {{expr}} / {expr} interpolation in text nodes
@@ -737,7 +1152,15 @@ export const REACTIVE_RUNTIME = String.raw`
var stmt = attr.value;
var listener = function (event) {
try { runStmt(stmt, { event: event, $event: event }); } catch (error) {
try {
var locals =
decodeLoopLocals(node);
locals.event = event;
locals.$event = event;
runStmt(stmt, locals);
} catch (error) {
console.error("[wrnexus] handler error in '" + stmt + "'", error);
}
};