complete framework remediation validation
This commit is contained in:
+137
-61
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
// Generated by scripts/build-editor-compiler.mjs. Do not edit directly.
|
||||
// WRN editor compiler source hash: 451706f47a7734fd5808cdeeb5786e1b97a183afec98db99fc041369eb16a205
|
||||
// WRN editor compiler source hash: 4f9249b868459eb792fcd6dc7814e440bd2c0ad6d08f1dec947939dc48db92b3
|
||||
// WRN editor compiler generator hash: c71e7fe4258c97b73b384ff14b321f0cf0b30cc2ed0322f5f84b04e757159b18
|
||||
// Generated with TypeScript: 5.9.3
|
||||
const __nodeRequire = require;
|
||||
@@ -558,7 +558,7 @@ function browserModuleRequired(ast) {
|
||||
const functions = ast.runtimeFunctions.filter((fn) => ["legacy", "client", "shared"].includes(fn.runtime));
|
||||
return functions.length > 0 || selectedBrowserImports(ast, functions).length > 0;
|
||||
}
|
||||
function functionEntry(ast, fn, availableFunctions) {
|
||||
function _functionEntry(ast, fn, availableFunctions) {
|
||||
const parameterNames = new Set(fn.parameters.map((parameter) => parameter.name));
|
||||
/*
|
||||
* Names the function body declares for itself.
|
||||
@@ -608,32 +608,42 @@ function functionEntry(ast, fn, availableFunctions) {
|
||||
const syncStateFromContext = stateNames
|
||||
.map((name) => `${name} = context.state.${name};`)
|
||||
.join(" ");
|
||||
const peerAliases = functionAliases
|
||||
.map((name) => {
|
||||
const call = `context.functions[${JSON.stringify(name)}](...__wrnexusPeerArgs)`;
|
||||
if (!stateNames.length) {
|
||||
return `const ${name} = (...__wrnexusPeerArgs) => ${call};`;
|
||||
}
|
||||
return `const ${name} = (...__wrnexusPeerArgs) => {
|
||||
${syncStateToContext}
|
||||
let __wrnexusPeerResult;
|
||||
const peerAliases = !stateNames.length
|
||||
? functionAliases
|
||||
.map((name) => `const ${name} = (...__wrnexusPeerArgs) => context.functions[${JSON.stringify(name)}](...__wrnexusPeerArgs);`)
|
||||
.join("\n")
|
||||
: `const __wrnexusFlush = () => { ${syncStateToContext} };
|
||||
const __wrnexusRestore = () => { ${syncStateFromContext} };
|
||||
const __wrnexusPeer = (name, args) => {
|
||||
__wrnexusFlush();
|
||||
let result;
|
||||
try {
|
||||
__wrnexusPeerResult = ${call};
|
||||
} catch (__wrnexusPeerError) {
|
||||
${syncStateFromContext}
|
||||
throw __wrnexusPeerError;
|
||||
result = context.functions[name](...args);
|
||||
} catch (error) {
|
||||
__wrnexusRestore();
|
||||
throw error;
|
||||
}
|
||||
if (__wrnexusPeerResult && typeof __wrnexusPeerResult.then === "function") {
|
||||
return Promise.resolve(__wrnexusPeerResult).finally(() => { ${syncStateFromContext} });
|
||||
if (result && typeof result.then === "function") {
|
||||
return Promise.resolve(result).finally(__wrnexusRestore);
|
||||
}
|
||||
${syncStateFromContext}
|
||||
return __wrnexusPeerResult;
|
||||
};`;
|
||||
})
|
||||
.join("\n");
|
||||
__wrnexusRestore();
|
||||
return result;
|
||||
};
|
||||
${functionAliases.map((name) => `const ${name} = (...__wrnexusPeerArgs) => __wrnexusPeer(${JSON.stringify(name)}, __wrnexusPeerArgs);`).join("\n")}`;
|
||||
const copyBack = stateNames
|
||||
.map((name) => `if (!Object.is(${name}, __wrnexusInitialState[${JSON.stringify(name)}])) context.state.${name} = ${name};`)
|
||||
.join("\n");
|
||||
const commitBinding = stateNames.length
|
||||
? `const __wrnexusCommit = () => { ${copyBack} };
|
||||
${!parameterNames.has("commit") && !declaredLocals.has("commit") && !functionAliases.includes("commit") ? "const commit = __wrnexusCommit;" : ""}
|
||||
${!parameterNames.has("setTimeout") &&
|
||||
!declaredLocals.has("setTimeout") &&
|
||||
!functionAliases.includes("setTimeout")
|
||||
? `const setTimeout = (callback, delay, ...args) => globalThis.setTimeout(() => {
|
||||
try { return callback(...args); } finally { __wrnexusCommit(); }
|
||||
}, delay);`
|
||||
: ""}`
|
||||
: "";
|
||||
const body = (0, syntax_1.eraseFunctionTypes)(fn.body);
|
||||
const runtimeBindings = [
|
||||
!parameterNames.has("output") ? "const output = context.output;" : "",
|
||||
@@ -647,12 +657,13 @@ function functionEntry(ast, fn, availableFunctions) {
|
||||
${initialStateSnapshot}
|
||||
${stateAliases}
|
||||
${propAliases}
|
||||
${commitBinding}
|
||||
${peerAliases}
|
||||
${runtimeBindings}
|
||||
try {
|
||||
${body}
|
||||
} finally {
|
||||
${copyBack}
|
||||
${stateNames.length ? "__wrnexusCommit();" : ""}
|
||||
}
|
||||
}`;
|
||||
}
|
||||
@@ -663,22 +674,74 @@ function generateBrowserModule(ast) {
|
||||
const selectedImports = selectedBrowserImports(ast, functions);
|
||||
const imports = selectedImports.map((entry) => entry.code).join("\n");
|
||||
const importedBindings = [...new Set(selectedImports.flatMap((entry) => entry.bindings))];
|
||||
const sharedState = state.filter((name) => safeIdentifier(name) && !RUNTIME_BINDINGS.has(name));
|
||||
const sharedProps = ast.props
|
||||
.map((entry) => entry.name)
|
||||
.filter((name) => safeIdentifier(name) && !RUNTIME_BINDINGS.has(name) && !sharedState.includes(name));
|
||||
const callableAliases = functionNames.filter((name) => safeIdentifier(name) &&
|
||||
!RUNTIME_BINDINGS.has(name) &&
|
||||
!sharedState.includes(name) &&
|
||||
!sharedProps.includes(name));
|
||||
const sharedCommit = sharedState.map((name) => `context.state.${name} = ${name};`).join(" ");
|
||||
const sharedRestore = [
|
||||
...sharedState.map((name) => `${name} = context.state.${name};`),
|
||||
...sharedProps.map((name) => `${name} = context.props.${name};`),
|
||||
].join(" ");
|
||||
const hasAuthoredCommit = callableAliases.includes("commit");
|
||||
const hasAuthoredSetTimeout = callableAliases.includes("setTimeout");
|
||||
const implementations = functions
|
||||
.map((fn) => {
|
||||
const parameters = fn.parameters.map((parameter) => parameter.name).join(", ");
|
||||
return `${JSON.stringify(fn.name)}: ${fn.async ? "async " : ""}function(${parameters}) {
|
||||
try { ${(0, syntax_1.eraseFunctionTypes)(fn.body)} } finally { __wrnexusCommit(); }
|
||||
}`;
|
||||
})
|
||||
.join(",\n");
|
||||
return `// generated WRNexusJS browser module for ${ast.name}
|
||||
${imports}
|
||||
export const __wrnexusClientFunctions = {
|
||||
${functions.map((fn) => ` ${functionEntry(ast, fn, functionNames)}`).join(",\n")}
|
||||
${functions
|
||||
.map((fn) => ` ${JSON.stringify(fn.name)}: (context, ...args) => __wrnexusBindings(context)[${JSON.stringify(fn.name)}](...args)`)
|
||||
.join(",\n")}
|
||||
};
|
||||
export const __wrnexusClientState = ${JSON.stringify(state)};
|
||||
export const __wrnexusOutputs = ${JSON.stringify(ast.outputs)};
|
||||
export const __wrnexusImportedBindings = { ${importedBindings.join(", ")} };
|
||||
export function bindClientScope(context) {
|
||||
function __wrnexusCreateClientFunctions(context) {
|
||||
${sharedState.length ? `let { ${sharedState.join(", ")} } = context.state;` : ""}
|
||||
${sharedProps.length ? `let { ${sharedProps.join(", ")} } = context.props;` : ""}
|
||||
const output = context.output;
|
||||
const server = context.server;
|
||||
const props = context.props;
|
||||
const refs = context.refs;
|
||||
const __wrnexusCommit = () => { ${sharedCommit} };
|
||||
const __wrnexusRestore = () => { ${sharedRestore} };
|
||||
${!hasAuthoredCommit ? "const commit = __wrnexusCommit;" : ""}
|
||||
${!hasAuthoredSetTimeout
|
||||
? `const setTimeout = (callback, delay, ...args) => globalThis.setTimeout(() => {
|
||||
try { return callback(...args); } finally { __wrnexusCommit(); }
|
||||
}, delay);`
|
||||
: ""}
|
||||
const implementations = {
|
||||
${implementations}
|
||||
};
|
||||
${callableAliases.map((name) => `const ${name} = (...args) => implementations[${JSON.stringify(name)}](...args);`).join("\n ")}
|
||||
const functions = {};
|
||||
const scopedContext = { ...context, functions };
|
||||
for (const [name, handler] of Object.entries(__wrnexusClientFunctions)) {
|
||||
functions[name] = (...args) => handler(scopedContext, ...args);
|
||||
for (const name of Object.keys(implementations)) {
|
||||
functions[name] = (...args) => {
|
||||
__wrnexusRestore();
|
||||
return implementations[name](...args);
|
||||
};
|
||||
}
|
||||
return functions;
|
||||
}
|
||||
function __wrnexusBindings(context) {
|
||||
return context.__wrnexusBoundFunctions ||
|
||||
(context.__wrnexusBoundFunctions = __wrnexusCreateClientFunctions(context));
|
||||
}
|
||||
export function bindClientScope(context) {
|
||||
return __wrnexusBindings(context);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1282,13 +1345,12 @@ function renderNestedComponentInvocation(node, ctx) {
|
||||
? `\${__wireProp(${ctx.resolveExpr(wholeExpression)})}`
|
||||
: compileAttrValue(attr.value, ctx);
|
||||
const rendered = ` ${attr.name}="${compiledValue}"`;
|
||||
if (wholeExpression ||
|
||||
!attr.value.includes("{") ||
|
||||
!exprRefsState(attr.value, ctx.stateNames)) {
|
||||
if (!attr.value.includes("{") ||
|
||||
(!exprRefsState(attr.value, ctx.stateNames) && !exprRefsState(attr.value, ctx.propNames))) {
|
||||
return rendered;
|
||||
}
|
||||
const marker = attrEscape(JSON.stringify([attr.name, attr.value]));
|
||||
return rendered + ` data-wrn-bind-${bindIndex++}="${escLit(marker)}"`;
|
||||
return rendered + ` data-wrn-prop-bind-${bindIndex++}="${escLit(marker)}"`;
|
||||
})
|
||||
.join("");
|
||||
const loops = loopVarsOf(node);
|
||||
@@ -2178,7 +2240,9 @@ function exprRefsState(expr, stateNames) {
|
||||
return false;
|
||||
}
|
||||
function exprRefsComponentReactiveValue(expr, ctx) {
|
||||
return exprRefsState(expr, ctx.stateNames) || exprRefsState(expr, ctx.functionNames);
|
||||
return (exprRefsState(expr, ctx.stateNames) ||
|
||||
exprRefsState(expr, ctx.propNames) ||
|
||||
exprRefsState(expr, ctx.functionNames));
|
||||
}
|
||||
function viewHasEvents(nodes) {
|
||||
return nodes.some((node) => {
|
||||
@@ -2242,6 +2306,9 @@ function compileText(raw, ctx) {
|
||||
// list renderer fills it per item; it has no server-side value.
|
||||
out += escLit(`{${expr}}`);
|
||||
}
|
||||
else if (expr === "content") {
|
||||
out += `\${__wireRaw(${ctx.resolveExpr(expr)})}`;
|
||||
}
|
||||
else if (exprRefsComponentReactiveValue(expr, ctx)) {
|
||||
// State interpolation: bake the initial value AND keep it reactive via a
|
||||
// data-text span, so no-JS clients see the real value and hydration
|
||||
@@ -2251,9 +2318,6 @@ function compileText(raw, ctx) {
|
||||
`\${__wireHtml(${ctx.resolveExpr(expr)})}` +
|
||||
escLit(`</span>`);
|
||||
}
|
||||
else if (expr === "content") {
|
||||
out += `\${__wireRaw(${ctx.resolveExpr(expr)})}`;
|
||||
}
|
||||
else {
|
||||
out += `\${__wireHtml(${ctx.resolveExpr(expr)})}`;
|
||||
}
|
||||
@@ -2593,6 +2657,7 @@ function generateComponent(ast) {
|
||||
};
|
||||
const ctx = {
|
||||
stateNames,
|
||||
propNames: new Set(effectiveProps.map((entry) => entry.name)),
|
||||
functionNames: new Set(ast.runtimeFunctions.filter((fn) => fn.runtime !== "server").map((fn) => fn.name)),
|
||||
resolveExpr,
|
||||
eventNames: publicOutputNames(ast),
|
||||
@@ -2610,12 +2675,12 @@ function generateComponent(ast) {
|
||||
.join("");
|
||||
const styles = ast.styles.map((body) => body.trim()).filter(Boolean);
|
||||
const styleTag = escLit(localStyleTag(ast, styles));
|
||||
// A component needs a reactive scope only when it has state or event handlers.
|
||||
// Prop-driven text/attributes are baked server-side, so static components ship
|
||||
// no JavaScript at all.
|
||||
// Props remain server-rendered and also become signals so a parent can drive
|
||||
// a mounted child after hydration.
|
||||
const behavior = componentBehavior(ast);
|
||||
const needsScope = ast.runtime !== "server" &&
|
||||
(browserStates.length > 0 ||
|
||||
(effectiveProps.length > 0 ||
|
||||
browserStates.length > 0 ||
|
||||
ast.computed.length > 0 ||
|
||||
viewHasEvents(ast.view) ||
|
||||
behavior !== null);
|
||||
@@ -2632,7 +2697,7 @@ function generateComponent(ast) {
|
||||
if (prop.required) {
|
||||
decls.push(` if (__p[${JSON.stringify(prop.name)}] === undefined) throw new TypeError(${JSON.stringify(`${ast.name} requires prop '${prop.name}' (${prop.valueType ?? "unknown"})`)});`);
|
||||
}
|
||||
decls.push(` const ${nameRefs.get(prop.name)}: ${prop.valueType ?? "any"} = __coerce(__p[${JSON.stringify(prop.name)}], (${resolveExpr(prop.default)}), ${JSON.stringify((0, types_ts_1.runtimeTypeOf)(prop.valueType))});`);
|
||||
decls.push(` const ${nameRefs.get(prop.name)}: ${prop.valueType ?? "any"} = __coerce(__p[${JSON.stringify(prop.name)}], (${resolveExpr(prop.default)}), ${JSON.stringify((0, types_ts_1.runtimeTypeOf)(prop.valueType))}, ${JSON.stringify(prop.name)});`);
|
||||
}
|
||||
if (!effectiveProps.some((prop) => prop.name === "attrs")) {
|
||||
decls.push(` const __attrs = __restProps(__p, new Set(${JSON.stringify(effectiveProps.map((prop) => prop.name))}));`);
|
||||
@@ -2697,7 +2762,7 @@ function generateComponent(ast) {
|
||||
.map((output) => ` ${JSON.stringify(output.name)}(${output.payload ? `${output.payload.name}${output.payload.optional ? "?" : ""}: ${output.payload.valueType}` : ""}): void;`)
|
||||
.join("\n")}\n}`);
|
||||
}
|
||||
out.push(`function __coerce(v: any, def: any, declared: string = "unknown"): any {
|
||||
out.push(`function __coerce(v: any, def: any, declared: string = "unknown", propName: string = "prop"): any {
|
||||
if (v === undefined || v === null) {
|
||||
return def;
|
||||
}
|
||||
@@ -2722,14 +2787,14 @@ function generateComponent(ast) {
|
||||
if (typeof v === "string") {
|
||||
try {
|
||||
const parsed = JSON.parse(v);
|
||||
return Array.isArray(parsed) ? parsed : def;
|
||||
if (!Array.isArray(parsed)) throw new TypeError("Expected an array prop '" + propName + "'");
|
||||
return parsed;
|
||||
} catch {
|
||||
if (declared === "array") throw new TypeError("Expected an array prop");
|
||||
return def;
|
||||
throw new TypeError("Expected an array prop '" + propName + "'");
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
throw new TypeError("Expected an array prop '" + propName + "'");
|
||||
}
|
||||
|
||||
if (declared === "object" || (def !== null && typeof def === "object")) {
|
||||
@@ -2745,20 +2810,16 @@ function generateComponent(ast) {
|
||||
try {
|
||||
const parsed = JSON.parse(v);
|
||||
|
||||
return (
|
||||
parsed !== null &&
|
||||
typeof parsed === "object" &&
|
||||
!Array.isArray(parsed)
|
||||
)
|
||||
? parsed
|
||||
: def;
|
||||
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||
throw new TypeError("Expected an object prop '" + propName + "'");
|
||||
}
|
||||
return parsed;
|
||||
} catch {
|
||||
if (declared === "object") throw new TypeError("Expected an object prop");
|
||||
return def;
|
||||
throw new TypeError("Expected an object prop '" + propName + "'");
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
throw new TypeError("Expected an object prop '" + propName + "'");
|
||||
}
|
||||
|
||||
if (declared === "bigint") return BigInt(v);
|
||||
@@ -2828,10 +2889,12 @@ function __wireSpreadAttrs(value: any): string {
|
||||
lowerName === "style" ||
|
||||
lowerName === "slot" ||
|
||||
lowerName === "data-component" ||
|
||||
// Internal markers must not leak through a spread -- except the
|
||||
// parent's output handlers, whose whole job is to ride from the mount
|
||||
// onto the view root so the mounting scope can bind them there.
|
||||
(lowerName.startsWith("data-wrn") && !lowerName.startsWith("data-wrn-out-"))
|
||||
// Internal markers must not leak through a spread. Parent-owned output
|
||||
// and prop bindings ride from the mount onto the
|
||||
// rendered child root so the mounting scope can bind them there.
|
||||
(lowerName.startsWith("data-wrn") &&
|
||||
!lowerName.startsWith("data-wrn-out-") &&
|
||||
!lowerName.startsWith("data-wrn-prop-bind-"))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -5634,7 +5697,12 @@ function parse(source) {
|
||||
// props { name: Type = <default>; @event name = function }
|
||||
lx.next();
|
||||
expect("lbrace");
|
||||
while (lx.peek().type !== "rbrace") {
|
||||
while (true) {
|
||||
if (lx.startsWithBlockComment()) {
|
||||
throw new ParseError("Block comments are not allowed inside props {}; use // line comments instead", "WRN-PROPS-BLOCK-COMMENT");
|
||||
}
|
||||
if (lx.peek().type === "rbrace")
|
||||
break;
|
||||
const t = lx.peek();
|
||||
if (t.type === "eof")
|
||||
throw new ParseError("Unexpected end of input inside props");
|
||||
@@ -6013,6 +6081,9 @@ function parse(source) {
|
||||
}
|
||||
expect("rbrace");
|
||||
const declaredStates = new Set(states.map((state) => state.name));
|
||||
if (declaredStates.has("page")) {
|
||||
throw new ParseError("State name 'page' collides with the WRN 'page' keyword; choose another state name", "WRN-STATE-RESERVED-NAME");
|
||||
}
|
||||
for (const watcher of watches) {
|
||||
if (!declaredStates.has(watcher.state)) {
|
||||
throw new ParseError(`Cannot watch undeclared state '${watcher.state}'`);
|
||||
@@ -6543,6 +6614,11 @@ class Lexer {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/** True when the next non-trivia characters open a block comment. */
|
||||
startsWithBlockComment() {
|
||||
this.skipTrivia();
|
||||
return this.src[this.pos] === "/" && this.src[this.pos + 1] === "*";
|
||||
}
|
||||
/** Read and consume the next structural token. */
|
||||
next() {
|
||||
this.skipTrivia();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// WRN editor extension source hash: 6165420f37af57e441086efeb6b25528f72e77a39abb2c76ca5bc8748dd90c2f
|
||||
// WRN editor extension source hash: c09cc127dcc7fbae562648b09435fb96134c18e63ba21d7a4b476b916f4a8256
|
||||
// WRN editor extension generator hash: 456d1d614e44e5fb1f19b784176c09cf2ade9b64ef73a17934c2698150b62728
|
||||
"use strict";
|
||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
@@ -23202,7 +23202,7 @@ ${(0, codegen_ts_1.generate)(ast)}`,
|
||||
const functions = ast.runtimeFunctions.filter((fn) => ["legacy", "client", "shared"].includes(fn.runtime));
|
||||
return functions.length > 0 || selectedBrowserImports(ast, functions).length > 0;
|
||||
}
|
||||
function functionEntry(ast, fn, availableFunctions) {
|
||||
function _functionEntry(ast, fn, availableFunctions) {
|
||||
const parameterNames = new Set(fn.parameters.map((parameter) => parameter.name));
|
||||
const declaredLocals = new Set;
|
||||
for (const match of fn.body.matchAll(/\b(?:var|let|const)\s+([A-Za-z_$][\w$]*)|\bfunction\s+([A-Za-z_$][\w$]*)/g)) {
|
||||
@@ -23220,30 +23220,33 @@ ${(0, codegen_ts_1.generate)(ast)}`,
|
||||
const propAliases = propNames.length ? `const { ${propNames.join(", ")} } = context.props;` : "";
|
||||
const syncStateToContext = stateNames.map((name) => `context.state.${name} = ${name};`).join(" ");
|
||||
const syncStateFromContext = stateNames.map((name) => `${name} = context.state.${name};`).join(" ");
|
||||
const peerAliases = functionAliases.map((name) => {
|
||||
const call = `context.functions[${JSON.stringify(name)}](...__wrnexusPeerArgs)`;
|
||||
if (!stateNames.length) {
|
||||
return `const ${name} = (...__wrnexusPeerArgs) => ${call};`;
|
||||
}
|
||||
return `const ${name} = (...__wrnexusPeerArgs) => {
|
||||
${syncStateToContext}
|
||||
let __wrnexusPeerResult;
|
||||
const peerAliases = !stateNames.length ? functionAliases.map((name) => `const ${name} = (...__wrnexusPeerArgs) => context.functions[${JSON.stringify(name)}](...__wrnexusPeerArgs);`).join(`
|
||||
`) : `const __wrnexusFlush = () => { ${syncStateToContext} };
|
||||
const __wrnexusRestore = () => { ${syncStateFromContext} };
|
||||
const __wrnexusPeer = (name, args) => {
|
||||
__wrnexusFlush();
|
||||
let result;
|
||||
try {
|
||||
__wrnexusPeerResult = ${call};
|
||||
} catch (__wrnexusPeerError) {
|
||||
${syncStateFromContext}
|
||||
throw __wrnexusPeerError;
|
||||
result = context.functions[name](...args);
|
||||
} catch (error) {
|
||||
__wrnexusRestore();
|
||||
throw error;
|
||||
}
|
||||
if (__wrnexusPeerResult && typeof __wrnexusPeerResult.then === "function") {
|
||||
return Promise.resolve(__wrnexusPeerResult).finally(() => { ${syncStateFromContext} });
|
||||
if (result && typeof result.then === "function") {
|
||||
return Promise.resolve(result).finally(__wrnexusRestore);
|
||||
}
|
||||
${syncStateFromContext}
|
||||
return __wrnexusPeerResult;
|
||||
};`;
|
||||
}).join(`
|
||||
`);
|
||||
__wrnexusRestore();
|
||||
return result;
|
||||
};
|
||||
${functionAliases.map((name) => `const ${name} = (...__wrnexusPeerArgs) => __wrnexusPeer(${JSON.stringify(name)}, __wrnexusPeerArgs);`).join(`
|
||||
`)}`;
|
||||
const copyBack = stateNames.map((name) => `if (!Object.is(${name}, __wrnexusInitialState[${JSON.stringify(name)}])) context.state.${name} = ${name};`).join(`
|
||||
`);
|
||||
const commitBinding = stateNames.length ? `const __wrnexusCommit = () => { ${copyBack} };
|
||||
${!parameterNames.has("commit") && !declaredLocals.has("commit") && !functionAliases.includes("commit") ? "const commit = __wrnexusCommit;" : ""}
|
||||
${!parameterNames.has("setTimeout") && !declaredLocals.has("setTimeout") && !functionAliases.includes("setTimeout") ? `const setTimeout = (callback, delay, ...args) => globalThis.setTimeout(() => {
|
||||
try { return callback(...args); } finally { __wrnexusCommit(); }
|
||||
}, delay);` : ""}` : "";
|
||||
const body = (0, syntax_1.eraseFunctionTypes)(fn.body);
|
||||
const runtimeBindings = [
|
||||
!parameterNames.has("output") ? "const output = context.output;" : "",
|
||||
@@ -23256,12 +23259,13 @@ ${(0, codegen_ts_1.generate)(ast)}`,
|
||||
${initialStateSnapshot}
|
||||
${stateAliases}
|
||||
${propAliases}
|
||||
${commitBinding}
|
||||
${peerAliases}
|
||||
${runtimeBindings}
|
||||
try {
|
||||
${body}
|
||||
} finally {
|
||||
${copyBack}
|
||||
${stateNames.length ? "__wrnexusCommit();" : ""}
|
||||
}
|
||||
}`;
|
||||
}
|
||||
@@ -23273,23 +23277,66 @@ ${(0, codegen_ts_1.generate)(ast)}`,
|
||||
const imports = selectedImports.map((entry) => entry.code).join(`
|
||||
`);
|
||||
const importedBindings = [...new Set(selectedImports.flatMap((entry) => entry.bindings))];
|
||||
const sharedState = state.filter((name) => safeIdentifier(name) && !RUNTIME_BINDINGS.has(name));
|
||||
const sharedProps = ast.props.map((entry) => entry.name).filter((name) => safeIdentifier(name) && !RUNTIME_BINDINGS.has(name) && !sharedState.includes(name));
|
||||
const callableAliases = functionNames.filter((name) => safeIdentifier(name) && !RUNTIME_BINDINGS.has(name) && !sharedState.includes(name) && !sharedProps.includes(name));
|
||||
const sharedCommit = sharedState.map((name) => `context.state.${name} = ${name};`).join(" ");
|
||||
const sharedRestore = [
|
||||
...sharedState.map((name) => `${name} = context.state.${name};`),
|
||||
...sharedProps.map((name) => `${name} = context.props.${name};`)
|
||||
].join(" ");
|
||||
const hasAuthoredCommit = callableAliases.includes("commit");
|
||||
const hasAuthoredSetTimeout = callableAliases.includes("setTimeout");
|
||||
const implementations = functions.map((fn) => {
|
||||
const parameters = fn.parameters.map((parameter) => parameter.name).join(", ");
|
||||
return `${JSON.stringify(fn.name)}: ${fn.async ? "async " : ""}function(${parameters}) {
|
||||
try { ${(0, syntax_1.eraseFunctionTypes)(fn.body)} } finally { __wrnexusCommit(); }
|
||||
}`;
|
||||
}).join(`,
|
||||
`);
|
||||
return `// generated WRNexusJS browser module for ${ast.name}
|
||||
${imports}
|
||||
export const __wrnexusClientFunctions = {
|
||||
${functions.map((fn) => ` ${functionEntry(ast, fn, functionNames)}`).join(`,
|
||||
${functions.map((fn) => ` ${JSON.stringify(fn.name)}: (context, ...args) => __wrnexusBindings(context)[${JSON.stringify(fn.name)}](...args)`).join(`,
|
||||
`)}
|
||||
};
|
||||
export const __wrnexusClientState = ${JSON.stringify(state)};
|
||||
export const __wrnexusOutputs = ${JSON.stringify(ast.outputs)};
|
||||
export const __wrnexusImportedBindings = { ${importedBindings.join(", ")} };
|
||||
export function bindClientScope(context) {
|
||||
function __wrnexusCreateClientFunctions(context) {
|
||||
${sharedState.length ? `let { ${sharedState.join(", ")} } = context.state;` : ""}
|
||||
${sharedProps.length ? `let { ${sharedProps.join(", ")} } = context.props;` : ""}
|
||||
const output = context.output;
|
||||
const server = context.server;
|
||||
const props = context.props;
|
||||
const refs = context.refs;
|
||||
const __wrnexusCommit = () => { ${sharedCommit} };
|
||||
const __wrnexusRestore = () => { ${sharedRestore} };
|
||||
${!hasAuthoredCommit ? "const commit = __wrnexusCommit;" : ""}
|
||||
${!hasAuthoredSetTimeout ? `const setTimeout = (callback, delay, ...args) => globalThis.setTimeout(() => {
|
||||
try { return callback(...args); } finally { __wrnexusCommit(); }
|
||||
}, delay);` : ""}
|
||||
const implementations = {
|
||||
${implementations}
|
||||
};
|
||||
${callableAliases.map((name) => `const ${name} = (...args) => implementations[${JSON.stringify(name)}](...args);`).join(`
|
||||
`)}
|
||||
const functions = {};
|
||||
const scopedContext = { ...context, functions };
|
||||
for (const [name, handler] of Object.entries(__wrnexusClientFunctions)) {
|
||||
functions[name] = (...args) => handler(scopedContext, ...args);
|
||||
for (const name of Object.keys(implementations)) {
|
||||
functions[name] = (...args) => {
|
||||
__wrnexusRestore();
|
||||
return implementations[name](...args);
|
||||
};
|
||||
}
|
||||
return functions;
|
||||
}
|
||||
function __wrnexusBindings(context) {
|
||||
return context.__wrnexusBoundFunctions ||
|
||||
(context.__wrnexusBoundFunctions = __wrnexusCreateClientFunctions(context));
|
||||
}
|
||||
export function bindClientScope(context) {
|
||||
return __wrnexusBindings(context);
|
||||
}
|
||||
`;
|
||||
}
|
||||
},
|
||||
@@ -23719,11 +23766,11 @@ export function bindClientScope(context) {
|
||||
const wholeExpression = wholeAttributeExpression(attr.value);
|
||||
const compiledValue = wholeExpression ? `\${__wireProp(${ctx.resolveExpr(wholeExpression)})}` : compileAttrValue(attr.value, ctx);
|
||||
const rendered = ` ${attr.name}="${compiledValue}"`;
|
||||
if (wholeExpression || !attr.value.includes("{") || !exprRefsState(attr.value, ctx.stateNames)) {
|
||||
if (!attr.value.includes("{") || !exprRefsState(attr.value, ctx.stateNames) && !exprRefsState(attr.value, ctx.propNames)) {
|
||||
return rendered;
|
||||
}
|
||||
const marker = attrEscape(JSON.stringify([attr.name, attr.value]));
|
||||
return rendered + ` data-wrn-bind-${bindIndex++}="${escLit(marker)}"`;
|
||||
return rendered + ` data-wrn-prop-bind-${bindIndex++}="${escLit(marker)}"`;
|
||||
}).join("");
|
||||
const loops = loopVarsOf(node);
|
||||
const childCtx = loops.length > 0 ? {
|
||||
@@ -24547,7 +24594,7 @@ ${handlers.join(`
|
||||
return false;
|
||||
}
|
||||
function exprRefsComponentReactiveValue(expr, ctx) {
|
||||
return exprRefsState(expr, ctx.stateNames) || exprRefsState(expr, ctx.functionNames);
|
||||
return exprRefsState(expr, ctx.stateNames) || exprRefsState(expr, ctx.propNames) || exprRefsState(expr, ctx.functionNames);
|
||||
}
|
||||
function viewHasEvents(nodes) {
|
||||
return nodes.some((node) => {
|
||||
@@ -24599,10 +24646,10 @@ ${handlers.join(`
|
||||
out += escLit(`<span data-t="${attrEscape(expr.slice(2).trim())}"></span>`);
|
||||
} else if (ctx.loopVars && exprRefsState(expr, ctx.loopVars)) {
|
||||
out += escLit(`{${expr}}`);
|
||||
} else if (exprRefsComponentReactiveValue(expr, ctx)) {
|
||||
out += escLit(`<span data-text="${attrEscape(expr)}">`) + `\${__wireHtml(${ctx.resolveExpr(expr)})}` + escLit(`</span>`);
|
||||
} else if (expr === "content") {
|
||||
out += `\${__wireRaw(${ctx.resolveExpr(expr)})}`;
|
||||
} else if (exprRefsComponentReactiveValue(expr, ctx)) {
|
||||
out += escLit(`<span data-text="${attrEscape(expr)}">`) + `\${__wireHtml(${ctx.resolveExpr(expr)})}` + escLit(`</span>`);
|
||||
} else {
|
||||
out += `\${__wireHtml(${ctx.resolveExpr(expr)})}`;
|
||||
}
|
||||
@@ -24855,6 +24902,7 @@ ${handlers.join(`
|
||||
};
|
||||
const ctx = {
|
||||
stateNames,
|
||||
propNames: new Set(effectiveProps.map((entry) => entry.name)),
|
||||
functionNames: new Set(ast.runtimeFunctions.filter((fn) => fn.runtime !== "server").map((fn) => fn.name)),
|
||||
resolveExpr,
|
||||
eventNames: publicOutputNames(ast)
|
||||
@@ -24867,7 +24915,7 @@ ${handlers.join(`
|
||||
const styles = ast.styles.map((body) => body.trim()).filter(Boolean);
|
||||
const styleTag = escLit(localStyleTag(ast, styles));
|
||||
const behavior = componentBehavior(ast);
|
||||
const needsScope = ast.runtime !== "server" && (browserStates.length > 0 || ast.computed.length > 0 || viewHasEvents(ast.view) || behavior !== null);
|
||||
const needsScope = ast.runtime !== "server" && (effectiveProps.length > 0 || browserStates.length > 0 || ast.computed.length > 0 || viewHasEvents(ast.view) || behavior !== null);
|
||||
if (hasServerEach || needsScope) {
|
||||
out.push(`import { Buffer as __WrnexusBuffer } from "node:buffer";`);
|
||||
}
|
||||
@@ -24881,7 +24929,7 @@ ${handlers.join(`
|
||||
if (prop.required) {
|
||||
decls.push(` if (__p[${JSON.stringify(prop.name)}] === undefined) throw new TypeError(${JSON.stringify(`${ast.name} requires prop '${prop.name}' (${prop.valueType ?? "unknown"})`)});`);
|
||||
}
|
||||
decls.push(` const ${nameRefs.get(prop.name)}: ${prop.valueType ?? "any"} = __coerce(__p[${JSON.stringify(prop.name)}], (${resolveExpr(prop.default)}), ${JSON.stringify((0, types_ts_1.runtimeTypeOf)(prop.valueType))});`);
|
||||
decls.push(` const ${nameRefs.get(prop.name)}: ${prop.valueType ?? "any"} = __coerce(__p[${JSON.stringify(prop.name)}], (${resolveExpr(prop.default)}), ${JSON.stringify((0, types_ts_1.runtimeTypeOf)(prop.valueType))}, ${JSON.stringify(prop.name)});`);
|
||||
}
|
||||
if (!effectiveProps.some((prop) => prop.name === "attrs")) {
|
||||
decls.push(` const __attrs = __restProps(__p, new Set(${JSON.stringify(effectiveProps.map((prop) => prop.name))}));`);
|
||||
@@ -24941,7 +24989,7 @@ ${ast.outputs.map((output) => ` ${JSON.stringify(output.name)}(${output.payload
|
||||
`)}
|
||||
}`);
|
||||
}
|
||||
out.push(`function __coerce(v: any, def: any, declared: string = "unknown"): any {
|
||||
out.push(`function __coerce(v: any, def: any, declared: string = "unknown", propName: string = "prop"): any {
|
||||
if (v === undefined || v === null) {
|
||||
return def;
|
||||
}
|
||||
@@ -24966,14 +25014,14 @@ ${ast.outputs.map((output) => ` ${JSON.stringify(output.name)}(${output.payload
|
||||
if (typeof v === "string") {
|
||||
try {
|
||||
const parsed = JSON.parse(v);
|
||||
return Array.isArray(parsed) ? parsed : def;
|
||||
if (!Array.isArray(parsed)) throw new TypeError("Expected an array prop '" + propName + "'");
|
||||
return parsed;
|
||||
} catch {
|
||||
if (declared === "array") throw new TypeError("Expected an array prop");
|
||||
return def;
|
||||
throw new TypeError("Expected an array prop '" + propName + "'");
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
throw new TypeError("Expected an array prop '" + propName + "'");
|
||||
}
|
||||
|
||||
if (declared === "object" || (def !== null && typeof def === "object")) {
|
||||
@@ -24989,20 +25037,16 @@ ${ast.outputs.map((output) => ` ${JSON.stringify(output.name)}(${output.payload
|
||||
try {
|
||||
const parsed = JSON.parse(v);
|
||||
|
||||
return (
|
||||
parsed !== null &&
|
||||
typeof parsed === "object" &&
|
||||
!Array.isArray(parsed)
|
||||
)
|
||||
? parsed
|
||||
: def;
|
||||
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||
throw new TypeError("Expected an object prop '" + propName + "'");
|
||||
}
|
||||
return parsed;
|
||||
} catch {
|
||||
if (declared === "object") throw new TypeError("Expected an object prop");
|
||||
return def;
|
||||
throw new TypeError("Expected an object prop '" + propName + "'");
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
throw new TypeError("Expected an object prop '" + propName + "'");
|
||||
}
|
||||
|
||||
if (declared === "bigint") return BigInt(v);
|
||||
@@ -25072,10 +25116,12 @@ function __wireSpreadAttrs(value: any): string {
|
||||
lowerName === "style" ||
|
||||
lowerName === "slot" ||
|
||||
lowerName === "data-component" ||
|
||||
// Internal markers must not leak through a spread -- except the
|
||||
// parent's output handlers, whose whole job is to ride from the mount
|
||||
// onto the view root so the mounting scope can bind them there.
|
||||
(lowerName.startsWith("data-wrn") && !lowerName.startsWith("data-wrn-out-"))
|
||||
// Internal markers must not leak through a spread. Parent-owned output
|
||||
// and prop bindings ride from the mount onto the
|
||||
// rendered child root so the mounting scope can bind them there.
|
||||
(lowerName.startsWith("data-wrn") &&
|
||||
!lowerName.startsWith("data-wrn-out-") &&
|
||||
!lowerName.startsWith("data-wrn-prop-bind-"))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -27916,7 +27962,12 @@ ${serverFunctions}
|
||||
case "props": {
|
||||
lx.next();
|
||||
expect("lbrace");
|
||||
while (lx.peek().type !== "rbrace") {
|
||||
while (true) {
|
||||
if (lx.startsWithBlockComment()) {
|
||||
throw new ParseError("Block comments are not allowed inside props {}; use // line comments instead", "WRN-PROPS-BLOCK-COMMENT");
|
||||
}
|
||||
if (lx.peek().type === "rbrace")
|
||||
break;
|
||||
const t = lx.peek();
|
||||
if (t.type === "eof")
|
||||
throw new ParseError("Unexpected end of input inside props");
|
||||
@@ -28275,6 +28326,9 @@ ${serverFunctions}
|
||||
}
|
||||
expect("rbrace");
|
||||
const declaredStates = new Set(states.map((state) => state.name));
|
||||
if (declaredStates.has("page")) {
|
||||
throw new ParseError("State name 'page' collides with the WRN 'page' keyword; choose another state name", "WRN-STATE-RESERVED-NAME");
|
||||
}
|
||||
for (const watcher of watches) {
|
||||
if (!declaredStates.has(watcher.state)) {
|
||||
throw new ParseError(`Cannot watch undeclared state '${watcher.state}'`);
|
||||
@@ -28748,6 +28802,10 @@ ${serverFunctions}
|
||||
break;
|
||||
}
|
||||
}
|
||||
startsWithBlockComment() {
|
||||
this.skipTrivia();
|
||||
return this.src[this.pos] === "/" && this.src[this.pos + 1] === "*";
|
||||
}
|
||||
next() {
|
||||
this.skipTrivia();
|
||||
const { src } = this;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// WRN editor language server source hash: 65a5536efbc7c9fee15040559bb48afe78c4686fc5e7341ffc941cc7113c8556
|
||||
// WRN editor language server source hash: 4c7e59ebeb512e23d95f4169dd80da2b951ae39c31d31919994368b8e90f5cec
|
||||
// WRN editor language server generator hash: f593a44aaf05495b789ce7a3086bee1eebb951b884d41c0e017bbcfe5f547e72
|
||||
// @bun @bun-cjs
|
||||
(function(exports, require, module, __filename, __dirname) {var __create = Object.create;
|
||||
@@ -168786,6 +168786,10 @@ class Lexer {
|
||||
break;
|
||||
}
|
||||
}
|
||||
startsWithBlockComment() {
|
||||
this.skipTrivia();
|
||||
return this.src[this.pos] === "/" && this.src[this.pos + 1] === "*";
|
||||
}
|
||||
next() {
|
||||
this.skipTrivia();
|
||||
const { src } = this;
|
||||
@@ -170521,7 +170525,12 @@ function parse(source) {
|
||||
case "props": {
|
||||
lx.next();
|
||||
expect("lbrace");
|
||||
while (lx.peek().type !== "rbrace") {
|
||||
while (true) {
|
||||
if (lx.startsWithBlockComment()) {
|
||||
throw new ParseError("Block comments are not allowed inside props {}; use // line comments instead", "WRN-PROPS-BLOCK-COMMENT");
|
||||
}
|
||||
if (lx.peek().type === "rbrace")
|
||||
break;
|
||||
const t = lx.peek();
|
||||
if (t.type === "eof")
|
||||
throw new ParseError("Unexpected end of input inside props");
|
||||
@@ -170880,6 +170889,9 @@ function parse(source) {
|
||||
}
|
||||
expect("rbrace");
|
||||
const declaredStates = new Set(states.map((state) => state.name));
|
||||
if (declaredStates.has("page")) {
|
||||
throw new ParseError("State name 'page' collides with the WRN 'page' keyword; choose another state name", "WRN-STATE-RESERVED-NAME");
|
||||
}
|
||||
for (const watcher of watches) {
|
||||
if (!declaredStates.has(watcher.state)) {
|
||||
throw new ParseError(`Cannot watch undeclared state '${watcher.state}'`);
|
||||
|
||||
Reference in New Issue
Block a user