feat(csr): diagnose missing output binding functions
This commit is contained in:
@@ -210,6 +210,17 @@ export const REACTIVE_RUNTIME = String.raw`
|
|||||||
// CustomEvent can be unavailable in minimal DOM test environments.
|
// CustomEvent can be unavailable in minimal DOM test environments.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function warnMissingBindingFunction(statement, resolve, element) {
|
||||||
|
var match = /^\s*([A-Za-z_$][\w$]*)\s*\(/.exec(statement || "");
|
||||||
|
if (!match || typeof resolve(match[1]) === "function") return;
|
||||||
|
warnOnce(
|
||||||
|
"WRN-DEV-BINDING-MISSING",
|
||||||
|
"Component binding calls '" + match[1] + "', but that function does not exist in the parent scope.",
|
||||||
|
element,
|
||||||
|
{ binding: statement, functionName: match[1] },
|
||||||
|
);
|
||||||
|
}
|
||||||
/*__WRNEXUS_DEV_END__*/
|
/*__WRNEXUS_DEV_END__*/
|
||||||
|
|
||||||
function scheduleUpdateHook(element, callback) {
|
function scheduleUpdateHook(element, callback) {
|
||||||
@@ -2514,6 +2525,9 @@ export const REACTIVE_RUNTIME = String.raw`
|
|||||||
locals.event = undefined;
|
locals.event = undefined;
|
||||||
locals.$event = undefined;
|
locals.$event = undefined;
|
||||||
try {
|
try {
|
||||||
|
/*__WRNEXUS_DEV_START__*/
|
||||||
|
warnMissingBindingFunction(outStmt, function (name) { return peekScope(name); }, componentRoot);
|
||||||
|
/*__WRNEXUS_DEV_END__*/
|
||||||
return runStmt(outStmt, locals);
|
return runStmt(outStmt, locals);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
|
|||||||
@@ -689,6 +689,41 @@ test("development runtime warns once for an output with no parent binding", () =
|
|||||||
expect(String(warnings[0]?.[0])).toContain("WRN-DEV-OUTPUT-UNHANDLED");
|
expect(String(warnings[0]?.[0])).toContain("WRN-DEV-OUTPUT-UNHANDLED");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("development runtime warns when a component binding names a missing function", () => {
|
||||||
|
const win = new Window() as unknown as Window & Record<string, unknown>;
|
||||||
|
win.document.body.innerHTML =
|
||||||
|
`<div data-scope="">` +
|
||||||
|
`<div data-scope="" data-wrn-hydration="Child:x">` +
|
||||||
|
`<div data-wrn-events="save" data-wrn-out-save="missingSave(payload)"></div>` +
|
||||||
|
`</div></div>`;
|
||||||
|
(globalThis as Record<string, unknown>).window = win;
|
||||||
|
(globalThis as Record<string, unknown>).document = win.document;
|
||||||
|
(globalThis as Record<string, unknown>).location = win.location;
|
||||||
|
(globalThis as Record<string, unknown>).NodeFilter = (win as unknown as { NodeFilter: unknown }).NodeFilter;
|
||||||
|
(globalThis as Record<string, unknown>).MutationObserver = (win as unknown as { MutationObserver: unknown }).MutationObserver;
|
||||||
|
(globalThis as Record<string, unknown>).CustomEvent = (win as unknown as { CustomEvent: unknown }).CustomEvent;
|
||||||
|
const warnings: unknown[][] = [];
|
||||||
|
const originalWarn = console.warn;
|
||||||
|
const originalError = console.error;
|
||||||
|
console.warn = (...args: unknown[]) => warnings.push(args);
|
||||||
|
console.error = () => {};
|
||||||
|
try {
|
||||||
|
(0, eval)(getReactiveRuntime(true));
|
||||||
|
(win as unknown as { __wrnexusHydrateScopes?: (root: unknown) => void }).__wrnexusHydrateScopes?.(win.document);
|
||||||
|
const target = win.document.querySelector("[data-wrn-events]") as unknown as {
|
||||||
|
__wrnexusOutputHandlers?: Record<string, Set<(payload: unknown) => unknown>>;
|
||||||
|
};
|
||||||
|
target.__wrnexusOutputHandlers?.save?.forEach((handler) => handler({ id: 1 }));
|
||||||
|
target.__wrnexusOutputHandlers?.save?.forEach((handler) => handler({ id: 2 }));
|
||||||
|
} finally {
|
||||||
|
console.warn = originalWarn;
|
||||||
|
console.error = originalError;
|
||||||
|
}
|
||||||
|
expect(warnings).toHaveLength(1);
|
||||||
|
expect(String(warnings[0]?.[0])).toContain("WRN-DEV-BINDING-MISSING");
|
||||||
|
expect(String(warnings[0]?.[0])).toContain("missingSave");
|
||||||
|
});
|
||||||
|
|
||||||
test("production runtime strips development diagnostics", () => {
|
test("production runtime strips development diagnostics", () => {
|
||||||
const production = getReactiveRuntime();
|
const production = getReactiveRuntime();
|
||||||
expect(production).not.toContain("WRN-DEV-");
|
expect(production).not.toContain("WRN-DEV-");
|
||||||
|
|||||||
Reference in New Issue
Block a user