From cbb9f95cb09d27e739d017cf7734d9f7b6149adf Mon Sep 17 00:00:00 2001 From: dpiercey Date: Wed, 6 Mar 2024 11:58:27 -0700 Subject: [PATCH] fix: avoid using mangled internal props in interop runtime --- .changeset/thick-dolphins-destroy.md | 6 + .sizes.json | 50 ++++---- .../runtime/helpers/tags-compat/dom-debug.js | 2 +- .../runtime/helpers/tags-compat/dom-debug.mjs | 4 +- .../src/runtime/helpers/tags-compat/dom.js | 2 +- .../src/runtime/helpers/tags-compat/dom.mjs | 4 +- .../helpers/tags-compat/html-debug.mjs | 2 +- .../src/runtime/helpers/tags-compat/html.mjs | 2 +- .../helpers/tags-compat/runtime-dom.js | 121 ++++++------------ packages/runtime-tags/src/dom.ts | 3 +- packages/runtime-tags/src/dom/compat.ts | 87 +++++++++++++ 11 files changed, 165 insertions(+), 118 deletions(-) create mode 100644 .changeset/thick-dolphins-destroy.md create mode 100644 packages/runtime-tags/src/dom/compat.ts diff --git a/.changeset/thick-dolphins-destroy.md b/.changeset/thick-dolphins-destroy.md new file mode 100644 index 000000000..e74ed9065 --- /dev/null +++ b/.changeset/thick-dolphins-destroy.md @@ -0,0 +1,6 @@ +--- +"@marko/runtime-tags": patch +"marko": patch +--- + +Avoid using internal mangled props from tags api in the interop runtime. diff --git a/.sizes.json b/.sizes.json index f1f4f1073..24bdf56ca 100644 --- a/.sizes.json +++ b/.sizes.json @@ -7,81 +7,81 @@ { "name": "*", "total": { - "min": 12664, - "gzip": 5402, - "brotli": 4922 + "min": 13282, + "gzip": 5676, + "brotli": 5196 } }, { "name": "counter", "user": { "min": 351, - "gzip": 275, - "brotli": 238 + "gzip": 276, + "brotli": 241 }, "runtime": { "min": 3821, - "gzip": 1808, + "gzip": 1809, "brotli": 1614 }, "total": { "min": 4172, - "gzip": 2083, - "brotli": 1852 + "gzip": 2085, + "brotli": 1855 } }, { "name": "counter 💧", "user": { "min": 204, - "gzip": 180, - "brotli": 157 + "gzip": 181, + "brotli": 152 }, "runtime": { "min": 2683, - "gzip": 1362, - "brotli": 1222 + "gzip": 1361, + "brotli": 1220 }, "total": { "min": 2887, "gzip": 1542, - "brotli": 1379 + "brotli": 1372 } }, { "name": "comments", "user": { "min": 1182, - "gzip": 696, - "brotli": 637 + "gzip": 698, + "brotli": 639 }, "runtime": { - "min": 7363, - "gzip": 3396, - "brotli": 3091 + "min": 7358, + "gzip": 3394, + "brotli": 3096 }, "total": { - "min": 8545, + "min": 8540, "gzip": 4092, - "brotli": 3728 + "brotli": 3735 } }, { "name": "comments 💧", "user": { "min": 949, - "gzip": 583, - "brotli": 544 + "gzip": 585, + "brotli": 540 }, "runtime": { "min": 7887, "gzip": 3605, - "brotli": 3284 + "brotli": 3283 }, "total": { "min": 8836, - "gzip": 4188, - "brotli": 3828 + "gzip": 4190, + "brotli": 3823 } } ] diff --git a/packages/marko/src/runtime/helpers/tags-compat/dom-debug.js b/packages/marko/src/runtime/helpers/tags-compat/dom-debug.js index ec5896b2c..e515b40e4 100644 --- a/packages/marko/src/runtime/helpers/tags-compat/dom-debug.js +++ b/packages/marko/src/runtime/helpers/tags-compat/dom-debug.js @@ -1 +1 @@ -require("./runtime-dom.js").p(require("@marko/runtime-tags/debug/dom")); +require("./runtime-dom.js").p(require("@marko/runtime-tags/debug/dom").compat); diff --git a/packages/marko/src/runtime/helpers/tags-compat/dom-debug.mjs b/packages/marko/src/runtime/helpers/tags-compat/dom-debug.mjs index 4338602ca..52a362043 100644 --- a/packages/marko/src/runtime/helpers/tags-compat/dom-debug.mjs +++ b/packages/marko/src/runtime/helpers/tags-compat/dom-debug.mjs @@ -1,3 +1,3 @@ -import api from "@marko/runtime-tags/debug/dom"; +import { compat } from "@marko/runtime-tags/debug/dom"; import { p } from "./runtime-dom.js"; -p(api); +p(compat); diff --git a/packages/marko/src/runtime/helpers/tags-compat/dom.js b/packages/marko/src/runtime/helpers/tags-compat/dom.js index ee969adb4..a03b0689a 100644 --- a/packages/marko/src/runtime/helpers/tags-compat/dom.js +++ b/packages/marko/src/runtime/helpers/tags-compat/dom.js @@ -1 +1 @@ -require("./runtime-dom.js").p(require("@marko/runtime-tags/dom")); +require("./runtime-dom.js").p(require("@marko/runtime-tags/dom").compat); diff --git a/packages/marko/src/runtime/helpers/tags-compat/dom.mjs b/packages/marko/src/runtime/helpers/tags-compat/dom.mjs index 078539aaa..0276ee5b5 100644 --- a/packages/marko/src/runtime/helpers/tags-compat/dom.mjs +++ b/packages/marko/src/runtime/helpers/tags-compat/dom.mjs @@ -1,3 +1,3 @@ -import api from "@marko/runtime-tags/dom"; +import { compat } from "@marko/runtime-tags/dom"; import { p } from "./runtime-dom.js"; -p(api); +p(compat); diff --git a/packages/marko/src/runtime/helpers/tags-compat/html-debug.mjs b/packages/marko/src/runtime/helpers/tags-compat/html-debug.mjs index 0219be591..3ccf53eea 100644 --- a/packages/marko/src/runtime/helpers/tags-compat/html-debug.mjs +++ b/packages/marko/src/runtime/helpers/tags-compat/html-debug.mjs @@ -1,3 +1,3 @@ -import api from "@marko/runtime-tags/debug/html"; +import * as api from "@marko/runtime-tags/debug/html"; import { p } from "./runtime-html.js"; export const s = p(api); diff --git a/packages/marko/src/runtime/helpers/tags-compat/html.mjs b/packages/marko/src/runtime/helpers/tags-compat/html.mjs index 2e662f102..d7f537e97 100644 --- a/packages/marko/src/runtime/helpers/tags-compat/html.mjs +++ b/packages/marko/src/runtime/helpers/tags-compat/html.mjs @@ -1,3 +1,3 @@ -import api from "@marko/runtime-tags/html"; +import * as api from "@marko/runtime-tags/html"; import { p } from "./runtime-html.js"; export const s = p(api); diff --git a/packages/marko/src/runtime/helpers/tags-compat/runtime-dom.js b/packages/marko/src/runtime/helpers/tags-compat/runtime-dom.js index 7130b47b0..50e7f788b 100644 --- a/packages/marko/src/runtime/helpers/tags-compat/runtime-dom.js +++ b/packages/marko/src/runtime/helpers/tags-compat/runtime-dom.js @@ -10,71 +10,40 @@ const morphdom = require("../../vdom/morphdom"); const { ___createFragmentNode } = require("../../vdom/morphdom/fragment"); const dynamicTag = require("../dynamic-tag"); -exports.p = function ({ - prepare, - runEffects, - patchConditionals, - createScopeWithRenderer, - queueEffect, - scopeLookup, - getRegisteredWithScope, - register, -}) { +exports.p = function (domCompat) { dynamicTag.___runtimeCompat = function tagsToVdom( - tagsRenderer, + renderer, renderBody, args, ) { - if ( - tagsRenderer - ? tagsRenderer.___clone === undefined - : !Array.isArray(renderBody) && renderBody?.___clone === undefined - ) - return tagsRenderer; + const tagsRenderer = domCompat.resolveRenderer(renderer || renderBody); - return (input, out) => - TagsCompat( - { i: args ? args : input, r: tagsRenderer || renderBody }, - out, - ); + if (tagsRenderer) { + return (input, out) => + TagsCompat({ i: args ? args : input, r: tagsRenderer }, out); + } + + return renderer; }; const TagsCompatId = "tags-compat"; const TagsCompat = createRenderer( function (_, out, componentDef, component) { - let existing = false; const isHydrate = ___getComponentsContext(out).___globalContext.___isHydrate; const input = Array.isArray(_.i) ? _.i : [_.i]; - const tagsRenderer = resolveRegistered(_.r); - const args = tagsRenderer.___args; + const tagsRenderer = domCompat.resolveRenderer(_.r); + const newNode = domCompat.render( + isHydrate, + out, + component, + tagsRenderer, + input, + ); - component.effects = prepare(() => { - if (isHydrate) { - const scopeId = out.global.componentIdToScopeId[component.id]; - component.scope = scopeLookup[scopeId]; - } - if (!component.scope) { - component.scope = createScopeWithRenderer( - tagsRenderer /* out.global as context */, - ); - for (const signal of tagsRenderer.___closureSignals) { - signal(component.scope, true); - } - } else { - args && args(component.scope, input, 1); - existing = true; - } - args && args(component.scope, input); - }); - out.bf(out.___assignedKey, component, existing); - if (!existing) { - out.node({ - ___actualize: () => - component.scope.___startNode === component.scope.___endNode - ? component.scope.___startNode - : component.scope.___startNode.parentNode, - }); + out.bf(out.___assignedKey, component, !newNode); + if (newNode) { + out.node({ ___actualize: () => newNode }); } out.ef(); }, @@ -94,12 +63,8 @@ exports.p = function ({ _: TagsCompat, Component: defineComponent( { - onMount() { - runEffects(this.effects); - }, - onUpdate() { - runEffects(this.effects); - }, + onMount: domCompat.runComponentEffects, + onUpdate: domCompat.runComponentEffects, }, TagsCompat, ), @@ -114,7 +79,7 @@ exports.p = function ({ const rendererCache = new WeakMap(); - patchConditionals((conditional) => (...args) => { + domCompat.patchConditionals((conditional) => (...args) => { const signal = conditional(...args); const hasAttrs = args.length > 1; return (scope, renderer, clean) => { @@ -124,17 +89,14 @@ exports.p = function ({ function create5to6Renderer(renderer, hasAttrs) { let newRenderer = renderer; - if (renderer) { + if (renderer && typeof renderer !== "string") { const rendererFromAnywhere = renderer._ || renderer.render || (renderer.renderer && renderer.renderer.renderer) || renderer.renderer; - const isMarko6 = rendererFromAnywhere - ? rendererFromAnywhere.___clone - : renderer.___clone; - if (typeof renderer !== "string" && !isMarko6) { + if (!domCompat.isRenderer(rendererFromAnywhere || renderer)) { newRenderer = rendererCache.get(renderer); if (!newRenderer) { const { Component } = renderer; @@ -145,29 +107,28 @@ exports.p = function ({ scopeId, ) { for (const customEvent of customEvents) { - customEvent[1] = resolveRegistered(customEvent[1]); + customEvent[1] = domCompat.resolveRenderer(customEvent[1]); } setCustomEvents.call(this, customEvents, scopeId); }; } - newRenderer = { - ___setup(scope) { + newRenderer = domCompat.createRenderer( + (scope) => { if (!hasAttrs) { renderAndMorph(scope, rendererFromAnywhere, renderer, {}); } }, - ___clone() { + () => { const realFragment = document.createDocumentFragment(); ___createFragmentNode(null, null, realFragment); return realFragment; }, - ___hasUserEffects: 1, - ___args(scope, input, clean) { + (scope, input, clean) => { if (clean) return; renderAndMorph(scope, rendererFromAnywhere, renderer, input); }, - }; + ); rendererCache.set(renderer, newRenderer); } } @@ -175,18 +136,18 @@ exports.p = function ({ return newRenderer; } - register("@marko/tags-compat-5-to-6", create5to6Renderer); + domCompat.register("@marko/tags-compat-5-to-6", create5to6Renderer); function renderAndMorph(scope, renderer, renderBody, input) { const out = defaultCreateOut(); - let rootNode = scope.___startNode.fragment; + let host = domCompat.getStartNode(scope); + let rootNode = host.fragment; if (!rootNode) { const component = (scope.marko5Component = ___componentLookup[scope.m5c]); rootNode = component.___rootNode; - scope.___startNode = rootNode.startNode; - scope.___endNode = rootNode.endNode; + host = rootNode.startNode; + domCompat.setScopeNodes(host, rootNode.endNode); } - const host = scope.___startNode; const existingComponent = scope.marko5Component; const componentsContext = ___getComponentsContext(out); const globalComponentsContext = componentsContext.___globalContext; @@ -215,7 +176,7 @@ exports.p = function ({ RenderBodyComponent({ renderBody, args: input }, out); } - queueEffect(scope, () => { + domCompat.queueEffect(scope, () => { const targetNode = out.___getOutput().___firstChild; morphdom(rootNode, targetNode, host, componentsContext); const componentDefs = componentsContext.___initComponents( @@ -267,12 +228,4 @@ exports.p = function ({ _: RenderBodyComponent, Component: defineComponent({}, RenderBodyComponent), })); - - function resolveRegistered(renderer) { - if (!Array.isArray(renderer)) return renderer; - - const [registerId, scopeId] = renderer; - const scope = scopeLookup[scopeId]; - return getRegisteredWithScope(registerId, scope); - } }; diff --git a/packages/runtime-tags/src/dom.ts b/packages/runtime-tags/src/dom.ts index 3c199ae1a..47de56a9f 100644 --- a/packages/runtime-tags/src/dom.ts +++ b/packages/runtime-tags/src/dom.ts @@ -6,7 +6,6 @@ export { loopIn, loopTo, inLoopScope, - patchConditionals, } from "./dom/control-flow"; export { @@ -67,3 +66,5 @@ export { values, intersections, } from "./dom/signals"; + +export { compat } from "./dom/compat"; diff --git a/packages/runtime-tags/src/dom/compat.ts b/packages/runtime-tags/src/dom/compat.ts new file mode 100644 index 000000000..d858807ea --- /dev/null +++ b/packages/runtime-tags/src/dom/compat.ts @@ -0,0 +1,87 @@ +import { patchConditionals } from "./control-flow"; +import { prepare, queueEffect, runEffects } from "./queue"; +import { + createRenderer, + createScopeWithRenderer, + type Renderer, +} from "./renderer"; +import { getRegisteredWithScope, register, scopeLookup } from "./resume"; + +export const compat = { + register, + patchConditionals, + queueEffect, + isRenderer(renderer: any) { + return renderer.___clone !== undefined; + }, + getStartNode(scope: any) { + return scope.___startNode; + }, + setScopeNodes(scope: any, startNode: Node, endNode: Node) { + scope.___startNode = startNode; + scope.___endNode = endNode; + }, + runComponentEffects(this: any) { + runEffects(this.effects); + }, + resolveRenderer(renderer: any) { + if (renderer && typeof renderer === "object") { + if (Array.isArray(renderer)) { + const [registerId, scopeId] = renderer; + const scope = scopeLookup[scopeId]; + return getRegisteredWithScope(registerId, scope); + } + + if (renderer.___clone) { + return renderer; + } + } + }, + createRenderer( + setup: Renderer["___setup"], + clone: Renderer["___clone"], + args: Renderer["___args"], + ) { + const renderer = createRenderer("", undefined, setup, undefined, 1, args); + renderer.___clone = clone; + return renderer; + }, + render( + isHydrate: boolean, + out: any, + component: any, + renderer: Renderer, + input: any, + ) { + const args = renderer.___args || noop; + let existing = false; + let scope: any = isHydrate + ? (component.scope = + scopeLookup[(out.global.componentIdToScopeId as any)[component.id]]) + : component.scope; + + component.effects = prepare(() => { + if (!scope) { + scope = component.scope = createScopeWithRenderer(renderer, out.global); + const closures = renderer.___closureSignals; + if (closures) { + for (const signal of closures) { + signal(component.scope, true); + } + } + } else { + args(scope, input, 1); + existing = true; + } + args(scope, input); + }); + + if (!existing) { + return scope.___startNode === scope.___endNode + ? scope.___startNode + : scope.___startNode.parentNode; + } + }, +}; + +function noop() {}