fix: class/tags compat improvements

This commit is contained in:
dpiercey 2025-08-28 07:58:05 -07:00 committed by Dylan Piercey
parent 20f45d39b1
commit bbde0e8415
22 changed files with 166 additions and 82 deletions

View File

@ -0,0 +1,5 @@
---
"@marko/runtime-tags": patch
---
Fix issue with compat layer scope serialized async.

View File

@ -0,0 +1,5 @@
---
"marko": patch
---
Allow split components to rerender if the template has been loaded.

View File

@ -0,0 +1,7 @@
---
"@marko/translator-interop-class-tags": patch
"marko": patch
"@marko/runtime-tags": patch
---
Ensure interop class/tags init happens after all modules load.

View File

@ -50,7 +50,7 @@ exports.p = function (domCompat) {
const TagsCompat = createRenderer(
function (_, out, componentDef, component) {
const input = Array.isArray(_.i) ? _.i : [_.i];
const tagsRenderer = domCompat.resolveRegistered(_.r, global);
const tagsRenderer = domCompat.resolveRegistered(_.r, out.global);
const newNode = domCompat.render(out, component, tagsRenderer, input);
out.bf("1", component, !newNode);

View File

@ -70,17 +70,19 @@ exports.p = function (htmlCompat) {
return tagsRenderer;
}
if (!tagsRenderer && renderBody) {
renderBody.toJSON = htmlCompat.toJSON;
}
return (input, out) =>
return (input, out) => {
if (!tagsRenderer && renderBody) {
renderBody.toJSON = htmlCompat.toJSON(
htmlCompat.ensureState(out.global),
);
}
TagsCompat(
args
? { i: args, r: (args) => (tagsRenderer || renderBody)(...args) }
: { i: input, r: tagsRenderer || renderBody },
out,
);
};
};
const TagsCompatId = "tags-compat";
@ -147,7 +149,7 @@ exports.p = function (htmlCompat) {
return (input, ...args) => {
// tags to class
const $global = htmlCompat.$global();
htmlCompat.ensureState($global);
const state = htmlCompat.ensureState($global);
let writers = writersByGlobal.get($global);
if (!writers) {
writersByGlobal.set($global, (writers = { classAPI: [], tagsAPI: [] }));
@ -169,7 +171,7 @@ exports.p = function (htmlCompat) {
key[2] === "-" ? key.slice(3) : key.slice(2).toLowerCase(),
value,
]);
value.toJSON = htmlCompat.toJSON;
value.toJSON = htmlCompat.toJSON(state);
}
} else {
input[key === "content" ? "renderBody" : key] = value;

View File

@ -428,32 +428,44 @@ export const translate = {
);
}
path.pushContainer(
"body",
t.expressionStatement(
t.assignmentExpression(
"=",
templateRendererMember,
t.callExpression(rendererIdentifier, [
t.functionExpression(
null,
[
t.identifier("input"),
t.identifier("out"),
file._componentDefIdentifier,
file._componentInstanceIdentifier,
t.identifier("state"),
t.identifier("$global"),
],
renderBlock.node,
),
t.objectExpression(templateRenderOptionsProps),
componentIdentifier,
]),
let rendererAssignment = t.assignmentExpression(
"=",
templateRendererMember,
t.callExpression(rendererIdentifier, [
t.functionExpression(
null,
[
t.identifier("input"),
t.identifier("out"),
file._componentDefIdentifier,
file._componentInstanceIdentifier,
t.identifier("state"),
t.identifier("$global"),
],
renderBlock.node,
),
),
t.objectExpression(templateRenderOptionsProps),
componentIdentifier,
]),
);
if (!isHTML && componentBrowserFile && !meta.implicitSplitComponent) {
rendererAssignment = t.assignmentExpression(
"=",
t.memberExpression(
importDefault(
file,
resolveRelativePath(file, componentBrowserFile),
"marko_split_component",
),
t.identifier("renderer"),
),
rendererAssignment,
);
}
path.pushContainer("body", t.expressionStatement(rendererAssignment));
if (meta.implicitSplitComponent && isHTML) {
renderBlock.unshiftContainer(
"body",

View File

@ -43,7 +43,7 @@ export default (entryFile, isHydrate) => {
};
export const entryBuilder = {
build(entryFile) {
build(entryFile, exportInit) {
const state = entryFile[kEntryState];
if (!state) {
throw entryFile.path.buildCodeFrameError(
@ -53,6 +53,7 @@ export const entryBuilder = {
const { markoOpts } = entryFile;
const entryMarkoMeta = entryFile.metadata.marko;
const { body } = state;
let didExportInit = false;
entryMarkoMeta.watchFiles = [...state.watchFiles];
entryMarkoMeta.deps = [...state.lassoDeps];
@ -69,19 +70,29 @@ export const entryBuilder = {
body.unshift(markoComponentsImport);
if (markoOpts.hydrateInit) {
if (markoOpts.hydrateInit || exportInit) {
const initExpression = t.callExpression(
initId,
markoOpts.runtimeId ? [t.stringLiteral(markoOpts.runtimeId)] : [],
);
markoComponentsImport.specifiers.push(
t.importSpecifier(initId, initId),
);
body.push(
t.expressionStatement(
t.callExpression(
initId,
markoOpts.runtimeId ? [t.stringLiteral(markoOpts.runtimeId)] : [],
),
),
exportInit
? t.exportDefaultDeclaration(
t.arrowFunctionExpression([], initExpression),
)
: t.expressionStatement(initExpression),
);
}
} else if (exportInit) {
body.push(
t.exportDefaultDeclaration(
t.arrowFunctionExpression([], t.blockStatement([])),
),
);
}
return body;

View File

@ -8,7 +8,7 @@ import { r as _marko_registerComponent } from "marko/src/runtime/components/regi
import _marko_split_component from "./template.component-browser.js";
_marko_registerComponent(_marko_componentType, () => _marko_split_component);
const _marko_component2 = _marko_component;
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_split_component.renderer = _marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
out.e("div", null, "0", _component, 0, 0);
}, {
t: _marko_componentType,

View File

@ -10,7 +10,7 @@ import { r as _marko_registerComponent } from "marko/dist/runtime/components/reg
import _marko_split_component from "./template.component-browser.js";
_marko_registerComponent(_marko_componentType, () => _marko_split_component);
const _marko_component2 = _marko_component;
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_split_component.renderer = _marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
out.n(_marko_node, _component);
}, {
t: _marko_componentType,

View File

@ -7,7 +7,7 @@ import { r as _marko_registerComponent } from "marko/src/runtime/components/regi
import _marko_split_component from "./template.component-browser.js";
_marko_registerComponent(_marko_componentType, () => _marko_split_component);
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_split_component.renderer = _marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
out.e("div", null, "0", _component, 0, 0);
}, {
t: _marko_componentType,

View File

@ -9,7 +9,7 @@ import { r as _marko_registerComponent } from "marko/dist/runtime/components/reg
import _marko_split_component from "./template.component-browser.js";
_marko_registerComponent(_marko_componentType, () => _marko_split_component);
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_split_component.renderer = _marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
out.n(_marko_node, _component);
}, {
t: _marko_componentType,

View File

@ -22,6 +22,7 @@ import {
write,
writeEffect,
writeScope,
writeScopeToState,
writeScript,
} from "./writer";
@ -68,23 +69,25 @@ export const compat = {
writeScope(branchId, { m5c });
writeEffect(branchId, SET_SCOPE_REGISTER_ID);
},
toJSON(this: WeakKey) {
let compatRegistered = COMPAT_REGISTRY.get(this);
if (!compatRegistered) {
const registered = getRegistered(this);
if (registered) {
const scopeId = getScopeId(registered.scope as Scope);
if (scopeId !== undefined) {
writeScope(scopeId, {});
toJSON(state: State) {
return function toJSON(this: WeakKey) {
let compatRegistered = COMPAT_REGISTRY.get(this);
if (!compatRegistered) {
const registered = getRegistered(this);
if (registered) {
const scopeId = getScopeId(registered.scope as Scope);
if (scopeId !== undefined) {
writeScopeToState(state, scopeId, {});
}
COMPAT_REGISTRY.set(
this,
(compatRegistered = [registered.id, scopeId]),
);
}
COMPAT_REGISTRY.set(
this,
(compatRegistered = [registered.id, scopeId]),
);
}
}
return compatRegistered;
return compatRegistered;
};
},
render(
renderer: ServerRenderer,

View File

@ -505,7 +505,14 @@ function writeBranchEnd(
}
let writeScope = (scopeId: number, partialScope: PartialScope) => {
const { state } = $chunk.boundary;
return writeScopeToState($chunk.boundary.state, scopeId, partialScope);
};
export function writeScopeToState(
state: State,
scopeId: number,
partialScope: PartialScope,
) {
const { scopes } = state;
let scope: ScopeInternals | undefined = scopes.get(scopeId);
state.needsMainRuntime = true;
@ -525,7 +532,7 @@ let writeScope = (scopeId: number, partialScope: PartialScope) => {
}
return scope;
};
}
if (MARKO_DEBUG) {
writeScope = (

View File

@ -20,7 +20,7 @@ type EntryFile = t.BabelFile & {
const kState: unique symbol = Symbol();
export default {
build(entryFile: EntryFile) {
build(entryFile: EntryFile, exportInit?: boolean) {
const state = entryFile[kState];
if (!state) {
throw entryFile.path.buildCodeFrameError(
@ -42,12 +42,22 @@ export default {
),
);
const { runtimeId } = entryFile.markoOpts;
const initExpression = t.callExpression(
t.identifier("init"),
runtimeId ? [t.stringLiteral(runtimeId)] : [],
);
body.push(
t.expressionStatement(
t.callExpression(
t.identifier("init"),
runtimeId ? [t.stringLiteral(runtimeId)] : [],
),
exportInit
? t.exportDefaultDeclaration(
t.arrowFunctionExpression([], initExpression),
)
: t.expressionStatement(initExpression),
);
} else if (exportInit) {
body.push(
t.exportDefaultDeclaration(
t.arrowFunctionExpression([], t.blockStatement([])),
),
);
}

View File

@ -1,2 +1,4 @@
import "virtual:./template.marko.hydrate-5.js ";
import "virtual:./template.marko.hydrate-6.js import \"./tags/hello.marko\";";
import init6 from "virtual:./template.marko.hydrate-6.js import \"./tags/hello.marko\";\nexport default () => {};";
import init5 from "virtual:./template.marko.hydrate-5.js export default () => {};";
init6()
init5()

View File

@ -1,2 +1,4 @@
import "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./components/class-counter.marko\";\ninit();";
import "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\ninit();";
import init6 from "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\nexport default () => init();";
import init5 from "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./components/class-counter.marko\";\nexport default () => init();";
init6()
init5()

View File

@ -1,2 +1,4 @@
import "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./tags/components/hello-internal.marko\";\ninit();";
import "virtual:./template.marko.hydrate-6.js import \"./tags/hello.marko\";";
import init6 from "virtual:./template.marko.hydrate-6.js import \"./tags/hello.marko\";\nexport default () => {};";
import init5 from "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./tags/components/hello-internal.marko\";\nexport default () => init();";
init6()
init5()

View File

@ -1,2 +1,4 @@
import "virtual:./template.marko.hydrate-5.js import { register, init } from \"marko/src/runtime/components/index.js\";\nimport component_0 from \"marko/src/runtime/helpers/empty-component.js\";\nregister(\"__tests__/components/my-button.marko\", component_0);\ninit();";
import "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\ninit();";
import init6 from "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\nexport default () => init();";
import init5 from "virtual:./template.marko.hydrate-5.js import { register, init } from \"marko/src/runtime/components/index.js\";\nimport component_0 from \"marko/src/runtime/helpers/empty-component.js\";\nregister(\"__tests__/components/my-button.marko\", component_0);\nexport default () => init();";
init6()
init5()

View File

@ -1,2 +1,4 @@
import "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./components/class-counter.marko\";\ninit();";
import "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\ninit();";
import init6 from "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\nexport default () => init();";
import init5 from "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./components/class-counter.marko\";\nexport default () => init();";
init6()
init5()

View File

@ -1,2 +1,4 @@
import "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./components/class-layout.marko\";\ninit();";
import "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\ninit();";
import init6 from "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\nexport default () => init();";
import init5 from "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./components/class-layout.marko\";\nexport default () => init();";
init6()
init5()

View File

@ -1,2 +1,4 @@
import "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./components/class-layout.marko\";\ninit();";
import "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\ninit();";
import init6 from "virtual:./template.marko.hydrate-6.js import { init } from \"@marko/runtime-tags/debug/dom\";\nimport \"./template.marko\";\nexport default () => init();";
import init5 from "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./components/class-layout.marko\";\nexport default () => init();";
init6()
init5()

View File

@ -92,7 +92,7 @@ function patchTranslateProgram(visitor: t.Visitor) {
statements: t.Statement[],
) => {
return t.importDeclaration(
[],
[t.importDefaultSpecifier(t.identifier(`init${name}`))],
t.stringLiteral(
resolveVirtualDependency!(filename, {
code: generate(t.program(statements) as any, generatorOpts)
@ -103,8 +103,16 @@ function patchTranslateProgram(visitor: t.Visitor) {
);
};
return [
importHydrateProgram("5", internalEntryBuilder5.build(entryFile)),
importHydrateProgram("6", internalEntryBuilder6.build(entryFile)),
importHydrateProgram(
"6",
internalEntryBuilder6.build(entryFile, true),
),
importHydrateProgram(
"5",
internalEntryBuilder5.build(entryFile, true),
),
t.callExpression(t.identifier("init6"), []),
t.callExpression(t.identifier("init5"), []),
];
} else {
return internalEntryBuilder5.build(entryFile);