Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d22824fa62 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -28,16 +28,16 @@
|
||||
"wrnexus": "./dist/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.46",
|
||||
"@wrnexus/router": "^0.2.46",
|
||||
"@wrnexus/csr": "^0.2.46",
|
||||
"@wrnexus/compiler": "^0.2.46",
|
||||
"@wrnexus/styles": "^0.2.46",
|
||||
"@wrnexus/dev-server": "^0.2.46",
|
||||
"@wrnexus/ui": "^0.2.46",
|
||||
"@wrnexus/validation": "^0.2.46",
|
||||
"@wrnexus/i18n": "^0.2.46",
|
||||
"@wrnexus/db": "^0.2.46"
|
||||
"@wrnexus/core": "^0.2.47",
|
||||
"@wrnexus/router": "^0.2.47",
|
||||
"@wrnexus/csr": "^0.2.47",
|
||||
"@wrnexus/compiler": "^0.2.47",
|
||||
"@wrnexus/styles": "^0.2.47",
|
||||
"@wrnexus/dev-server": "^0.2.47",
|
||||
"@wrnexus/ui": "^0.2.47",
|
||||
"@wrnexus/validation": "^0.2.47",
|
||||
"@wrnexus/i18n": "^0.2.47",
|
||||
"@wrnexus/db": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/core — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.46"
|
||||
"@wrnexus/core": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/db — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -25,18 +25,18 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.46",
|
||||
"@wrnexus/router": "^0.2.46",
|
||||
"@wrnexus/ssr": "^0.2.46",
|
||||
"@wrnexus/csr": "^0.2.46",
|
||||
"@wrnexus/compiler": "^0.2.46",
|
||||
"@wrnexus/styles": "^0.2.46",
|
||||
"@wrnexus/ui": "^0.2.46",
|
||||
"@wrnexus/validation": "^0.2.46",
|
||||
"@wrnexus/i18n": "^0.2.46",
|
||||
"@wrnexus/db": "^0.2.46",
|
||||
"@wrnexus/pubsub": "^0.2.46",
|
||||
"@wrnexus/uploader": "^0.2.46"
|
||||
"@wrnexus/core": "^0.2.47",
|
||||
"@wrnexus/router": "^0.2.47",
|
||||
"@wrnexus/ssr": "^0.2.47",
|
||||
"@wrnexus/csr": "^0.2.47",
|
||||
"@wrnexus/compiler": "^0.2.47",
|
||||
"@wrnexus/styles": "^0.2.47",
|
||||
"@wrnexus/ui": "^0.2.47",
|
||||
"@wrnexus/validation": "^0.2.47",
|
||||
"@wrnexus/i18n": "^0.2.47",
|
||||
"@wrnexus/db": "^0.2.47",
|
||||
"@wrnexus/pubsub": "^0.2.47",
|
||||
"@wrnexus/uploader": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/encryption — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.46"
|
||||
"@wrnexus/core": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/i18n — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.46"
|
||||
"@wrnexus/core": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/jwt — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/native": "^0.2.46"
|
||||
"@wrnexus/native": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/native — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/router — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,8 +21,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/compiler": "^0.2.46",
|
||||
"@wrnexus/core": "^0.2.46"
|
||||
"@wrnexus/compiler": "^0.2.47",
|
||||
"@wrnexus/core": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.46"
|
||||
"@wrnexus/core": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/uploader": "^0.2.46"
|
||||
"@wrnexus/uploader": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/test — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -22,7 +22,7 @@
|
||||
"./ui.css": "./ui.css"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.46"
|
||||
"@wrnexus/core": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/uploader — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.46"
|
||||
"@wrnexus/core": "^0.2.47"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/validation — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -492,6 +492,15 @@ const MIGRATIONS: Migration[] = [
|
||||
// No generated application files require automatic migration.
|
||||
},
|
||||
},
|
||||
{
|
||||
version: "0.2.47",
|
||||
id: "conditions-bug",
|
||||
description:
|
||||
"Adds server-rendered component each/if blocks and fixes dynamic component rendering.",
|
||||
apply() {
|
||||
// No generated application files require automatic migration.
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/** Release tooling uses this to require an explicit migration entry per version. */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -996,6 +996,20 @@ function viewHasEvents(nodes: ViewNode[]): boolean {
|
||||
});
|
||||
}
|
||||
|
||||
function viewHasServerEach(nodes: ViewNode[]): boolean {
|
||||
return nodes.some((node) => {
|
||||
if (node.type === "text") return false;
|
||||
|
||||
if (node.type === "each") return true;
|
||||
|
||||
if (node.type === "if") {
|
||||
return node.branches.some((branch) => viewHasServerEach(branch.body));
|
||||
}
|
||||
|
||||
return viewHasServerEach(node.children);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a text node. Interpolations that reference state stay as client
|
||||
* mustaches (`{expr}`, hydrated by the reactive runtime); interpolations of
|
||||
@@ -1102,6 +1116,18 @@ function renderComponentEachNode(node: EachNode, ctx: CompCtx): string {
|
||||
);
|
||||
}
|
||||
|
||||
function serverLoopLocalsAttribute(ctx: CompCtx): string {
|
||||
const locals = [...(ctx.serverLocals ?? [])];
|
||||
|
||||
if (locals.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const entries = locals.map((name) => `${JSON.stringify(name)}: ${name}`).join(", ");
|
||||
|
||||
return ` data-wrn-loop-locals="\${__wrnexusEncodeLoopLocals({ ${entries} })}"`;
|
||||
}
|
||||
|
||||
/** Render a component view node into template-literal-ready source. */
|
||||
function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
if (node.type === "text") return compileText(node.value, ctx);
|
||||
@@ -1167,7 +1193,14 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
? exprRefsState(a.value, elementContext.loopVars)
|
||||
: false;
|
||||
|
||||
if (!a.value.includes("{") || (!referencesState && !referencesLoopVariable)) {
|
||||
const referencesServerLocal = ctx.serverLocals
|
||||
? exprRefsState(a.value, ctx.serverLocals)
|
||||
: false;
|
||||
|
||||
if (
|
||||
!a.value.includes("{") ||
|
||||
(!referencesState && !referencesLoopVariable && !referencesServerLocal)
|
||||
) {
|
||||
return rendered;
|
||||
}
|
||||
|
||||
@@ -1195,8 +1228,17 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
|
||||
const staticClassValue = staticClasses.join(" ");
|
||||
|
||||
const classReferencesState = exprRefsState(staticClassValue, ctx.stateNames);
|
||||
const classReferencesLoopVariable = elementContext.loopVars
|
||||
? exprRefsState(staticClassValue, elementContext.loopVars)
|
||||
: false;
|
||||
const classReferencesServerLocal = ctx.serverLocals
|
||||
? exprRefsState(staticClassValue, ctx.serverLocals)
|
||||
: false;
|
||||
|
||||
const classHasReactiveExpression =
|
||||
staticClassValue.includes("{") && exprRefsState(staticClassValue, ctx.stateNames);
|
||||
staticClassValue.includes("{") &&
|
||||
(classReferencesState || classReferencesLoopVariable || classReferencesServerLocal);
|
||||
|
||||
const classAttribute =
|
||||
staticClasses.length > 0 || conditionalClasses.length > 0
|
||||
@@ -1208,9 +1250,6 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
: "";
|
||||
|
||||
const classBindings = conditionalClasses
|
||||
.filter(({ expression }) => {
|
||||
return !ctx.serverLocals || !exprRefsState(expression, ctx.serverLocals);
|
||||
})
|
||||
.map(({ className, expression }, index) => {
|
||||
const marker = attrEscape(JSON.stringify([className, expression]));
|
||||
|
||||
@@ -1218,8 +1257,14 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
})
|
||||
.join("");
|
||||
|
||||
const loopLocalsAttribute = serverLoopLocalsAttribute(ctx);
|
||||
|
||||
const allAttrs =
|
||||
`${classAttribute}` + `${classReactiveBinding}` + `${classBindings}` + `${attrs}`;
|
||||
`${loopLocalsAttribute}` +
|
||||
`${classAttribute}` +
|
||||
`${classReactiveBinding}` +
|
||||
`${classBindings}` +
|
||||
`${attrs}`;
|
||||
|
||||
if (VOID_ELEMENTS.has(node.tag.toLowerCase())) {
|
||||
return `<${node.tag}${allAttrs}>`;
|
||||
@@ -1232,6 +1277,11 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
|
||||
function generateComponent(ast: PageAst): string {
|
||||
const out: string[] = [];
|
||||
const hasServerEach = viewHasServerEach(ast.view);
|
||||
|
||||
if (hasServerEach) {
|
||||
out.push(`import { Buffer } from "node:buffer";`);
|
||||
}
|
||||
|
||||
const effectiveProps =
|
||||
ast.kind === "layout" && !ast.props.some((prop) => prop.name === "content")
|
||||
@@ -1420,6 +1470,12 @@ function __wireRaw(v: any): string {
|
||||
return String(v == null ? "" : v);
|
||||
}`);
|
||||
|
||||
if (hasServerEach) {
|
||||
out.push(`function __wrnexusEncodeLoopLocals(value: Record<string, any>): string {
|
||||
return Buffer.from(JSON.stringify(value), "utf8").toString("base64");
|
||||
}`);
|
||||
}
|
||||
|
||||
if (needsScope) {
|
||||
out.push(`function __wrnexusSerializeScopeValue(value: any): string {
|
||||
if (value === undefined) {
|
||||
|
||||
@@ -881,3 +881,68 @@ component Accordion {
|
||||
|
||||
expect(output).not.toContain('${(isOpen(index)) ? " open" : ""}');
|
||||
});
|
||||
test("server-rendered each locals work in reactive handlers", () => {
|
||||
const firstLocals = Buffer.from(
|
||||
JSON.stringify({
|
||||
item: {
|
||||
question: "First",
|
||||
},
|
||||
index: 0,
|
||||
}),
|
||||
"utf8",
|
||||
).toString("base64");
|
||||
|
||||
const secondLocals = Buffer.from(
|
||||
JSON.stringify({
|
||||
item: {
|
||||
question: "Second",
|
||||
},
|
||||
index: 1,
|
||||
}),
|
||||
"utf8",
|
||||
).toString("base64");
|
||||
|
||||
const dom = mountHtml(`
|
||||
<div
|
||||
data-scope="openIndex: -1"
|
||||
>
|
||||
<article
|
||||
data-wrn-loop-locals="${firstLocals}"
|
||||
>
|
||||
<button
|
||||
data-on-click="openIndex = index"
|
||||
>
|
||||
First
|
||||
</button>
|
||||
|
||||
<span
|
||||
data-wrn-class-0='["open","openIndex === index"]'
|
||||
></span>
|
||||
</article>
|
||||
|
||||
<article
|
||||
data-wrn-loop-locals="${secondLocals}"
|
||||
>
|
||||
<button
|
||||
data-on-click="openIndex = index"
|
||||
>
|
||||
Second
|
||||
</button>
|
||||
|
||||
<span
|
||||
data-wrn-class-0='["open","openIndex === index"]'
|
||||
></span>
|
||||
</article>
|
||||
</div>
|
||||
`);
|
||||
|
||||
const buttons: any = dom.querySelectorAll("button");
|
||||
|
||||
buttons[1].click();
|
||||
|
||||
const articles = dom.querySelectorAll("article");
|
||||
|
||||
expect(articles[0].querySelector("span")?.classList.contains("open")).toBe(false);
|
||||
|
||||
expect(articles[1].querySelector("span")?.classList.contains("open")).toBe(true);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* data-scope="count: 0, name: 'x'" declare reactive state on a subtree
|
||||
* data-on-<event>="count++" run a statement in scope on an event
|
||||
* data-text="expr" element textContent follows an expression
|
||||
* data-wrn-loop-locals="base64-json" preserves SSR {#each} item/index values
|
||||
* data-wrnexus-csr="id" target for generated CSR fetch bindings
|
||||
* {{expr}} or {expr} interpolation inside text nodes
|
||||
*
|
||||
@@ -211,16 +212,96 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
|
||||
function signal(initial) {
|
||||
var value = initial;
|
||||
var subs = new Set();
|
||||
var subscribers = new Set();
|
||||
var notifying = false;
|
||||
var pendingNotification = false;
|
||||
var pendingPrevious;
|
||||
|
||||
function notify(previous) {
|
||||
if (notifying) {
|
||||
pendingNotification = true;
|
||||
|
||||
if (pendingPrevious === undefined) {
|
||||
pendingPrevious = previous;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
notifying = true;
|
||||
|
||||
try {
|
||||
/*
|
||||
* Iterate a snapshot. Reactive renderers may subscribe again while
|
||||
* running, but that must not mutate the collection currently being
|
||||
* iterated.
|
||||
*/
|
||||
var snapshot =
|
||||
Array.from(subscribers);
|
||||
|
||||
snapshot.forEach(function (
|
||||
subscriber,
|
||||
) {
|
||||
if (
|
||||
subscribers.has(
|
||||
subscriber,
|
||||
)
|
||||
) {
|
||||
subscriber(
|
||||
value,
|
||||
previous,
|
||||
);
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
notifying = false;
|
||||
}
|
||||
|
||||
if (pendingNotification) {
|
||||
var nextPrevious =
|
||||
pendingPrevious;
|
||||
|
||||
pendingNotification = false;
|
||||
pendingPrevious = undefined;
|
||||
|
||||
notify(nextPrevious);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
get: function () { return value; },
|
||||
set: function (v) {
|
||||
if (Object.is(v, value)) return;
|
||||
var previous = value;
|
||||
value = v;
|
||||
subs.forEach(function (f) { f(value, previous); });
|
||||
get: function () {
|
||||
return value;
|
||||
},
|
||||
|
||||
set: function (nextValue) {
|
||||
if (
|
||||
Object.is(
|
||||
nextValue,
|
||||
value,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
var previous = value;
|
||||
value = nextValue;
|
||||
|
||||
notify(previous);
|
||||
},
|
||||
|
||||
subscribe: function (
|
||||
subscriber,
|
||||
) {
|
||||
subscribers.add(
|
||||
subscriber,
|
||||
);
|
||||
|
||||
return function () {
|
||||
subscribers.delete(
|
||||
subscriber,
|
||||
);
|
||||
};
|
||||
},
|
||||
subscribe: function (f) { subs.add(f); return function () { subs.delete(f); }; }
|
||||
};
|
||||
}
|
||||
function applyReactiveAttribute(node, name, value) {
|
||||
@@ -385,21 +466,52 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
// change then re-runs only the renderers that actually read it. The Set in
|
||||
// signal.subscribe dedupes, so re-subscribing each run is cheap and bounded.
|
||||
var currentRenderer = null;
|
||||
var currentRenderer = null;
|
||||
|
||||
function reactive(fn) {
|
||||
var running = false;
|
||||
|
||||
function run() {
|
||||
var prev = currentRenderer;
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = true;
|
||||
|
||||
var previousRenderer =
|
||||
currentRenderer;
|
||||
|
||||
currentRenderer = run;
|
||||
try { fn(); } finally { currentRenderer = prev; }
|
||||
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
currentRenderer =
|
||||
previousRenderer;
|
||||
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
renderers.push(run);
|
||||
|
||||
return run;
|
||||
}
|
||||
function renderAll() { renderers.forEach(function (f) { f(); }); }
|
||||
function decodeLoopLocals(node) {
|
||||
if (!node) {
|
||||
return {};
|
||||
}
|
||||
|
||||
var element =
|
||||
node.nodeType === 1
|
||||
? node
|
||||
: node.parentElement;
|
||||
|
||||
var owner =
|
||||
node &&
|
||||
node.closest &&
|
||||
node.closest(
|
||||
element &&
|
||||
element.closest &&
|
||||
element.closest(
|
||||
"[data-wrn-loop-locals]",
|
||||
);
|
||||
|
||||
@@ -421,9 +533,7 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
window.atob(encoded);
|
||||
|
||||
var bytes =
|
||||
new Uint8Array(
|
||||
binary.length,
|
||||
);
|
||||
new Uint8Array(binary.length);
|
||||
|
||||
for (
|
||||
var index = 0;
|
||||
@@ -434,16 +544,17 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
binary.charCodeAt(index);
|
||||
}
|
||||
|
||||
var value =
|
||||
JSON.parse(
|
||||
new TextDecoder(
|
||||
"utf-8",
|
||||
).decode(bytes),
|
||||
var decoded =
|
||||
new TextDecoder("utf-8").decode(
|
||||
bytes,
|
||||
);
|
||||
|
||||
return value &&
|
||||
typeof value === "object"
|
||||
? value
|
||||
var parsed =
|
||||
JSON.parse(decoded);
|
||||
|
||||
return parsed &&
|
||||
typeof parsed === "object"
|
||||
? parsed
|
||||
: {};
|
||||
} catch (error) {
|
||||
console.error(
|
||||
@@ -515,17 +626,43 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
});
|
||||
}
|
||||
|
||||
function writeScope(name, value) {
|
||||
function writeScope(
|
||||
name,
|
||||
value,
|
||||
) {
|
||||
if (!signals[name]) {
|
||||
signals[name] = signal(value);
|
||||
signals[name] =
|
||||
signal(value);
|
||||
|
||||
notifyState(
|
||||
name,
|
||||
value,
|
||||
undefined,
|
||||
);
|
||||
|
||||
renderAll();
|
||||
notifyState(name, value, undefined);
|
||||
return;
|
||||
}
|
||||
var previous = signals[name].get();
|
||||
if (Object.is(previous, value)) return;
|
||||
|
||||
var previous =
|
||||
signals[name].get();
|
||||
|
||||
if (
|
||||
Object.is(
|
||||
previous,
|
||||
value,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
signals[name].set(value);
|
||||
notifyState(name, value, previous);
|
||||
|
||||
notifyState(
|
||||
name,
|
||||
value,
|
||||
previous,
|
||||
);
|
||||
}
|
||||
|
||||
function evalExpr(expr, locals) {
|
||||
@@ -1122,22 +1259,46 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
});
|
||||
});
|
||||
|
||||
// data-text bindings
|
||||
// data-text bindings. Server-rendered {#each} nodes recover their item/index
|
||||
// values from the nearest data-wrn-loop-locals marker.
|
||||
el.querySelectorAll("[data-text]").forEach(function (node) {
|
||||
if (!owns(node)) return;
|
||||
|
||||
var expr = node.getAttribute("data-text");
|
||||
|
||||
reactive(function () {
|
||||
try { node.textContent = String(evalExpr(expr)); } catch (e) { /* ignore */ }
|
||||
try {
|
||||
var value = evalExpr(
|
||||
expr,
|
||||
decodeLoopLocals(node),
|
||||
);
|
||||
|
||||
node.textContent =
|
||||
value == null ? "" : String(value);
|
||||
} catch (_) {
|
||||
// Keep the server-rendered value when evaluation is unavailable.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// data-show="expr" — toggle visibility on truthiness.
|
||||
el.querySelectorAll("[data-show]").forEach(function (node) {
|
||||
if (!owns(node)) return;
|
||||
|
||||
var showExpr = node.getAttribute("data-show");
|
||||
|
||||
reactive(function () {
|
||||
var visible = true;
|
||||
try { visible = !!evalExpr(showExpr); } catch (e) { /* keep visible */ }
|
||||
|
||||
try {
|
||||
visible = !!evalExpr(
|
||||
showExpr,
|
||||
decodeLoopLocals(node),
|
||||
);
|
||||
} catch (_) {
|
||||
// Keep visible when evaluation is unavailable.
|
||||
}
|
||||
|
||||
node.style.display = visible ? "" : "none";
|
||||
});
|
||||
});
|
||||
@@ -1245,7 +1406,7 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
try {
|
||||
raw = evalExpr(
|
||||
exact[1].trim(),
|
||||
decodeLoopLocals(node),
|
||||
locals,
|
||||
);
|
||||
} catch (error) {
|
||||
return;
|
||||
@@ -1259,10 +1420,10 @@ export const REACTIVE_RUNTIME = String.raw`
|
||||
) {
|
||||
try {
|
||||
var value =
|
||||
evalExpr(
|
||||
expression.trim(),
|
||||
decodeLoopLocals(node),
|
||||
);
|
||||
evalExpr(
|
||||
expression.trim(),
|
||||
locals,
|
||||
);
|
||||
|
||||
return value == null
|
||||
? ""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.2.46",
|
||||
"version": "0.2.47",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
const version = "0.2.46";
|
||||
const version = "0.2.47";
|
||||
|
||||
const dependencyGroups = [
|
||||
"dependencies",
|
||||
|
||||
Reference in New Issue
Block a user