marko 6.3.27 → 6.3.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/debug/dom.js CHANGED
@@ -1694,7 +1694,7 @@ function _if(nodeAccessor, ...branchesArgs) {
1694
1694
  if (newBranch !== (scope[branchAccessor] ?? (scope["BranchScopes:" + nodeAccessor] && 0))) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
1695
1695
  };
1696
1696
  }
1697
- function _show(nodeAccessor, startNodeAccessor) {
1697
+ function _show(nodeAccessor, startNodeAccessor, endNodeAccessor) {
1698
1698
  const rangeAccessor = BranchScopes$1 + nodeAccessor;
1699
1699
  enableBranches();
1700
1700
  return (scope, display) => {
@@ -1705,7 +1705,7 @@ function _show(nodeAccessor, startNodeAccessor) {
1705
1705
  if (!range) {
1706
1706
  range = scope[rangeAccessor] = {};
1707
1707
  range[StartNode] = onlyChild ? parentNode.firstChild : scope[startNodeAccessor];
1708
- range[EndNode] = onlyChild ? parentNode.lastChild : referenceNode.previousSibling;
1708
+ range[EndNode] = onlyChild ? parentNode.lastChild : endNodeAccessor === void 0 ? referenceNode.previousSibling : scope[endNodeAccessor];
1709
1709
  }
1710
1710
  let startNode = range[StartNode];
1711
1711
  if (range["#Id"] && startNode === range["#EndNode"] && startNode.tagName === "T") {
@@ -1716,10 +1716,16 @@ function _show(nodeAccessor, startNodeAccessor) {
1716
1716
  range[EndNode] = wrapper.lastChild;
1717
1717
  wrapper.replaceWith(...wrapper.childNodes);
1718
1718
  }
1719
- const inDom = startNode.parentNode === parentNode;
1719
+ const inDom = onlyChild ? !!parentNode.firstChild : startNode.parentNode === parentNode;
1720
1720
  if (display) {
1721
1721
  if (!inDom) insertBranchBefore(range, parentNode, onlyChild ? null : referenceNode);
1722
- } else if (inDom) tempDetachBranch(range);
1722
+ } else if (inDom) {
1723
+ if (onlyChild) {
1724
+ range[StartNode] = parentNode.firstChild;
1725
+ range[EndNode] = parentNode.lastChild;
1726
+ }
1727
+ tempDetachBranch(range);
1728
+ }
1723
1729
  };
1724
1730
  }
1725
1731
  function patchDynamicTag(fn) {
@@ -1692,7 +1692,7 @@ function _if(nodeAccessor, ...branchesArgs) {
1692
1692
  if (newBranch !== (scope[branchAccessor] ?? (scope["BranchScopes:" + nodeAccessor] && 0))) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
1693
1693
  };
1694
1694
  }
1695
- function _show(nodeAccessor, startNodeAccessor) {
1695
+ function _show(nodeAccessor, startNodeAccessor, endNodeAccessor) {
1696
1696
  const rangeAccessor = BranchScopes$1 + nodeAccessor;
1697
1697
  enableBranches();
1698
1698
  return (scope, display) => {
@@ -1703,7 +1703,7 @@ function _show(nodeAccessor, startNodeAccessor) {
1703
1703
  if (!range) {
1704
1704
  range = scope[rangeAccessor] = {};
1705
1705
  range[StartNode] = onlyChild ? parentNode.firstChild : scope[startNodeAccessor];
1706
- range[EndNode] = onlyChild ? parentNode.lastChild : referenceNode.previousSibling;
1706
+ range[EndNode] = onlyChild ? parentNode.lastChild : endNodeAccessor === void 0 ? referenceNode.previousSibling : scope[endNodeAccessor];
1707
1707
  }
1708
1708
  let startNode = range[StartNode];
1709
1709
  if (range["#Id"] && startNode === range["#EndNode"] && startNode.tagName === "T") {
@@ -1714,10 +1714,16 @@ function _show(nodeAccessor, startNodeAccessor) {
1714
1714
  range[EndNode] = wrapper.lastChild;
1715
1715
  wrapper.replaceWith(...wrapper.childNodes);
1716
1716
  }
1717
- const inDom = startNode.parentNode === parentNode;
1717
+ const inDom = onlyChild ? !!parentNode.firstChild : startNode.parentNode === parentNode;
1718
1718
  if (display) {
1719
1719
  if (!inDom) insertBranchBefore(range, parentNode, onlyChild ? null : referenceNode);
1720
- } else if (inDom) tempDetachBranch(range);
1720
+ } else if (inDom) {
1721
+ if (onlyChild) {
1722
+ range[StartNode] = parentNode.firstChild;
1723
+ range[EndNode] = parentNode.lastChild;
1724
+ }
1725
+ tempDetachBranch(range);
1726
+ }
1721
1727
  };
1722
1728
  }
1723
1729
  function patchDynamicTag(fn) {
@@ -10,7 +10,7 @@ export declare function _try(nodeAccessor: EncodedAccessor, template?: string |
10
10
  }) => void;
11
11
  export declare function renderCatch(scope: Scope, error: unknown): void;
12
12
  export declare function _if(nodeAccessor: EncodedAccessor, ...branchesArgs: (string | SetupFn | 0)[]): (scope: Scope, newBranch: number) => void;
13
- export declare function _show(nodeAccessor: EncodedAccessor, startNodeAccessor?: EncodedAccessor): (scope: Scope, display: unknown) => void;
13
+ export declare function _show(nodeAccessor: EncodedAccessor, startNodeAccessor?: EncodedAccessor, endNodeAccessor?: EncodedAccessor): (scope: Scope, display: unknown) => void;
14
14
  export declare function patchDynamicTag(fn: <T extends typeof _dynamic_tag>(cond: T) => T): void;
15
15
  export declare let _dynamic_tag: (nodeAccessor: EncodedAccessor, getContent?: ((scope: Scope) => Renderer) | 0, getTagVar?: (() => Signal<unknown>) | 0, inputIsArgs?: 1) => Signal<Renderer | string | undefined>;
16
16
  export declare function _dynamic_tag_content(nodeAccessor: EncodedAccessor): Signal<Renderer | undefined>;
package/dist/dom.js CHANGED
@@ -1054,19 +1054,19 @@ function _if(nodeAccessor, ...branchesArgs) {
1054
1054
  newBranch !== (scope[branchAccessor] ?? (scope["A" + nodeAccessor] && 0)) && setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
1055
1055
  };
1056
1056
  }
1057
- function _show(nodeAccessor, startNodeAccessor) {
1058
- nodeAccessor = decodeAccessor(nodeAccessor), startNodeAccessor !== void 0 && (startNodeAccessor = decodeAccessor(startNodeAccessor));
1057
+ function _show(nodeAccessor, startNodeAccessor, endNodeAccessor) {
1058
+ nodeAccessor = decodeAccessor(nodeAccessor), startNodeAccessor !== void 0 && (startNodeAccessor = decodeAccessor(startNodeAccessor)), endNodeAccessor !== void 0 && (endNodeAccessor = decodeAccessor(endNodeAccessor));
1059
1059
  let rangeAccessor = "A" + nodeAccessor;
1060
1060
  return enableBranches(), (scope, display) => {
1061
1061
  let referenceNode = scope[nodeAccessor], onlyChild = referenceNode.nodeType === 1, parentNode = onlyChild ? referenceNode : referenceNode.parentNode, range = scope[rangeAccessor];
1062
- range || (range = scope[rangeAccessor] = {}, range.S = onlyChild ? parentNode.firstChild : scope[startNodeAccessor], range.K = onlyChild ? parentNode.lastChild : referenceNode.previousSibling);
1062
+ range || (range = scope[rangeAccessor] = {}, range.S = onlyChild ? parentNode.firstChild : scope[startNodeAccessor], range.K = onlyChild ? parentNode.lastChild : endNodeAccessor === void 0 ? referenceNode.previousSibling : scope[endNodeAccessor]);
1063
1063
  let startNode = range.S;
1064
1064
  if (range.L && startNode === range.K && startNode.tagName === "T") {
1065
1065
  let wrapper = startNode;
1066
1066
  wrapper.firstChild || wrapper.appendChild(new Text()), range = scope[rangeAccessor] = {}, range.S = startNode = wrapper.firstChild, range.K = wrapper.lastChild, wrapper.replaceWith(...wrapper.childNodes);
1067
1067
  }
1068
- let inDom = startNode.parentNode === parentNode;
1069
- display ? inDom || insertBranchBefore(range, parentNode, onlyChild ? null : referenceNode) : inDom && tempDetachBranch(range);
1068
+ let inDom = onlyChild ? !!parentNode.firstChild : startNode.parentNode === parentNode;
1069
+ display ? inDom || insertBranchBefore(range, parentNode, onlyChild ? null : referenceNode) : inDom && (onlyChild && (range.S = parentNode.firstChild, range.K = parentNode.lastChild), tempDetachBranch(range));
1070
1070
  };
1071
1071
  }
1072
1072
  function patchDynamicTag(fn) {
package/dist/dom.mjs CHANGED
@@ -1053,19 +1053,19 @@ function _if(nodeAccessor, ...branchesArgs) {
1053
1053
  newBranch !== (scope[branchAccessor] ?? (scope["A" + nodeAccessor] && 0)) && setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
1054
1054
  };
1055
1055
  }
1056
- function _show(nodeAccessor, startNodeAccessor) {
1057
- nodeAccessor = decodeAccessor(nodeAccessor), startNodeAccessor !== void 0 && (startNodeAccessor = decodeAccessor(startNodeAccessor));
1056
+ function _show(nodeAccessor, startNodeAccessor, endNodeAccessor) {
1057
+ nodeAccessor = decodeAccessor(nodeAccessor), startNodeAccessor !== void 0 && (startNodeAccessor = decodeAccessor(startNodeAccessor)), endNodeAccessor !== void 0 && (endNodeAccessor = decodeAccessor(endNodeAccessor));
1058
1058
  let rangeAccessor = "A" + nodeAccessor;
1059
1059
  return enableBranches(), (scope, display) => {
1060
1060
  let referenceNode = scope[nodeAccessor], onlyChild = referenceNode.nodeType === 1, parentNode = onlyChild ? referenceNode : referenceNode.parentNode, range = scope[rangeAccessor];
1061
- range || (range = scope[rangeAccessor] = {}, range.S = onlyChild ? parentNode.firstChild : scope[startNodeAccessor], range.K = onlyChild ? parentNode.lastChild : referenceNode.previousSibling);
1061
+ range || (range = scope[rangeAccessor] = {}, range.S = onlyChild ? parentNode.firstChild : scope[startNodeAccessor], range.K = onlyChild ? parentNode.lastChild : endNodeAccessor === void 0 ? referenceNode.previousSibling : scope[endNodeAccessor]);
1062
1062
  let startNode = range.S;
1063
1063
  if (range.L && startNode === range.K && startNode.tagName === "T") {
1064
1064
  let wrapper = startNode;
1065
1065
  wrapper.firstChild || wrapper.appendChild(new Text()), range = scope[rangeAccessor] = {}, range.S = startNode = wrapper.firstChild, range.K = wrapper.lastChild, wrapper.replaceWith(...wrapper.childNodes);
1066
1066
  }
1067
- let inDom = startNode.parentNode === parentNode;
1068
- display ? inDom || insertBranchBefore(range, parentNode, onlyChild ? null : referenceNode) : inDom && tempDetachBranch(range);
1067
+ let inDom = onlyChild ? !!parentNode.firstChild : startNode.parentNode === parentNode;
1068
+ display ? inDom || insertBranchBefore(range, parentNode, onlyChild ? null : referenceNode) : inDom && (onlyChild && (range.S = parentNode.firstChild, range.K = parentNode.lastChild), tempDetachBranch(range));
1069
1069
  };
1070
1070
  }
1071
1071
  function patchDynamicTag(fn) {
@@ -2,12 +2,14 @@ import { types as t } from "@marko/compiler";
2
2
  import { type Tag } from "@marko/compiler/babel-utils";
3
3
  import { type Binding } from "../util/references";
4
4
  declare const kStartBinding: unique symbol;
5
+ declare const kEndBinding: unique symbol;
5
6
  declare const kStaticDisplay: unique symbol;
6
7
  declare const kSingleNodeBody: unique symbol;
7
8
  declare const kDisplayRef: unique symbol;
8
9
  declare module "@marko/compiler/dist/types" {
9
10
  interface NodeExtra {
10
11
  [kStartBinding]?: Binding;
12
+ [kEndBinding]?: Binding;
11
13
  [kStaticDisplay]?: boolean;
12
14
  [kSingleNodeBody]?: boolean;
13
15
  [kDisplayRef]?: t.Expression;
@@ -1514,6 +1514,7 @@ function isNativeNode(tag) {
1514
1514
  //#endregion
1515
1515
  //#region src/translator/visitors/function.ts
1516
1516
  const [getReferencesByFn] = createProgramState(() => /* @__PURE__ */ new Map());
1517
+ const [getReferencesByImportedFn] = createProgramState(() => /* @__PURE__ */ new Map());
1517
1518
  var function_default = { analyze(fn) {
1518
1519
  if (fn !== getFnRoot(fn)) return;
1519
1520
  const exprRoot = getExprRoot(fn);
@@ -1524,8 +1525,9 @@ var function_default = { analyze(fn) {
1524
1525
  const fnExtra = node.extra ??= {};
1525
1526
  fnExtra.section = section;
1526
1527
  fnExtra.name = node.id?.name || (isMarkoAttribute(markoRoot) ? markoRoot.node.default ? _marko_compiler.types.toIdentifier((markoRoot.parentPath.has("var") ? markoRoot.parentPath.get("var") : markoRoot.parentPath.get("name")).toString()) : markoRoot.node.name : _marko_compiler.types.isVariableDeclarator(fn.parent) && _marko_compiler.types.isIdentifier(fn.parent.id) ? fn.parent.id.name : _marko_compiler.types.isObjectMethod(node) && _marko_compiler.types.isIdentifier(node.key) ? node.key.name : "anonymous");
1528
+ reserveExportRegisterId(fnExtra, fn, markoRoot);
1527
1529
  if (isStaticRoot(markoRoot)) {
1528
- const refs = getStaticDeclRefs(fnExtra, fn);
1530
+ const refs = getStaticDeclRefs(fn);
1529
1531
  if (refs === true) registerFunction(fnExtra, true);
1530
1532
  else if (refs.size) getReferencesByFn().set(fnExtra, refs);
1531
1533
  } else if (shouldAlwaysRegister(markoRoot)) registerFunction(fnExtra, true);
@@ -1533,15 +1535,113 @@ var function_default = { analyze(fn) {
1533
1535
  } };
1534
1536
  function finalizeFunctionRegistry() {
1535
1537
  for (const [fnExtra, exprExtras] of getReferencesByFn()) {
1536
- let reason;
1537
- for (const exprExtra of exprExtras) reason = mergeSerializeReasons(reason, getAllSerializeReasonsForExtra(getCanonicalExtra(exprExtra)));
1538
+ const reason = resolveSerializeReason(exprExtras);
1538
1539
  if (reason) registerFunction(fnExtra, reason);
1539
1540
  }
1541
+ for (const [importedFn, exprExtras] of getReferencesByImportedFn()) if (resolveSerializeReason(exprExtras)) registerImportedFn(importedFn);
1542
+ }
1543
+ /**
1544
+ * Tracks a function imported from another template that reserved a register id.
1545
+ * The importing template registers it (under the reserved id) when its own
1546
+ * references reach something serialized.
1547
+ */
1548
+ function trackImportedFn(importDecl, local, resolved) {
1549
+ const binding = importDecl.scope.getBinding(local);
1550
+ if (!binding) return;
1551
+ const importedFn = {
1552
+ ...resolved,
1553
+ node: importDecl.node,
1554
+ local
1555
+ };
1556
+ const refs = /* @__PURE__ */ new Set();
1557
+ if (addBindingRefs(binding, refs, /* @__PURE__ */ new Set()) === true) registerImportedFn(importedFn);
1558
+ else if (refs.size) getReferencesByImportedFn().set(importedFn, refs);
1559
+ }
1560
+ /**
1561
+ * Finds the id reserved for an export, following `export ... from` hops. Each
1562
+ * template only records the exports it declares, so this resolves on demand:
1563
+ * a template in an import cycle is read while it is still analyzing, and would
1564
+ * hand out an incomplete map if the ids were pushed ahead of time.
1565
+ */
1566
+ function resolveRegisteredExport(file, exportName, seen = /* @__PURE__ */ new Set()) {
1567
+ const filename = file.opts.filename;
1568
+ const key = `${filename}\0${exportName}`;
1569
+ if (seen.has(key)) return;
1570
+ seen.add(key);
1571
+ const reserved = file.ast.program.extra?.registeredExports?.get(exportName);
1572
+ if (reserved) return {
1573
+ ...reserved,
1574
+ filename
1575
+ };
1576
+ for (const child of file.ast.program.body) {
1577
+ if (child.type !== "ExportNamedDeclaration" && child.type !== "ExportAllDeclaration" || !child.source || (child.exportKind || "value") !== "value") continue;
1578
+ const sourceFile = (0, _marko_compiler_babel_utils.loadFileForImport)(file, child.source.value);
1579
+ if (!sourceFile) continue;
1580
+ if (child.type === "ExportAllDeclaration") {
1581
+ const resolved = resolveRegisteredExport(sourceFile, exportName, seen);
1582
+ if (resolved) return resolved;
1583
+ continue;
1584
+ }
1585
+ for (const specifier of child.specifiers) {
1586
+ if (specifier.type !== "ExportSpecifier" || (specifier.exportKind || "value") !== "value" || getExportedName(specifier) !== exportName) continue;
1587
+ const resolved = resolveRegisteredExport(sourceFile, specifier.local.name, seen);
1588
+ if (resolved) return resolved;
1589
+ }
1590
+ }
1591
+ }
1592
+ function resolveSerializeReason(exprExtras) {
1593
+ let reason;
1594
+ for (const exprExtra of exprExtras) reason = mergeSerializeReasons(reason, getAllSerializeReasonsForExtra(getCanonicalExtra(exprExtra)));
1595
+ return reason;
1596
+ }
1597
+ function registerImportedFn({ node, ...importedFn }) {
1598
+ const extra = node.extra ??= {};
1599
+ extra.registeredImportedFns ??= [];
1600
+ extra.registeredImportedFns.push(importedFn);
1601
+ (0, _marko_compiler_babel_utils.getProgram)().node.extra.isInteractive = true;
1602
+ }
1603
+ function reserveExportRegisterId(fnExtra, fn, markoRoot) {
1604
+ if (!isStaticRoot(markoRoot)) return;
1605
+ const exportNames = getExportNames(fn, markoRoot);
1606
+ const exportName = exportNames?.find((name) => _marko_compiler.types.isValidIdentifier(name));
1607
+ if (!exportName) return;
1608
+ const { markoOpts, opts: { filename } } = (0, _marko_compiler_babel_utils.getFile)();
1609
+ const programExtra = (0, _marko_compiler_babel_utils.getProgram)().node.extra;
1610
+ const registerId = (0, _marko_compiler_babel_utils.getTemplateId)(markoOpts, filename, `${fnExtra.section.id}/export/${exportName}`);
1611
+ fnExtra.exportRegisterId = registerId;
1612
+ programExtra.registeredExports ??= /* @__PURE__ */ new Map();
1613
+ for (const name of exportNames) programExtra.registeredExports.set(name, {
1614
+ registerId,
1615
+ exportName
1616
+ });
1617
+ }
1618
+ function getExportNames(fn, markoRoot) {
1619
+ const localName = getExportableName(fn);
1620
+ if (localName === void 0) return;
1621
+ const exportNames = markoRoot.isExportNamedDeclaration() && !markoRoot.node.source ? [localName] : [];
1622
+ const binding = fn.scope.getBinding(localName);
1623
+ if (binding) for (const ref of binding.referencePaths) {
1624
+ const specifier = ref.parent;
1625
+ if (specifier?.type === "ExportSpecifier" && (specifier.exportKind || "value") === "value" && !ref.parentPath.parent.source) {
1626
+ const exportName = getExportedName(specifier);
1627
+ if (exportName !== "default") exportNames.push(exportName);
1628
+ }
1629
+ }
1630
+ return exportNames.length ? exportNames : void 0;
1631
+ }
1632
+ function getExportableName(fn) {
1633
+ const { node, parent } = fn;
1634
+ if (node.type === "FunctionDeclaration") return node.id?.name;
1635
+ if (_marko_compiler.types.isVariableDeclarator(parent) && parent.init === node && _marko_compiler.types.isIdentifier(parent.id) && _marko_compiler.types.isVariableDeclaration(fn.parentPath.parent, { kind: "const" })) return parent.id.name;
1636
+ }
1637
+ function getExportedName(specifier) {
1638
+ const { exported } = specifier;
1639
+ return exported.type === "Identifier" ? exported.name : exported.value;
1540
1640
  }
1541
1641
  function canIgnoreRegister(markoRoot, exprRoot) {
1542
1642
  return markoRoot.isMarkoPlaceholder() || markoRoot.isMarkoScriptlet() && (!markoRoot.node.static || markoRoot.node.target === "server") || markoRoot.isMarkoTag() && markoRoot.node.name == exprRoot.node || isMarkoAttribute(markoRoot) && (analyzeTagNameType(markoRoot.parentPath) === 0 && /^on[A-Z-]/.test(markoRoot.node.name) && !hasSpreadAttributeAfter(markoRoot) || isCoreTagName(markoRoot.parentPath, "script") || isCoreTagName(markoRoot.parentPath, "lifecycle") || isCoreTagName(markoRoot.parentPath, "for"));
1543
1643
  }
1544
- function getStaticDeclRefs(fnExtra, path, refs = /* @__PURE__ */ new Set(), seen = /* @__PURE__ */ new Set()) {
1644
+ function getStaticDeclRefs(path, refs = /* @__PURE__ */ new Set(), seen = /* @__PURE__ */ new Set()) {
1545
1645
  const decl = getDeclarationRoot(path);
1546
1646
  if (decl && !seen.has(decl.node)) {
1547
1647
  seen.add(decl.node);
@@ -1549,20 +1649,23 @@ function getStaticDeclRefs(fnExtra, path, refs = /* @__PURE__ */ new Set(), seen
1549
1649
  if (ids) for (const name in ids) {
1550
1650
  const binding = decl.scope.getBinding(name);
1551
1651
  if (!binding) continue;
1552
- for (const ref of binding.referencePaths) {
1553
- if (isInvokedFunction(ref) || ref.parentPath.type === "Program") continue;
1554
- const exprRoot = getExprRoot(ref);
1555
- const markoRoot = getMarkoRoot(exprRoot);
1556
- if (!markoRoot || canIgnoreRegister(markoRoot, exprRoot)) continue;
1557
- if (isStaticRoot(markoRoot)) {
1558
- if (getStaticDeclRefs(fnExtra, ref, refs, seen) === true) return true;
1559
- } else if (shouldAlwaysRegister(markoRoot)) return true;
1560
- else refs.add(exprRoot.node.extra ??= {});
1561
- }
1652
+ if (addBindingRefs(binding, refs, seen) === true) return true;
1562
1653
  }
1563
1654
  }
1564
1655
  return refs;
1565
1656
  }
1657
+ function addBindingRefs(binding, refs, seen) {
1658
+ for (const ref of binding.referencePaths) {
1659
+ if (isInvokedFunction(ref) || ref.parentPath.type === "Program") continue;
1660
+ const exprRoot = getExprRoot(ref);
1661
+ const markoRoot = getMarkoRoot(exprRoot);
1662
+ if (!markoRoot || canIgnoreRegister(markoRoot, exprRoot)) continue;
1663
+ if (isStaticRoot(markoRoot)) {
1664
+ if (getStaticDeclRefs(ref, refs, seen) === true) return true;
1665
+ } else if (shouldAlwaysRegister(markoRoot)) return true;
1666
+ else refs.add(exprRoot.node.extra ??= {});
1667
+ }
1668
+ }
1566
1669
  function shouldAlwaysRegister(markoRoot) {
1567
1670
  const tag = getTagFromMarkoRoot(markoRoot);
1568
1671
  if (!tag) return false;
@@ -1594,7 +1697,7 @@ function registerFunction(fnExtra, reason) {
1594
1697
  program.node.extra.isInteractive = true;
1595
1698
  fnExtra.registerReason = reason;
1596
1699
  fnExtra.name = generateUid(fnExtra.name);
1597
- fnExtra.registerId = (0, _marko_compiler_babel_utils.getTemplateId)(markoOpts, filename, `${fnExtra.section.id}/${fnExtra.name.slice(1)}`);
1700
+ fnExtra.registerId = fnExtra.exportRegisterId ?? (0, _marko_compiler_babel_utils.getTemplateId)(markoOpts, filename, `${fnExtra.section.id}/${fnExtra.name.slice(1)}`);
1598
1701
  }
1599
1702
  function isMarkoAttribute(path) {
1600
1703
  return path ? path.isMarkoAttribute() : false;
@@ -2231,6 +2334,61 @@ function bindingHasProperty(binding, properties) {
2231
2334
  return false;
2232
2335
  }
2233
2336
  //#endregion
2337
+ //#region src/translator/util/module-registrations.ts
2338
+ /**
2339
+ * Writes the registrations for module scoped functions: the ones this template
2340
+ * exports and registers itself, and the ones it imports from a template that
2341
+ * reserved a register id without registering it.
2342
+ */
2343
+ function writeModuleRegistrations(program) {
2344
+ const { file } = program.hub;
2345
+ const statements = [];
2346
+ const seen = /* @__PURE__ */ new Set();
2347
+ for (const child of program.node.body) {
2348
+ const registeredImportedFns = child.extra?.registeredImportedFns;
2349
+ if (registeredImportedFns) for (const importedFn of registeredImportedFns) {
2350
+ if (seen.has(importedFn.registerId)) continue;
2351
+ seen.add(importedFn.registerId);
2352
+ if (isOutputHTML()) statements.push(buildRegistration(importedFn.local, importedFn.registerId));
2353
+ else (0, _marko_compiler_babel_utils.importDefault)(file, resolveRegisterModule(file, importedFn));
2354
+ }
2355
+ else if (child.type === "ExportNamedDeclaration") addExportRegistrations(child, seen, statements);
2356
+ }
2357
+ program.node.body.push(...statements);
2358
+ }
2359
+ function addExportRegistrations(node, seen, statements) {
2360
+ const { declaration } = node;
2361
+ if (!declaration) return;
2362
+ const add = (local, registerId) => {
2363
+ if (seen.has(registerId)) return;
2364
+ seen.add(registerId);
2365
+ statements.push(buildRegistration(local, registerId));
2366
+ };
2367
+ if (declaration.type === "FunctionDeclaration") {
2368
+ if (isRegisteredFnExtra(declaration.extra)) add(declaration.id.name, declaration.extra.registerId);
2369
+ return;
2370
+ }
2371
+ if (isOutputHTML() || declaration.type !== "VariableDeclaration") return;
2372
+ for (const declarator of declaration.declarations) {
2373
+ const extra = declarator.init?.extra;
2374
+ if (isRegisteredFnExtra(extra) && _marko_compiler.types.isIdentifier(declarator.id)) add(declarator.id.name, extra.registerId);
2375
+ }
2376
+ }
2377
+ function buildRegistration(local, registerId) {
2378
+ return _marko_compiler.types.expressionStatement(isOutputHTML() ? callRuntime("_resume", _marko_compiler.types.identifier(local), _marko_compiler.types.stringLiteral(registerId)) : callRuntime("_resume", _marko_compiler.types.stringLiteral(registerId), _marko_compiler.types.identifier(local)));
2379
+ }
2380
+ function resolveRegisterModule(file, { filename, exportName, registerId }) {
2381
+ const importer = file.opts.filename;
2382
+ return getMarkoOpts().resolveVirtualDependency(importer, {
2383
+ virtualPath: `${relativePath(importer, filename)}.register-${exportName}.js`,
2384
+ code: `import { ${exportName} } from "./${path.default.basename(filename)}";\nimport { _resume } from "${getRuntimePath("dom")}";\n_resume(${JSON.stringify(registerId)}, ${exportName});\n`
2385
+ });
2386
+ }
2387
+ function relativePath(from, to) {
2388
+ const relative = path.default.relative(path.default.dirname(from), to).split(path.default.sep).join("/");
2389
+ return relative.startsWith(".") ? relative : `./${relative}`;
2390
+ }
2391
+ //#endregion
2234
2392
  //#region src/translator/util/get-known-attr-values.ts
2235
2393
  function getKnownAttrValues(tag) {
2236
2394
  const attrs = {};
@@ -3677,6 +3835,7 @@ var dom_default = { translate: {
3677
3835
  if (!setup) program.node.body.unshift(_marko_compiler.types.exportNamedDeclaration(_marko_compiler.types.variableDeclaration("const", [_marko_compiler.types.variableDeclarator(setupIdentifier, _marko_compiler.types.arrowFunctionExpression([], _marko_compiler.types.blockStatement([])))])));
3678
3836
  program.node.body.unshift(_marko_compiler.types.exportNamedDeclaration(_marko_compiler.types.variableDeclaration("const", [_marko_compiler.types.variableDeclarator(templateIdentifier, writes || _marko_compiler.types.stringLiteral(""))])), _marko_compiler.types.exportNamedDeclaration(_marko_compiler.types.variableDeclaration("const", [_marko_compiler.types.variableDeclarator(walksIdentifier, walks || _marko_compiler.types.stringLiteral(""))])));
3679
3837
  if (extraDecls) program.node.body.unshift(_marko_compiler.types.variableDeclaration("const", extraDecls));
3838
+ writeModuleRegistrations(program);
3680
3839
  program.node.body.push(_marko_compiler.types.exportDefaultDeclaration(callRuntime("_template", _marko_compiler.types.stringLiteral(program.hub.file.metadata.marko.id), templateIdentifier, walksIdentifier, setupIdentifier, programInputSignal?.identifier)));
3681
3840
  }
3682
3841
  } };
@@ -3737,6 +3896,7 @@ var html_default = { translate: {
3737
3896
  child.remove();
3738
3897
  } else if (child.isMarkoScriptlet()) if (child.node.target && child.node.target !== "server") child.remove();
3739
3898
  else child.replaceWithMultiple(child.node.body);
3899
+ writeModuleRegistrations(program);
3740
3900
  const contentId = usedSharedUid("content") && getTemplateContentName();
3741
3901
  const contentFn = _marko_compiler.types.arrowFunctionExpression([_marko_compiler.types.identifier("input")], _marko_compiler.types.blockStatement(renderContent));
3742
3902
  const exportDefault = _marko_compiler.types.exportDefaultDeclaration(callRuntime("_template", _marko_compiler.types.stringLiteral(program.hub.file.metadata.marko.id), contentId ? _marko_compiler.types.identifier(contentId) : contentFn, program.node.extra.page || !getMarkoOpts().linkAssets ? _marko_compiler.types.numericLiteral(1) : void 0));
@@ -4510,7 +4670,8 @@ var native_tag_default = {
4510
4670
  throw tag.get("name").buildCodeFrameError(msg);
4511
4671
  });
4512
4672
  if (seen.content) {
4513
- if ((0, _marko_compiler_babel_utils.getTagDef)(tag)?.parseOptions?.openTagOnly) throw tag.hub.buildError(seen.content, `The \`<${tagName}>\` tag cannot have content, so it does not support the \`content\` attribute.`);
4673
+ const tagDef = (0, _marko_compiler_babel_utils.getTagDef)(tag);
4674
+ if (tagDef?.parseOptions?.openTagOnly && !tagDef.attributes?.content) throw tag.hub.buildError(seen.content, `The \`<${tagName}>\` tag cannot have content, so it does not support the \`content\` attribute.`);
4514
4675
  if (isTextOnly) throw tag.hub.buildError(seen.content, `The \`<${tagName}>\` tag takes its content from its body as text, so it does not support the \`content\` attribute.`);
4515
4676
  }
4516
4677
  let textPlaceholders;
@@ -8095,6 +8256,8 @@ var export_default = {
8095
8256
  const { node } = tag;
8096
8257
  const statements = (0, _marko_compiler_babel_utils.parseStatements)(tag.hub.file, node.rawValue, node.start, node.end);
8097
8258
  if (statements.length > 1) throw tag.hub.buildError(statements[1], "The `<export>` tag takes a single export statement.");
8259
+ const defaultExport = getDefaultExport(statements[0]);
8260
+ if (defaultExport) throw tag.hub.buildError(defaultExport, "A template is already its own default export. Use a named export instead.");
8098
8261
  tag.replaceWith(statements[0]);
8099
8262
  },
8100
8263
  parseOptions: {
@@ -8103,6 +8266,14 @@ var export_default = {
8103
8266
  },
8104
8267
  autocomplete: [{ displayText: "export <value>" }]
8105
8268
  };
8269
+ function getDefaultExport(statement) {
8270
+ if (statement.type === "ExportDefaultDeclaration") return statement;
8271
+ if (statement.type !== "ExportNamedDeclaration") return;
8272
+ for (const specifier of statement.specifiers) {
8273
+ const { exported } = specifier;
8274
+ if (exported && (exported.type === "Identifier" ? exported.name : exported.value) === "default") return specifier;
8275
+ }
8276
+ }
8106
8277
  //#endregion
8107
8278
  //#region src/translator/core/html-comment.ts
8108
8279
  const kNodeBinding$1 = Symbol("comment tag binding");
@@ -8539,9 +8710,25 @@ var server_default = {
8539
8710
  }]
8540
8711
  };
8541
8712
  //#endregion
8713
+ //#region src/translator/util/insertion-context.ts
8714
+ const discardsUnknownChildren = /* @__PURE__ */ new Set([
8715
+ "table",
8716
+ "thead",
8717
+ "tbody",
8718
+ "tfoot",
8719
+ "tr",
8720
+ "colgroup",
8721
+ "select",
8722
+ "optgroup"
8723
+ ]);
8724
+ function discardsWrapperChildren(tagName) {
8725
+ return discardsUnknownChildren.has(tagName);
8726
+ }
8727
+ //#endregion
8542
8728
  //#region src/translator/core/show.ts
8543
8729
  const kStatefulReason = Symbol("<show> stateful reason");
8544
8730
  const kStartBinding = Symbol("<show> range start binding");
8731
+ const kEndBinding = Symbol("<show> range end binding");
8545
8732
  const kStaticDisplay = Symbol("<show> static display");
8546
8733
  const kSingleNodeBody = Symbol("<show> single node body");
8547
8734
  const kDisplayRef = Symbol("<show> hoisted display reference");
@@ -8555,6 +8742,7 @@ var show_default = {
8555
8742
  tagExtra[kSingleNodeBody] = isSingleNodeBody(tag);
8556
8743
  if (displayEval.confident) tagExtra[kStaticDisplay] = !!displayEval.computed;
8557
8744
  if (tagExtra[kStaticDisplay] === true) return;
8745
+ assertLegalHiddenContext(tag);
8558
8746
  const tagSection = getOrCreateSection(tag);
8559
8747
  if (getOnlyChildParentTagName(tag)) getOptimizedOnlyChildNodeBinding(tag, tagSection);
8560
8748
  else tagExtra[kStartBinding] = createBinding("#text", 0, tagSection);
@@ -8567,6 +8755,7 @@ var show_default = {
8567
8755
  const tagExtra = tag.node.extra;
8568
8756
  if (tagExtra[kStaticDisplay] === true) return;
8569
8757
  const tagSection = getSection(tag);
8758
+ if (tagExtra[kStartBinding] && !tagExtra[kSingleNodeBody]) tagExtra[kEndBinding] = createBinding("#text", 0, tagSection);
8570
8759
  const nodeBinding = getOptimizedOnlyChildNodeBinding(tag, tagSection);
8571
8760
  if (tagExtra[kStaticDisplay] === void 0) addSerializeExpr(tagSection, tagExtra, nodeBinding);
8572
8761
  }
@@ -8636,16 +8825,21 @@ var show_default = {
8636
8825
  return;
8637
8826
  }
8638
8827
  const tagSection = getSection(tag);
8828
+ const endBinding = tagExtra[kEndBinding];
8639
8829
  const nodeBinding = getOptimizedOnlyChildNodeBinding(tag, tagSection);
8640
8830
  const startBinding = tagExtra[kStartBinding];
8641
8831
  const display = tagExtra[kStaticDisplay] === false ? _marko_compiler.types.booleanLiteral(false) : tag.node.attributes[0].value;
8832
+ if (endBinding) {
8833
+ visit(tag, 37);
8834
+ enterShallow(tag);
8835
+ }
8642
8836
  if (startBinding) {
8643
8837
  visit(tag, 37);
8644
8838
  enterShallow(tag);
8645
8839
  }
8646
8840
  const signal = getSignal(tagSection, nodeBinding, "show");
8647
8841
  signal.build = () => {
8648
- return callRuntime("_show", getScopeAccessorLiteral(nodeBinding, true), startBinding ? getScopeAccessorLiteral(startBinding, true) : void 0);
8842
+ return callRuntime("_show", getScopeAccessorLiteral(nodeBinding, true), startBinding ? getScopeAccessorLiteral(startBinding, true) : void 0, endBinding ? getScopeAccessorLiteral(endBinding, true) : void 0);
8649
8843
  };
8650
8844
  addValue(tagSection, tagExtra.referencedBindings, signal, display);
8651
8845
  tag.remove();
@@ -8676,6 +8870,10 @@ function assertValidShow(tag) {
8676
8870
  assertHasBody(tag);
8677
8871
  assertHasValueAttribute(tag);
8678
8872
  }
8873
+ function assertLegalHiddenContext(tag) {
8874
+ const parentName = getParentTag(tag)?.node.name;
8875
+ if (_marko_compiler.types.isStringLiteral(parentName) && discardsWrapperChildren(parentName.value)) throw tag.get("name").buildCodeFrameError(`A [\`<${getTagName(tag)}>\` tag](https://markojs.com/docs/reference/core-tag#show) cannot be a direct child of \`<${parentName.value}>\`: hidden content is wrapped in an element that \`<${parentName.value}>\` discards, which would render the content instead of hiding it. Move the \`<${getTagName(tag)}>\` inside the row or option, or use [\`<if>\`](https://markojs.com/docs/reference/core-tag#if).`);
8876
+ }
8679
8877
  function assertHasBody(tag) {
8680
8878
  if (!tag.node.body.body.length) throw tag.get("name").buildCodeFrameError(`The [\`<${getTagName(tag)}>\` tag](https://markojs.com/docs/reference/core-tag#show) requires [body content](https://markojs.com/docs/reference/language#tag-content).`);
8681
8879
  if (tag.node.body.attributeTags) throw tag.get("name").buildCodeFrameError(`The [\`<${getTagName(tag)}>\` tag](https://markojs.com/docs/reference/core-tag#show) does not support [attribute tags](https://markojs.com/docs/reference/language#attribute-tags).`);
@@ -9132,6 +9330,7 @@ var import_declaration_default = {
9132
9330
  (node.extra ??= {}).tagImport = tagImport;
9133
9331
  const tags = importDecl.hub.file.metadata.marko.tags;
9134
9332
  if (!tags.includes(tagImport)) tags.push(tagImport);
9333
+ trackImportedRegisteredFns(importDecl);
9135
9334
  }
9136
9335
  const loadAttrPath = node.attributes?.length ? importDecl.get("attributes").find((p) => (p.node.key.type === "Identifier" ? p.node.key.name : p.node.key.value) === "load") : void 0;
9137
9336
  if (loadAttrPath) {
@@ -9187,6 +9386,19 @@ function getOrCreateHtmlLoadWrapped(readyId, originalIdentifier, filename, trigg
9187
9386
  (0, _marko_compiler_babel_utils.getProgram)().node.body.push(_marko_compiler.types.markoScriptlet([_marko_compiler.types.variableDeclaration("const", [_marko_compiler.types.variableDeclarator(_marko_compiler.types.identifier(wrappedName), callRuntime("withLoadAssets", originalIdentifier, _marko_compiler.types.stringLiteral(readyId), triggers ? _marko_compiler.types.valueToNode(triggers) : void 0))])], true));
9188
9387
  return wrappedName;
9189
9388
  }
9389
+ function trackImportedRegisteredFns(importDecl) {
9390
+ const { node } = importDecl;
9391
+ const childFile = (0, _marko_compiler_babel_utils.loadFileForImport)(importDecl.hub.file, node.source.value);
9392
+ if (!childFile) return;
9393
+ for (const specifier of importDecl.get("specifiers")) {
9394
+ if (!specifier.isImportSpecifier()) continue;
9395
+ const { imported } = specifier.node;
9396
+ const importedName = imported.type === "Identifier" ? imported.name : imported.value;
9397
+ if (importedName === "default") continue;
9398
+ const resolved = resolveRegisteredExport(childFile, importedName);
9399
+ if (resolved) trackImportedFn(importDecl, specifier.node.local.name, resolved);
9400
+ }
9401
+ }
9190
9402
  function getLoadImportConfig(attrValue) {
9191
9403
  const raw = attrValue.node.value;
9192
9404
  if (raw === "render") return { render: true };
@@ -10005,7 +10217,7 @@ var text_default = {
10005
10217
  function buildAggregateError(file, rootMsg, ...paths) {
10006
10218
  const err = /* @__PURE__ */ new SyntaxError();
10007
10219
  const fileName = path.default.relative(_marko_compiler_modules.cwd, file.opts.filename);
10008
- const finalMsg = `${rootMsg}:\n\n${paths.map(([msg, path$12]) => `\x1b[90m${msg} at ${getFileNameWithLoc(fileName, path$12)}:\x1b[0m\n${getFrame(file, path$12)}`).join("\n\n")}`;
10220
+ const finalMsg = `${rootMsg}:\n\n${paths.map(([msg, path$13]) => `\x1b[90m${msg} at ${getFileNameWithLoc(fileName, path$13)}:\x1b[0m\n${getFrame(file, path$13)}`).join("\n\n")}`;
10009
10221
  if (!("MARKO_DEBUG" in process.env)) err.stack = finalMsg;
10010
10222
  Object.defineProperty(err, "message", {
10011
10223
  get() {
@@ -10235,13 +10447,13 @@ function mergeVisitors(visitor5, visitor6) {
10235
10447
  function mergeVisit(visit5, visit6) {
10236
10448
  const enter5 = getVisitorEnter(visit5);
10237
10449
  const enter6 = getVisitorEnter(visit6);
10238
- const enter = (enter5 || enter6) && function enter(path$8, state) {
10239
- return (isTagsAPI() ? enter6 : enter5)?.call(this, path$8, state);
10450
+ const enter = (enter5 || enter6) && function enter(path$9, state) {
10451
+ return (isTagsAPI() ? enter6 : enter5)?.call(this, path$9, state);
10240
10452
  };
10241
10453
  const exit5 = getVisitorExit(visit5);
10242
10454
  const exit6 = getVisitorExit(visit6);
10243
- const exit = (exit5 || exit6) && function exit(path$9, state) {
10244
- return (isTagsAPI() ? exit6 : exit5)?.call(this, path$9, state);
10455
+ const exit = (exit5 || exit6) && function exit(path$10, state) {
10456
+ return (isTagsAPI() ? exit6 : exit5)?.call(this, path$10, state);
10245
10457
  };
10246
10458
  return exit ? enter ? {
10247
10459
  enter,
@@ -10324,17 +10536,17 @@ function sequenceVisit(first, second) {
10324
10536
  if (!first || !second) return first || second;
10325
10537
  const enterFirst = getVisitorEnter(first);
10326
10538
  const enterSecond = getVisitorEnter(second);
10327
- const enter = (enterFirst || enterSecond) && function enter(path$10, state) {
10328
- const { node } = path$10;
10329
- enterFirst?.call(this, path$10, state);
10330
- if (path$10.node === node) enterSecond?.call(this, path$10, state);
10539
+ const enter = (enterFirst || enterSecond) && function enter(path$11, state) {
10540
+ const { node } = path$11;
10541
+ enterFirst?.call(this, path$11, state);
10542
+ if (path$11.node === node) enterSecond?.call(this, path$11, state);
10331
10543
  };
10332
10544
  const exitFirst = getVisitorExit(first);
10333
10545
  const exitSecond = getVisitorExit(second);
10334
- const exit = (exitFirst || exitSecond) && function exit(path$11, state) {
10335
- const { node } = path$11;
10336
- exitFirst?.call(this, path$11, state);
10337
- if (path$11.node === node) exitSecond?.call(this, path$11, state);
10546
+ const exit = (exitFirst || exitSecond) && function exit(path$12, state) {
10547
+ const { node } = path$12;
10548
+ exitFirst?.call(this, path$12, state);
10549
+ if (path$12.node === node) exitSecond?.call(this, path$12, state);
10338
10550
  };
10339
10551
  return exit ? enter ? {
10340
10552
  enter,
@@ -0,0 +1 @@
1
+ export declare function discardsWrapperChildren(tagName: string): boolean;
@@ -0,0 +1,7 @@
1
+ import { types as t } from "@marko/compiler";
2
+ /**
3
+ * Writes the registrations for module scoped functions: the ones this template
4
+ * exports and registers itself, and the ones it imports from a template that
5
+ * reserved a register id without registering it.
6
+ */
7
+ export declare function writeModuleRegistrations(program: t.NodePath<t.Program>): void;
@@ -103,6 +103,7 @@ declare module "@marko/compiler/dist/types" {
103
103
  name?: string;
104
104
  registerId?: string;
105
105
  registerReason?: SerializeReason;
106
+ exportRegisterId?: string;
106
107
  }
107
108
  interface ArrowFunctionExpressionExtra extends FunctionExtra {
108
109
  }
@@ -1,6 +1,33 @@
1
1
  import { types as t } from "@marko/compiler";
2
+ declare module "@marko/compiler/dist/types" {
3
+ interface ProgramExtra {
4
+ registeredExports?: Map<string, ReservedExport>;
5
+ }
6
+ }
7
+ /** The canonical name a reserved register id is exported under, and its id. */
8
+ export interface ReservedExport {
9
+ registerId: string;
10
+ exportName: string;
11
+ }
12
+ /** A reserved export, plus the template that declares it. */
13
+ export interface ResolvedExport extends ReservedExport {
14
+ filename: string;
15
+ }
2
16
  declare const _default: {
3
17
  analyze(this: unknown, fn: t.NodePath<t.Function>): void;
4
18
  };
5
19
  export default _default;
6
20
  export declare function finalizeFunctionRegistry(): void;
21
+ /**
22
+ * Tracks a function imported from another template that reserved a register id.
23
+ * The importing template registers it (under the reserved id) when its own
24
+ * references reach something serialized.
25
+ */
26
+ export declare function trackImportedFn(importDecl: t.NodePath<t.ImportDeclaration>, local: string, resolved: ResolvedExport): void;
27
+ /**
28
+ * Finds the id reserved for an export, following `export ... from` hops. Each
29
+ * template only records the exports it declares, so this resolves on demand:
30
+ * a template in an import cycle is read while it is still analyzing, and would
31
+ * hand out an incomplete map if the ids were pushed ahead of time.
32
+ */
33
+ export declare function resolveRegisteredExport(file: t.BabelFile, exportName: string, seen?: Set<string>): ResolvedExport | undefined;
@@ -1,9 +1,13 @@
1
1
  import { types as t } from "@marko/compiler";
2
2
  import type { LoadTrigger } from "../../html/assets";
3
+ import { type ResolvedExport } from "./function";
3
4
  declare module "@marko/compiler/dist/types" {
4
5
  interface NodeExtra {
5
6
  tagImport?: string;
6
7
  loadImport?: LoadImportConfig;
8
+ registeredImportedFns?: (ResolvedExport & {
9
+ local: string;
10
+ })[];
7
11
  }
8
12
  }
9
13
  export type LoadImportConfig = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marko",
3
- "version": "6.3.27",
3
+ "version": "6.3.29",
4
4
  "description": "Optimized runtime for Marko templates.",
5
5
  "keywords": [
6
6
  "api",
@@ -48,13 +48,13 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@marko/compiler": "^5.41.11",
51
+ "@marko/compiler": "^5.41.12",
52
52
  "csstype": "^3.2.3",
53
53
  "fastest-levenshtein": "^1.0.16",
54
54
  "magic-string": "^0.30.21"
55
55
  },
56
56
  "devDependencies": {
57
- "@marko/runtime-tags": "npm:marko@6.3.27",
57
+ "@marko/runtime-tags": "npm:marko@6.3.29",
58
58
  "marko": "5.39.30"
59
59
  },
60
60
  "engines": {