fix: avoid using mangled internal props in interop runtime

This commit is contained in:
dpiercey 2024-03-06 11:58:27 -07:00 committed by Dylan Piercey
parent 1ff2b0af54
commit cbb9f95cb0
11 changed files with 165 additions and 118 deletions

View File

@ -0,0 +1,6 @@
---
"@marko/runtime-tags": patch
"marko": patch
---
Avoid using internal mangled props from tags api in the interop runtime.

View File

@ -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
}
}
]

View File

@ -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);

View File

@ -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);

View File

@ -1 +1 @@
require("./runtime-dom.js").p(require("@marko/runtime-tags/dom"));
require("./runtime-dom.js").p(require("@marko/runtime-tags/dom").compat);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
};

View File

@ -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";

View File

@ -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() {}