marko 6.3.44 → 6.3.45

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/cheatsheet.md CHANGED
@@ -7,7 +7,7 @@ Marko 6 = HTML superset, not JSX and not Marko 4/5 syntax. `.marko` files are co
7
7
  1. Text interpolation: `${expr}` inside tag bodies. A bare line at the template root parses as a tag (concise mode): `Welcome aboard` fails to compile, but `p is a tag` compiles **silently** to `<p is a tag></p>`, since any line starting with a real tag name loses its words to attributes. Wrap text in an element (`<p>Welcome aboard</p>`) or prefix the line with `--` and a space (`-- Welcome ${name}`). Attributes take raw JS after `=` with no braces or quotes: `<div title=user.name data-n=1 + 1>`.
8
8
  2. A top-level `>` hugging its operand in an attribute value **ends the tag** silently: `<button disabled=count>=8 onClick() {…}>More</button>` is `disabled=count` plus the text `=8 onClick() {…}>`, so the handler never binds. Space a `>=` (`disabled=count >= 8`), and parenthesize a bare `>` comparison (`hidden=(a > b)`) and a TS type argument `<let/s=(new Set<string>())>`. Do not move the type onto the tag variable instead: `<let/s:Set<string>=new Set()>` compiles but fails type-check with TS2322 (the annotation does not flow into the initializer). A `>` nested inside `(…)`/`{…}`/`[…]` is safe (`class={ big: n > 1 }`). `<` never closes a tag (`disabled=count<=1` is fine).
9
9
  3. State: `<let/name=initial>` (slash then var name!). Update by plain assignment in an event handler: `count++`, `text = "hi"`. No setState, no hooks.
10
- 4. Derived values: `<const/total=items.length * price>` auto-recomputes. Never use an effect to derive state. A tag variable is whatever the tag returns, not the attribute you passed. `<let/draft=input.text>` re-runs on every `input.text` change but returns state it controls, so `draft` keeps your edits. Pick by intent: recomputes → `<const>`, seeds then diverges → `<let>`. A `<let>` you never assign is just a frozen `<const>`. Updates batch: mid-handler a reassigned `<let>` reads current but its derived `<const>` reads stale, so recompute from the `<let>`.
10
+ 4. Derived values: `<const/total=items.length * price>` auto-recomputes. Never use an effect to derive state. A tag variable is whatever the tag returns, not the attribute you passed. `<let/draft=input.text>` re-runs on every `input.text` change but returns state it controls, so `draft` keeps your edits; only a controllable `<let>` (`valueChange=`, or `<let/draft:=input.text>`) takes the new value. Pick by intent: recomputes → `<const>`, seeds then diverges → `<let>`. A `<let>` you never assign is just a frozen `<const>`. Updates batch: mid-handler a reassigned `<let>` reads current but its derived `<const>` reads stale, so recompute from the `<let>`.
11
11
  5. Never mutate state in place: `items.push(x)` does not update the UI. Always reassign:
12
12
  - add: `items = items.concat(x)`
13
13
  - remove: `items = items.toSpliced(i, 1)`
@@ -15,9 +15,9 @@ Marko 6 = HTML superset, not JSX and not Marko 4/5 syntax. `.marko` files are co
15
15
  - object: `user = { ...user, name }`
16
16
  6. Events: method shorthand `onClick() { ... }` or `onClick=fn`. Handlers receive `(event, element)`; delegation means the element is the second parameter, not `event.currentTarget`: `onSubmit(e) { e.preventDefault(); save() }`, `onClick(e, el) { el.focus() }`. Don't sync input values through `onInput`/`onChange`; that's what the change handlers below are for. Prefix with `async` to `await` in the body: `async onClick() { await save() }`.
17
17
  7. Native inputs are uncontrolled by default: `value=` sets the default value — later writes update what `form.reset()` restores, never a dirty field's display. Adding the matching `*Change` handler is what makes them controlled: `valueChange` on `<input>`/`<textarea>`/`<select>`, `checkedChange` on checkboxes/radios, `openChange` on `<details>`/`<dialog>`. `value:=text` is the shorthand for `value=text valueChange(v) { text = v }`. (`<textarea value:=text/>`: value attribute, not body.) `:=` differs by operand: on an identifier (`value:=text`) it assigns that variable; on a member expression (`<let/count:=input.count>` in a child) it wires `input.countChange`, so the child is controlled when the parent passes that handler and keeps its own state when it doesn't.
18
- 8. Transform in the handler when needed; number inputs give strings: `<input type="number" value=n valueChange(v) { n = +v }>`, or `value:parseFloat:=n`.
18
+ 8. Transform in the handler when needed; number inputs give strings: `<input type="number" value=n valueChange(v) { n = +v }>`, or `value:parseFloat:=n`. Uncommitted edits (debounce, commit on blur) never sync through a `<script>`; that is rule 4's derive-by-effect trap. Pair a controllable `<let/value:=input.value>` with `<let/pending=null>`, show `<const/draft=pending ?? value>`, collect with `valueChange(v) { pending = v }`, and commit by assigning `value = pending; pending = null`. Prefer that to calling `input.valueChange(...)`, which throws unless every caller controls the tag.
19
19
  9. Radio/checkbox groups: `checkedValue:=picked` on each input (shared var, distinct `value=`); the match is checked; array var for multi-checkbox. Dropdown: `<select value:=picked>`.
20
- 10. Module-level values, helpers and type aliases need `static`: `static const LIMIT = 10`, `static function fmt(n) {…}`, `static type Row = {…}`. Without it `function fmt(n) {` parses as a tag: ``Unable to find entry point for custom tag `<function>` ``, an error that never says `static`. Prefer it to `<const>` for anything that never changes: `<const/LIMIT=10>` emits a per-instance signal plus a `$setup` call. `server`/`client` narrow `static` to one platform (`client import { Chart } from "chart"`); the binding is `undefined` on the other, so read a `client` one only from `<script>`/handlers/`<lifecycle>` — reading it while rendering throws `is not a function` during SSR.
20
+ 10. Module-level values, helpers and type aliases need `static`: `static const LIMIT = 10`, `static function fmt(n) {…}`, `static type Row = {…}`. Without it `function fmt(n) {` parses as a tag: ``Unable to find entry point for custom tag `<function>` ``, an error that points at `static`. Prefer it to `<const>` for anything that never changes: `<const/LIMIT=10>` emits a per-instance signal plus a `$setup` call. `server`/`client` narrow `static` to one platform (`client import { Chart } from "chart"`); the binding is `undefined` on the other, so read a `client` one only from `<script>`/handlers/`<lifecycle>` — reading it while rendering throws `is not a function` during SSR.
21
21
 
22
22
  ## Canonical component
23
23
 
@@ -129,6 +129,7 @@ Don't fetch while rendering: start data loads early, pass the promise through th
129
129
  - Conditional attrs: `false`/`null` attrs are omitted from HTML. `aria-selected` etc. want strings: `aria-selected=(i === active && "true")`.
130
130
  - `class=` / `style=` accept strings, objects, arrays: `class=["btn", { active }]`, `style={ color }` (single braces). `style=` keys are kebab-case CSS names (`{ "background-color": c }`), not camelCase.
131
131
  - `<id/x>` mints a collision-free id for label/input wiring (`<label for=x>`/`<input id=x>`); don't hardcode ids in reusable tags; `<id/x=input.id>` reuses a caller's.
132
+ - Head tags render where written: a `<title>`/`<meta>`/`<link>` inside a nested component stays in the body, giving a second title or an inert canonical. `<head>` is already written by the time descendants render, so page meta has to be known before it: under @marko/run declare it in the route's `+meta.*` file and read `$global.meta` from the layout that owns `<head>`, otherwise pass it down or set it on `$global` at the render call.
132
133
 
133
134
  ## Sharing data (`$global`)
134
135
 
@@ -201,6 +202,7 @@ Each left-hand habit is an error or silently wrong.
201
202
  | `const [x, setX] = useState()` / `state` / `class {}` block | `<let/x=0>` then `x = 1` |
202
203
  | `$ const y = x * 2;` (scriptlets are removed) | `<const/y=x * 2>` |
203
204
  | `<let/n=a + b>` for a value that should recompute | `<const/n=a + b>`; `<let>` seeds an initial value, then de-syncs by design |
205
+ | `<let/x=input.x>` expecting it to track `input.x` | `<const/x=input.x>`, or `<let/x:=input.x>` to make it controllable |
204
206
  | `function fmt(n) {…}` / `const LIMIT = 10` at module level | `static function fmt(n) {…}` / `static const LIMIT = 10` |
205
207
  | `type Row = {…}` at module level | `static type Row = {…}` |
206
208
  | `<let x=0>` | `<let/x=0>` |
@@ -213,6 +215,7 @@ Each left-hand habit is an error or silently wrong.
213
215
  | bare text on its own line at template root | wrap in an element (`<p>...`), or prefix the line with `--` and a space |
214
216
  | `by=item` using the loop variable | `by="propName"` or `by=(item) => key`; `by=` is evaluated outside the loop |
215
217
  | `onInput(e) { q = e.target.value }` to sync an input | `value:=q`; the change handler owns the value |
218
+ | `<script>` syncing a draft field back from `value` | `<const/draft=pending ?? value>`; a `<let>` holds only the uncommitted edit |
216
219
  | fetching inside the component that renders the data | start the promise early (route handler / top of template), pass it down to `<await>` |
217
220
  | `style={ backgroundColor: c }` (camelCase keys) | `style={ "background-color": c }` (kebab-case) |
218
221
  | `this.querySelector` / `this.getRootNode()` in `<script>` | element ref getter: `<div/el>` then `el()` (there is no `this`) |
@@ -221,6 +224,7 @@ Each left-hand habit is an error or silently wrong.
221
224
  | hand-rolled `IntersectionObserver` to defer a widget's JS | `import W from "<w>" with { load: "visible#sel" }` |
222
225
  | imperative lib wired through `<script>` mount + cleanup | `<lifecycle onMount/onUpdate/onDestroy>` (keeps `this` across all three) |
223
226
  | `createContext`/provider to share data | `input` (prop drilling) or request-scoped `$global` |
227
+ | `<title>`/`<meta>` in a nested component | put page meta in the layout that owns `<head>`; head tags never hoist |
224
228
  | `$global.x` in client-reactive code, not allow-listed | `$global.serializedGlobals = { x: true }` first; otherwise the read is `undefined` |
225
229
  | hand-namespaced global classes (`.my-card-title`) | `<style/styles>` + `class=styles.card` (scoped CSS modules) |
226
230
  | `tsc --noEmit` to type check templates | `mtc`; `tsc` skips `.marko` files and exits 0 |
@@ -1011,7 +1011,8 @@ const replaceUnsafeRegExpSourceChar = (match) => {
1011
1011
  };
1012
1012
  function writeRegExp(state, val) {
1013
1013
  const { source } = val;
1014
- state.buf.push("/" + (unsafeRegExpSourceDetect.test(source) ? source.replace(unsafeRegExpSourceReg, replaceUnsafeRegExpSourceChar) : source) + "/" + val.flags);
1014
+ if (source.includes("<")) state.buf.push(`RegExp(${quote(source, 0)}${val.flags ? ",\"" + val.flags + "\"" : ""})`);
1015
+ else state.buf.push("/" + (unsafeRegExpSourceDetect.test(source) ? source.replace(unsafeRegExpSourceReg, replaceUnsafeRegExpSourceChar) : source) + "/" + val.flags);
1015
1016
  return true;
1016
1017
  }
1017
1018
  function writePromise(state, val, ref) {
@@ -1009,7 +1009,8 @@ const replaceUnsafeRegExpSourceChar = (match) => {
1009
1009
  };
1010
1010
  function writeRegExp(state, val) {
1011
1011
  const { source } = val;
1012
- state.buf.push("/" + (unsafeRegExpSourceDetect.test(source) ? source.replace(unsafeRegExpSourceReg, replaceUnsafeRegExpSourceChar) : source) + "/" + val.flags);
1012
+ if (source.includes("<")) state.buf.push(`RegExp(${quote(source, 0)}${val.flags ? ",\"" + val.flags + "\"" : ""})`);
1013
+ else state.buf.push("/" + (unsafeRegExpSourceDetect.test(source) ? source.replace(unsafeRegExpSourceReg, replaceUnsafeRegExpSourceChar) : source) + "/" + val.flags);
1013
1014
  return true;
1014
1015
  }
1015
1016
  function writePromise(state, val, ref) {
package/dist/html.js CHANGED
@@ -780,7 +780,7 @@ function writeDate(state, val) {
780
780
  }
781
781
  function writeRegExp(state, val) {
782
782
  let { source } = val;
783
- return state.buf.push("/" + (unsafeRegExpSourceDetect.test(source) ? source.replace(unsafeRegExpSourceReg, replaceUnsafeRegExpSourceChar) : source) + "/" + val.flags), !0;
783
+ return source.includes("<") ? state.buf.push(`RegExp(${quote(source, 0)}${val.flags ? ",\"" + val.flags + "\"" : ""})`) : state.buf.push("/" + (unsafeRegExpSourceDetect.test(source) ? source.replace(unsafeRegExpSourceReg, replaceUnsafeRegExpSourceChar) : source) + "/" + val.flags), !0;
784
784
  }
785
785
  function writePromise(state, val, ref) {
786
786
  let { boundary, channel } = state;
package/dist/html.mjs CHANGED
@@ -779,7 +779,7 @@ function writeDate(state, val) {
779
779
  }
780
780
  function writeRegExp(state, val) {
781
781
  let { source } = val;
782
- return state.buf.push("/" + (unsafeRegExpSourceDetect.test(source) ? source.replace(unsafeRegExpSourceReg, replaceUnsafeRegExpSourceChar) : source) + "/" + val.flags), !0;
782
+ return source.includes("<") ? state.buf.push(`RegExp(${quote(source, 0)}${val.flags ? ",\"" + val.flags + "\"" : ""})`) : state.buf.push("/" + (unsafeRegExpSourceDetect.test(source) ? source.replace(unsafeRegExpSourceReg, replaceUnsafeRegExpSourceChar) : source) + "/" + val.flags), !0;
783
783
  }
784
784
  function writePromise(state, val, ref) {
785
785
  let { boundary, channel } = state;
@@ -35,8 +35,6 @@ let package_json = require("../../package.json");
35
35
  package_json = __toESM(package_json, 1);
36
36
  let _marko_compiler = require("@marko/compiler");
37
37
  let _marko_compiler_babel_utils = require("@marko/compiler/babel-utils");
38
- let node_path = require("node:path");
39
- node_path = __toESM(node_path, 1);
40
38
  let path = require("path");
41
39
  path = __toESM(path, 1);
42
40
  let magic_string = require("magic-string");
@@ -1201,6 +1199,37 @@ function getSerializeSourcesForRef(ref) {
1201
1199
  } else return ref.sources;
1202
1200
  }
1203
1201
  }
1202
+ function mapCrossProgramReason(program, reason, exprs) {
1203
+ let params;
1204
+ let mapped;
1205
+ let crossProgram = false;
1206
+ forEach(reason.param, (param) => {
1207
+ if (param.section.program === program) params = bindingUtil.add(params, param);
1208
+ else {
1209
+ crossProgram = true;
1210
+ mapped = exprs ? mergeSerializeReasons(mapped, getSerializeSourcesForExprs(mapParamBindingToExpr(exprs, param))) : true;
1211
+ }
1212
+ });
1213
+ if (!crossProgram) return reason;
1214
+ return mergeRemappedSources(reason, params, mapped);
1215
+ }
1216
+ function mapDownstreamReason(program, reason, exprs) {
1217
+ let params;
1218
+ let mapped;
1219
+ let downstream = false;
1220
+ forEach(reason.param, (param) => {
1221
+ if (param.section.program === program) {
1222
+ downstream = true;
1223
+ mapped = mergeSerializeReasons(mapped, getSerializeSourcesForExprs(mapParamBindingToExpr(exprs, param)));
1224
+ } else params = bindingUtil.add(params, param);
1225
+ });
1226
+ if (!downstream) return reason;
1227
+ return mergeRemappedSources(reason, params, mapped);
1228
+ }
1229
+ function mergeRemappedSources(reason, params, mapped) {
1230
+ if (mapped !== true && (reason.state || reason.global || params)) mapped = mergeSerializeReasons(mapped, createSources(reason.state, params, reason.global));
1231
+ return mapped;
1232
+ }
1204
1233
  function mergeSerializeReasons(a, b) {
1205
1234
  if (a === true || b === true) return true;
1206
1235
  return mergeSources(a, b);
@@ -1347,6 +1376,7 @@ function analyzeTagNameType(tag, allowDynamic) {
1347
1376
  extra.tagNameType = 2;
1348
1377
  extra.tagNameDynamic = true;
1349
1378
  extra.featureType = "class";
1379
+ if (childFile?.ast.program.extra?.hydratesTags) (0, _marko_compiler_babel_utils.getProgram)().node.extra.isInteractive = true;
1350
1380
  } else if (!childFile) {
1351
1381
  extra.tagNameType = 2;
1352
1382
  extra.tagNameDynamic = true;
@@ -1447,6 +1477,7 @@ function startSection(path) {
1447
1477
  loc: parentTag?.node.name.loc || void 0,
1448
1478
  depth: parentSection ? parentSection.depth + 1 : 0,
1449
1479
  parent: parentSection,
1480
+ program: void 0,
1450
1481
  sectionAccessor: void 0,
1451
1482
  params: void 0,
1452
1483
  referencedLocalClosures: void 0,
@@ -1470,6 +1501,7 @@ function startSection(path) {
1470
1501
  isBranch: false,
1471
1502
  structure: parentSection && !parentSection.structure ? null : []
1472
1503
  };
1504
+ section.program = parentSection ? parentSection.program : section;
1473
1505
  sections.push(section);
1474
1506
  }
1475
1507
  return section;
@@ -1573,7 +1605,8 @@ function getSectionRegisterReasons(section) {
1573
1605
  if (section.isBranch) return false;
1574
1606
  const { downstreamBinding } = section;
1575
1607
  if (downstreamBinding) {
1576
- const downstreamReasons = getAllSerializeReasonsForBinding(downstreamBinding.binding, downstreamBinding.properties);
1608
+ let downstreamReasons = getAllSerializeReasonsForBinding(downstreamBinding.binding, downstreamBinding.properties);
1609
+ if (downstreamReasons && downstreamReasons !== true) downstreamReasons = mapCrossProgramReason(section.program, downstreamReasons, downstreamBinding.exprs);
1577
1610
  if (!downstreamReasons) return false;
1578
1611
  if (isReasonDynamic(downstreamReasons) && !section.serializeReason && !section.serializeReasons.size && !section.parent?.serializeReason && !section.parent?.serializeReasons.size) return false;
1579
1612
  return downstreamReasons;
@@ -1913,6 +1946,26 @@ function isClientAssetImport(file, request) {
1913
1946
  return typeof hydrateIncludeImports === "function" ? hydrateIncludeImports(request) : !!hydrateIncludeImports?.test(request);
1914
1947
  }
1915
1948
  //#endregion
1949
+ //#region src/translator/util/binding-has-prop.ts
1950
+ function isSectionRendererElided(section) {
1951
+ return !!section.downstreamBinding && !bindingHasProperty(section.downstreamBinding.binding, section.downstreamBinding.properties);
1952
+ }
1953
+ function bindingHasProperty(binding, properties) {
1954
+ if (binding.pruned) return false;
1955
+ else if (binding.pruned === void 0) throw new Error("Binding must be pruned before checking properties");
1956
+ if (binding.reads.size || !properties) return true;
1957
+ let property;
1958
+ let rest;
1959
+ if (Array.isArray(properties)) {
1960
+ property = properties[0];
1961
+ rest = properties.length === 2 ? properties[1] : properties.slice(1);
1962
+ } else property = properties;
1963
+ const propBinding = binding.propertyAliases.get(property);
1964
+ if (propBinding && bindingHasProperty(propBinding, rest)) return true;
1965
+ for (const alias of binding.aliases) if (bindingHasProperty(alias, properties)) return true;
1966
+ return false;
1967
+ }
1968
+ //#endregion
1916
1969
  //#region src/translator/util/binding-prop-tree.ts
1917
1970
  const kDirectContent = Symbol("direct content");
1918
1971
  function getBindingPropTree(binding) {
@@ -1973,35 +2026,70 @@ function resolveRelativeToEntry(entryFile, file, req) {
1973
2026
  //#endregion
1974
2027
  //#region src/translator/util/entry-builder.ts
1975
2028
  const kState = Symbol();
1976
- var entry_builder_default = {
2029
+ const builder = {
1977
2030
  build(entryFile, exportInit) {
1978
2031
  const state = entryFile[kState];
1979
2032
  if (!state) throw entryFile.path.buildCodeFrameError("Unable to build hydrate code, no files were visited before finalizing the build");
1980
2033
  const body = [];
1981
- if (state.init) {
2034
+ for (const asset of state.assets) body.push(_marko_compiler.types.importDeclaration([], _marko_compiler.types.stringLiteral(asset)));
2035
+ if (state.init || state.load) {
1982
2036
  const isPage = entryFile.path.node.extra.page;
1983
2037
  const initHelper = isPage ? "init" : "initEmbedded";
1984
- body.push(_marko_compiler.types.importDeclaration([_marko_compiler.types.importSpecifier(_marko_compiler.types.identifier(initHelper), _marko_compiler.types.identifier(initHelper))], _marko_compiler.types.stringLiteral(`${runtime_info_default.name}/${entryFile.markoOpts.optimize ? "" : "debug/"}dom`)), _marko_compiler.types.importDeclaration([], _marko_compiler.types.stringLiteral(`./${node_path.default.basename(entryFile.opts.filename)}`)));
2038
+ if (state.init) body.push(_marko_compiler.types.importDeclaration([_marko_compiler.types.importSpecifier(_marko_compiler.types.identifier(initHelper), _marko_compiler.types.identifier(initHelper))], _marko_compiler.types.stringLiteral(`${runtime_info_default.name}/${entryFile.markoOpts.optimize ? "" : "debug/"}dom`)));
2039
+ for (const root of state.roots) body.push(_marko_compiler.types.importDeclaration([], _marko_compiler.types.stringLiteral(root)));
2040
+ if (!state.init) {
2041
+ if (exportInit) body.push(_marko_compiler.types.exportDefaultDeclaration(_marko_compiler.types.arrowFunctionExpression([], _marko_compiler.types.blockStatement([]))));
2042
+ return body;
2043
+ }
1985
2044
  const { runtimeId } = entryFile.markoOpts;
1986
2045
  const readyId = !isPage && (0, _marko_compiler_babel_utils.getTemplateId)(entryFile.markoOpts, entryFile.opts.filename);
1987
2046
  const initExpression = _marko_compiler.types.callExpression(_marko_compiler.types.identifier(initHelper), readyId ? runtimeId ? [_marko_compiler.types.stringLiteral(readyId), _marko_compiler.types.stringLiteral(runtimeId)] : [_marko_compiler.types.stringLiteral(readyId)] : runtimeId ? [_marko_compiler.types.stringLiteral(runtimeId)] : []);
1988
2047
  body.push(exportInit ? _marko_compiler.types.exportDefaultDeclaration(_marko_compiler.types.arrowFunctionExpression([], initExpression)) : _marko_compiler.types.expressionStatement(initExpression));
1989
2048
  } else {
1990
- for (const asset of state.assets) body.push(_marko_compiler.types.importDeclaration([], _marko_compiler.types.stringLiteral(asset)));
2049
+ for (const asset of state.bundledAssets) body.push(_marko_compiler.types.importDeclaration([], _marko_compiler.types.stringLiteral(asset)));
1991
2050
  if (exportInit) body.push(_marko_compiler.types.exportDefaultDeclaration(_marko_compiler.types.arrowFunctionExpression([], _marko_compiler.types.blockStatement([]))));
1992
2051
  }
1993
2052
  return body;
1994
2053
  },
1995
- visit(file, entryFile, visitChild) {
2054
+ visit(file, entryFile, visitChild = (id, bundled = false) => {
2055
+ const state = entryFile[kState];
2056
+ const resolved = resolveRelativeToEntry(entryFile, file, id);
2057
+ const seenBundled = state.visited.get(resolved);
2058
+ if (seenBundled === false || seenBundled && bundled) return;
2059
+ state.visited.set(resolved, bundled);
2060
+ const childFile = (0, _marko_compiler_babel_utils.loadFileForImport)(entryFile, resolved);
2061
+ if (childFile) builder.visit(childFile, entryFile);
2062
+ }) {
1996
2063
  const state = entryFile[kState] ||= {
1997
2064
  init: false,
1998
- assets: /* @__PURE__ */ new Set()
2065
+ load: false,
2066
+ bundled: 0,
2067
+ roots: [],
2068
+ assets: /* @__PURE__ */ new Set(),
2069
+ bundledAssets: /* @__PURE__ */ new Set(),
2070
+ visited: /* @__PURE__ */ new Map([[(0, _marko_compiler_babel_utils.resolveRelativePath)(entryFile, entryFile.opts.filename), false]])
1999
2071
  };
2000
2072
  const programExtra = file.path.node.extra;
2001
2073
  const { analyzedTags, assetImports } = file.metadata.marko;
2002
- if (programExtra.isInteractive || programExtra.needsCompat) state.init = true;
2003
- if (assetImports) for (const request of assetImports) state.assets.add(resolveRelativeToEntry(entryFile, file, request));
2004
- for (const tag of analyzedTags || []) visitChild(tag);
2074
+ const { loadImports } = programExtra;
2075
+ const init = !!(programExtra.isInteractive || programExtra.needsCompat);
2076
+ const load = !!programExtra.hasClientStatement;
2077
+ const isRoot = !state.bundled && (init || load || !!programExtra.hasResumes);
2078
+ if (init) state.init = true;
2079
+ if (load) state.load = true;
2080
+ if (isRoot) state.roots.push((0, _marko_compiler_babel_utils.resolveRelativePath)(entryFile, file.opts.filename));
2081
+ if (assetImports) {
2082
+ const assets = isRoot || state.bundled ? state.bundledAssets : state.assets;
2083
+ for (const request of assetImports) assets.add(resolveRelativeToEntry(entryFile, file, request));
2084
+ }
2085
+ if (isRoot) state.bundled++;
2086
+ for (const tag of analyzedTags ? [...analyzedTags] : []) {
2087
+ const lazy = loadImports?.has(tag);
2088
+ if (lazy) state.bundled++;
2089
+ visitChild(tag, !!state.bundled);
2090
+ if (lazy) state.bundled--;
2091
+ }
2092
+ if (isRoot) state.bundled--;
2005
2093
  }
2006
2094
  };
2007
2095
  //#endregion
@@ -2536,26 +2624,6 @@ function sectionHasSetupStatements(section) {
2536
2624
  return false;
2537
2625
  }
2538
2626
  //#endregion
2539
- //#region src/translator/util/binding-has-prop.ts
2540
- function isSectionRendererElided(section) {
2541
- return !!section.downstreamBinding && !bindingHasProperty(section.downstreamBinding.binding, section.downstreamBinding.properties);
2542
- }
2543
- function bindingHasProperty(binding, properties) {
2544
- if (binding.pruned) return false;
2545
- else if (binding.pruned === void 0) throw new Error("Binding must be pruned before checking properties");
2546
- if (binding.reads.size || !properties) return true;
2547
- let property;
2548
- let rest;
2549
- if (Array.isArray(properties)) {
2550
- property = properties[0];
2551
- rest = properties.length === 2 ? properties[1] : properties.slice(1);
2552
- } else property = properties;
2553
- const propBinding = binding.propertyAliases.get(property);
2554
- if (propBinding && bindingHasProperty(propBinding, rest)) return true;
2555
- for (const alias of binding.aliases) if (bindingHasProperty(alias, properties)) return true;
2556
- return false;
2557
- }
2558
- //#endregion
2559
2627
  //#region src/translator/util/module-registrations.ts
2560
2628
  /**
2561
2629
  * Writes the registrations for module scoped functions: the ones this template
@@ -6364,6 +6432,9 @@ var program_default = {
6364
6432
  const paramsBinding = programExtra.binding;
6365
6433
  if (paramsBinding && !paramsBinding.pruned) programExtra.domExports.params = getBindingPropTree(paramsBinding);
6366
6434
  const section = programExtra.section;
6435
+ forEachSection((childSection) => {
6436
+ programExtra.hasResumes ||= !!(childSection.serializeReason || childSection.serializeReasons.size || childSection !== section && !isSectionRendererElided(childSection) && getSectionRegisterReasons(childSection));
6437
+ });
6367
6438
  if (!section.hoistedTo && !sectionHasSetupStatements(section)) programExtra.domExports.setupEmpty = true;
6368
6439
  }
6369
6440
  },
@@ -6388,16 +6459,8 @@ var program_default = {
6388
6459
  }
6389
6460
  if (isDOMPageEntry) {
6390
6461
  const entryFile = program.hub.file;
6391
- const { filename } = entryFile.opts;
6392
- const visitedFiles = /* @__PURE__ */ new Set([(0, _marko_compiler_babel_utils.resolveRelativePath)(entryFile, filename)]);
6393
- entry_builder_default.visit(entryFile, entryFile, function visitChild(resolved) {
6394
- if (!visitedFiles.has(resolved)) {
6395
- visitedFiles.add(resolved);
6396
- const file = (0, _marko_compiler_babel_utils.loadFileForImport)(entryFile, resolved);
6397
- if (file) entry_builder_default.visit(file, entryFile, (id) => visitChild(resolveRelativeToEntry(entryFile, file, id)));
6398
- }
6399
- });
6400
- program.node.body = entry_builder_default.build(entryFile);
6462
+ builder.visit(entryFile, entryFile);
6463
+ program.node.body = builder.build(entryFile);
6401
6464
  program.skip();
6402
6465
  return;
6403
6466
  }
@@ -6444,13 +6507,16 @@ var program_default = {
6444
6507
  //#endregion
6445
6508
  //#region src/translator/util/set-tag-sections-downstream.ts
6446
6509
  const [getTagDownstreams] = createSectionState("tag-downstreams", () => /* @__PURE__ */ new Map());
6447
- function setTagDownstream(tag, binding) {
6448
- if (binding) getTagDownstreams(getSection(tag)).set(tag, binding);
6510
+ function setTagDownstream(tag, binding, exprs) {
6511
+ if (binding) getTagDownstreams(getSection(tag)).set(tag, {
6512
+ binding,
6513
+ exprs
6514
+ });
6449
6515
  }
6450
6516
  function finalizeTagDownstreams(section) {
6451
- for (const [tag, binding] of getTagDownstreams(section)) crawlSectionsAndSetBinding(tag, binding);
6517
+ for (const [tag, { binding, exprs }] of getTagDownstreams(section)) crawlSectionsAndSetBinding(tag, binding, exprs);
6452
6518
  }
6453
- function crawlSectionsAndSetBinding(tag, binding, properties, skip) {
6519
+ function crawlSectionsAndSetBinding(tag, binding, exprs, properties, skip) {
6454
6520
  if (!skip) {
6455
6521
  const contentSection = getSectionForBody(tag.get("body"));
6456
6522
  if (contentSection) {
@@ -6460,7 +6526,8 @@ function crawlSectionsAndSetBinding(tag, binding, properties, skip) {
6460
6526
  });
6461
6527
  contentSection.downstreamBinding = target && (target.noSerialize || includes(target.noSerializeProperties, "content")) ? false : {
6462
6528
  binding,
6463
- properties: concat(properties, "content")
6529
+ properties: concat(properties, "content"),
6530
+ exprs
6464
6531
  };
6465
6532
  }
6466
6533
  }
@@ -6469,8 +6536,8 @@ function crawlSectionsAndSetBinding(tag, binding, properties, skip) {
6469
6536
  const attrTags = getAttrTagPaths(tag);
6470
6537
  for (const child of attrTags) if (child.isMarkoTag()) if ((0, _marko_compiler_babel_utils.isAttributeTag)(child)) {
6471
6538
  const attrTagMeta = attrTagLookup[getTagName(child)];
6472
- crawlSectionsAndSetBinding(child, binding, concat(properties, attrTagMeta.name));
6473
- } else crawlSectionsAndSetBinding(child, binding, properties, true);
6539
+ crawlSectionsAndSetBinding(child, binding, exprs, concat(properties, attrTagMeta.name));
6540
+ } else crawlSectionsAndSetBinding(child, binding, exprs, properties, true);
6474
6541
  }
6475
6542
  //#endregion
6476
6543
  //#region src/translator/util/translate-var.ts
@@ -6537,10 +6604,10 @@ function knownTagAnalyze(tag, contentSection, propTree) {
6537
6604
  startSection(tagBody);
6538
6605
  trackParamsReferences(tagBody, 3);
6539
6606
  getKnownTags(section).push(tagExtra);
6540
- setTagDownstream(tag, propTree?.props?.[0]?.binding);
6541
6607
  tagExtra[kContentSection] = contentSection;
6542
6608
  const varBinding = trackVarReferences(tag, 5);
6543
6609
  const exprs = tagExtra[kKnownExprs] = analyzeParams(tagExtra, section, tag, propTree, attrExprs);
6610
+ setTagDownstream(tag, propTree?.props?.[0]?.binding, exprs);
6544
6611
  if (varBinding) {
6545
6612
  addSetupStatement(section);
6546
6613
  const mutatesTagVar = !!(tag.node.var.type === "Identifier" && tag.scope.getBinding(tag.node.var.name)?.constantViolations.length);
@@ -6656,7 +6723,7 @@ function analyzeParams(rootTagExtra, section, tag, propTree, rootAttrExprs) {
6656
6723
  }
6657
6724
  if (!propTree.props || propTree.rest || tag.node.arguments?.some((node) => _marko_compiler.types.isSpreadElement(node))) {
6658
6725
  const extra = inputExpr.value = mergeReferences(section, tag.node, getAllTagReferenceNodes(tag.node));
6659
- setBindingDownstream(propTree.binding, extra);
6726
+ setBindingDownstream(propTree.binding, extra, inputExpr);
6660
6727
  return inputExpr;
6661
6728
  }
6662
6729
  const known = inputExpr.known = {};
@@ -6671,7 +6738,7 @@ function analyzeParams(rootTagExtra, section, tag, propTree, rootAttrExprs) {
6671
6738
  i++;
6672
6739
  }
6673
6740
  const attrPropsTree = propTree.props[i];
6674
- if (attrPropsTree) known[i] = analyzeAttrs(rootTagExtra, section, tag, attrPropsTree, rootAttrExprs);
6741
+ if (attrPropsTree) known[i] = analyzeAttrs(rootTagExtra, section, tag, attrPropsTree, rootAttrExprs, inputExpr);
6675
6742
  else {
6676
6743
  const args = tag.node.arguments;
6677
6744
  tag.node.arguments = null;
@@ -6680,11 +6747,11 @@ function analyzeParams(rootTagExtra, section, tag, propTree, rootAttrExprs) {
6680
6747
  }
6681
6748
  return inputExpr;
6682
6749
  }
6683
- function analyzeAttrs(rootTagExtra, section, tag, propTree, rootAttrExprs) {
6750
+ function analyzeAttrs(rootTagExtra, section, tag, propTree, rootAttrExprs, rootExprs) {
6684
6751
  const inputExpr = {};
6685
6752
  if (!propTree.props) {
6686
6753
  const extra = inputExpr.value = mergeReferences(section, tag.node, getAllTagReferenceNodes(tag.node));
6687
- setBindingDownstream(propTree.binding, extra);
6754
+ setBindingDownstream(propTree.binding, extra, rootExprs);
6688
6755
  return inputExpr;
6689
6756
  }
6690
6757
  const known = inputExpr.known = {};
@@ -6716,7 +6783,7 @@ function analyzeAttrs(rootTagExtra, section, tag, propTree, rootAttrExprs) {
6716
6783
  known[attrTagMeta.name] = { value: rootTagExtra };
6717
6784
  } else if (childAttrExport.props) {
6718
6785
  remaining.delete(attrTagMeta.name);
6719
- known[attrTagMeta.name] = analyzeAttrs(rootTagExtra, section, child, childAttrExport, rootAttrExprs);
6786
+ known[attrTagMeta.name] = analyzeAttrs(rootTagExtra, section, child, childAttrExport, rootAttrExprs, rootExprs);
6720
6787
  } else analyzeDynamicAttrTagChildGroup(attrTagMeta.group, child);
6721
6788
  } else {
6722
6789
  const group = child.node.extra.attributeTagGroup;
@@ -6752,7 +6819,7 @@ function analyzeAttrs(rootTagExtra, section, tag, propTree, rootAttrExprs) {
6752
6819
  const groupKnownValue = { value: groupExtra };
6753
6820
  rootAttrExprs.add(groupExtra);
6754
6821
  forEach(bindings, (binding) => {
6755
- setBindingDownstream(binding, groupExtra);
6822
+ setBindingDownstream(binding, groupExtra, rootExprs);
6756
6823
  });
6757
6824
  for (const name of group) {
6758
6825
  const attrTagMeta = attrTagLookup[name];
@@ -6795,7 +6862,7 @@ function analyzeAttrs(rootTagExtra, section, tag, propTree, rootAttrExprs) {
6795
6862
  known[attr.name] = { value: attrExtra };
6796
6863
  rootAttrExprs.add(attrExtra);
6797
6864
  addSetupExpr(section, attr.value);
6798
- setBindingDownstream(templateExportAttr.binding, attrExtra);
6865
+ setBindingDownstream(templateExportAttr.binding, attrExtra, rootExprs);
6799
6866
  if (getRootSection(templateExportAttr.binding.section) !== (0, _marko_compiler_babel_utils.getProgram)().node.extra.section && isInvokeOnlyBinding(templateExportAttr.binding)) attrExtra.invokeOnly = true;
6800
6867
  if (knownSpread && !includes(knownSpread.binding.excludeProperties, attr.name)) addRead(attrExtra, {}, getOrCreatePropertyAlias(knownSpread.binding, attr.name), section, void 0);
6801
6868
  }
@@ -6813,16 +6880,16 @@ function analyzeAttrs(rootTagExtra, section, tag, propTree, rootAttrExprs) {
6813
6880
  known[prop] = { value: propExtra };
6814
6881
  rootAttrExprs.add(propExtra);
6815
6882
  addRead(propExtra, propExtra, propBinding, section, void 0);
6816
- setBindingDownstream(templateExportAttr === true ? propTree.rest.binding : templateExportAttr.binding, propExtra);
6883
+ setBindingDownstream(templateExportAttr === true ? propTree.rest.binding : templateExportAttr.binding, propExtra, rootExprs);
6817
6884
  }
6818
6885
  else if (spreadReferenceNodes) if (remaining.size || propTree.rest && !propTree.rest.props) {
6819
6886
  inputExpr.value = mergeReferences(section, tag.node, spreadReferenceNodes);
6820
- setBindingDownstream(propTree.rest?.binding || propTree.binding, inputExpr.value);
6887
+ setBindingDownstream(propTree.rest?.binding || propTree.binding, inputExpr.value, rootExprs);
6821
6888
  } else dropNodes(spreadReferenceNodes);
6822
6889
  else {
6823
6890
  if (restReferenceNodes) {
6824
6891
  inputExpr.value = mergeReferences(section, tag.node, restReferenceNodes);
6825
- setBindingDownstream(propTree.rest.binding, inputExpr.value);
6892
+ setBindingDownstream(propTree.rest.binding, inputExpr.value, rootExprs);
6826
6893
  }
6827
6894
  if (remaining.size) addSetupStatement(section);
6828
6895
  if (propTree.rest && !propTree.rest.props) addSetupExpr(section, tag.node);
@@ -7057,41 +7124,6 @@ function writeAttrsToSignals(tag, propTree, importAlias, info) {
7057
7124
  if (propTree.rest && !propTree.rest.props) addStatement("render", info.tagSection, tagReferencedBindings, _marko_compiler.types.expressionStatement(_marko_compiler.types.callExpression(info.getBindingIdentifier(propTree.rest.binding, importAlias + "_$rest"), [createScopeReadExpression(info.childScopeBinding, info.tagSection), _marko_compiler.types.objectExpression(restProps || [])])), true);
7058
7125
  }
7059
7126
  }
7060
- function mapParamReasonToExpr(exprs, reason) {
7061
- if (reason) {
7062
- if (reason === true) return true;
7063
- const result = /* @__PURE__ */ new Set();
7064
- forEach(reason, (prop) => {
7065
- forEach(mapParamBindingToExpr(exprs, prop), (expr) => {
7066
- result.add(expr);
7067
- });
7068
- });
7069
- return fromIter(result);
7070
- }
7071
- }
7072
- function mapParamBindingToExpr(exprs, binding) {
7073
- const isWholeAlias = binding.property === void 0 && binding.upstreamAlias !== void 0;
7074
- const props = [];
7075
- let curBinding = isWholeAlias ? binding.upstreamAlias : binding;
7076
- while (curBinding && (curBinding.property !== void 0 || curBinding.upstreamAlias)) {
7077
- if (curBinding.property !== void 0) props.push(curBinding.property);
7078
- curBinding = curBinding.upstreamAlias;
7079
- }
7080
- let curExpr = exprs;
7081
- for (let i = props.length; i--;) {
7082
- const nestedExpr = curExpr.known?.[props[i]];
7083
- if (!nestedExpr) return curExpr.value;
7084
- curExpr = nestedExpr;
7085
- }
7086
- if (isWholeAlias) {
7087
- let result = curExpr.value;
7088
- if (curExpr.known) {
7089
- for (const key in curExpr.known) if (!includes(binding.excludeProperties, key)) result = concat(result, curExpr.known[key].value);
7090
- }
7091
- return result;
7092
- }
7093
- return curExpr.value;
7094
- }
7095
7127
  function callStatement(id, ...args) {
7096
7128
  return _marko_compiler.types.expressionStatement(callExpression(id, ...args));
7097
7129
  }
@@ -7730,10 +7762,11 @@ function getCollapsibleIntersectionSource(intersection, section) {
7730
7762
  const source = sources.state || sources.param;
7731
7763
  return source && !Array.isArray(source) && source.section === section && !source.scopeOffset ? source : void 0;
7732
7764
  }
7733
- function setBindingDownstream(binding, expr) {
7765
+ function setBindingDownstream(binding, expr, exprs) {
7734
7766
  getBindingValueExprs().set(binding, expr || false);
7735
7767
  if (expr && expr !== true) forEach(expr, (expr) => {
7736
7768
  expr.downstream = bindingUtil.add(expr.downstream, binding);
7769
+ if (exprs) expr.downstreamExprs = exprs;
7737
7770
  });
7738
7771
  }
7739
7772
  const [getResolvedSources] = createProgramState(() => /* @__PURE__ */ new Set());
@@ -8301,7 +8334,12 @@ function getAllSerializeReasonsForExtra(extra) {
8301
8334
  else {
8302
8335
  serializeReasonCache.set(extra, false);
8303
8336
  forEach(extra.downstream, (binding) => {
8304
- reason = mergeSerializeReasons(reason, getAllSerializeReasonsForBinding(binding, true));
8337
+ let linked = getAllSerializeReasonsForBinding(binding, true);
8338
+ if (linked && linked !== true) {
8339
+ const exprs = extra.downstreamExprs;
8340
+ if (exprs) linked = mapDownstreamReason(binding.section.program, linked, exprs);
8341
+ }
8342
+ reason = mergeSerializeReasons(reason, linked);
8305
8343
  });
8306
8344
  }
8307
8345
  if (reason) serializeReasonCache.set(extra, reason);
@@ -8383,6 +8421,41 @@ function setReadsOwner(from, to) {
8383
8421
  cur = cur.parent;
8384
8422
  }
8385
8423
  }
8424
+ function mapParamReasonToExpr(exprs, reason) {
8425
+ if (reason) {
8426
+ if (reason === true) return true;
8427
+ const result = /* @__PURE__ */ new Set();
8428
+ forEach(reason, (prop) => {
8429
+ forEach(mapParamBindingToExpr(exprs, prop), (expr) => {
8430
+ result.add(expr);
8431
+ });
8432
+ });
8433
+ return fromIter(result);
8434
+ }
8435
+ }
8436
+ function mapParamBindingToExpr(exprs, binding) {
8437
+ const isWholeAlias = binding.property === void 0 && binding.upstreamAlias !== void 0;
8438
+ const props = [];
8439
+ let curBinding = isWholeAlias ? binding.upstreamAlias : binding;
8440
+ while (curBinding && (curBinding.property !== void 0 || curBinding.upstreamAlias)) {
8441
+ if (curBinding.property !== void 0) props.push(curBinding.property);
8442
+ curBinding = curBinding.upstreamAlias;
8443
+ }
8444
+ let curExpr = exprs;
8445
+ for (let i = props.length; i--;) {
8446
+ const nestedExpr = curExpr.known?.[props[i]];
8447
+ if (!nestedExpr) return curExpr.value;
8448
+ curExpr = nestedExpr;
8449
+ }
8450
+ if (isWholeAlias) {
8451
+ let result = curExpr.value;
8452
+ if (curExpr.known) {
8453
+ for (const key in curExpr.known) if (!includes(binding.excludeProperties, key)) result = concat(result, curExpr.known[key].value);
8454
+ }
8455
+ return result;
8456
+ }
8457
+ return curExpr.value;
8458
+ }
8386
8459
  //#endregion
8387
8460
  //#region src/translator/core/await.ts
8388
8461
  const kDOMBinding$2 = Symbol("await tag dom binding");
@@ -9811,6 +9884,7 @@ var import_declaration_default = {
9811
9884
  const { file } = importDecl.hub;
9812
9885
  const loadFile = tagImport && (0, _marko_compiler_babel_utils.loadFileForImport)(file, value);
9813
9886
  if (!loadFile) throw importDecl.buildCodeFrameError("Unable to resolve marko file for load import.");
9887
+ (file.path.node.extra.loadImports ??= /* @__PURE__ */ new Set()).add(loadFile.opts.filename);
9814
9888
  if (loadFile.ast.program.extra?.featureType === "class") throw importDecl.buildCodeFrameError(`The [\`load\` import attribute](https://markojs.com/docs/reference/lazy-loading) is not supported for the Marko 5 (class API) tag \`${value}\`. Import it without \`load\`, or migrate the tag to the tags API.`);
9815
9889
  }
9816
9890
  },
@@ -10173,7 +10247,7 @@ var scriptlet_default = {
10173
10247
  return;
10174
10248
  }
10175
10249
  mergeReferences(getOrCreateSection(scriptlet), scriptlet.node, scriptlet.node.body);
10176
- if (scriptlet.node.target === "client") (0, _marko_compiler_babel_utils.getProgram)().node.extra.isInteractive = true;
10250
+ if (scriptlet.node.target === "client") (0, _marko_compiler_babel_utils.getProgram)().node.extra.hasClientStatement = true;
10177
10251
  },
10178
10252
  translate: { exit(scriptlet) {
10179
10253
  const { node } = scriptlet;
@@ -10335,10 +10409,18 @@ function getTagRelativePath(tag) {
10335
10409
  if (!relativePath) throw tagNotFoundError(tag);
10336
10410
  return relativePath;
10337
10411
  }
10412
+ const staticHint = "To declare module level JavaScript, prefix the statement with `static`.";
10338
10413
  const knownWrongTags = /* @__PURE__ */ new Map([
10339
10414
  ["slot", "To render content passed to this tag, use a [dynamic tag](https://markojs.com/docs/reference/language#dynamic-tags): `<${input.content}/>`."],
10340
10415
  ["state", "Reactive state is declared with the [`<let>` tag](https://markojs.com/docs/reference/core-tag#let): `<let/name=initialValue>`."],
10341
- ["fragment", "Marko templates and tag bodies may have multiple root nodes; no fragment wrapper is needed."]
10416
+ ["fragment", "Marko templates and tag bodies may have multiple root nodes; no fragment wrapper is needed."],
10417
+ ["async", staticHint],
10418
+ ["class", staticHint],
10419
+ ["declare", staticHint],
10420
+ ["enum", staticHint],
10421
+ ["function", staticHint],
10422
+ ["interface", staticHint],
10423
+ ["type", staticHint]
10342
10424
  ]);
10343
10425
  function tagNotFoundError(tag) {
10344
10426
  const tagName = getTagName(tag);
@@ -10352,7 +10434,7 @@ function tagNotFoundError(tag) {
10352
10434
  const closestTag = (0, fastest_levenshtein.closest)(tagName, Object.keys((0, _marko_compiler_babel_utils.getTaglibLookup)(tag.hub.file).merged.tags));
10353
10435
  if ((0, fastest_levenshtein.distance)(tagName, closestTag) < 4) didYouMean = ` Did you mean \`<${closestTag}>\`?`;
10354
10436
  }
10355
- return tag.get("name").buildCodeFrameError(`Unable to find entry point for [custom tag](https://markojs.com/docs/reference/custom-tag#relative-custom-tags) \`<${tagName}>\`.${didYouMean}`);
10437
+ return tag.get("name").buildCodeFrameError(`Unable to find entry point for [custom tag](https://markojs.com/docs/reference/custom-tag#relative-custom-tags) \`<${tagName ?? getStaticTagName(tag.node)}>\`.${didYouMean}`);
10356
10438
  }
10357
10439
  const wordReg = /^[A-Za-z]+$/;
10358
10440
  function getProseText(tag) {
@@ -10720,7 +10802,7 @@ var text_default = {
10720
10802
  function buildAggregateError(file, rootMsg, ...paths) {
10721
10803
  const err = /* @__PURE__ */ new SyntaxError();
10722
10804
  const fileName = path.default.relative(_marko_compiler_modules.cwd, file.opts.filename);
10723
- const finalMsg = `${rootMsg}:\n\n${paths.map(([msg, path$14]) => `\x1b[90m${msg} at ${getFileNameWithLoc(fileName, path$14)}:\x1b[0m\n${getFrame(file, path$14)}`).join("\n\n")}`;
10805
+ 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")}`;
10724
10806
  if (!("MARKO_DEBUG" in process.env)) err.stack = finalMsg;
10725
10807
  Object.defineProperty(err, "message", {
10726
10808
  get() {
@@ -10884,24 +10966,30 @@ function createInteropTranslator(translate5) {
10884
10966
  })));
10885
10967
  };
10886
10968
  return [
10887
- importHydrateProgram("6", entry_builder_default.build(entryFile, true)),
10969
+ ...state.needsCompat ? [_marko_compiler.types.importDeclaration([], _marko_compiler.types.stringLiteral(getCompatRuntimeFile()))] : [],
10970
+ ...Array.from(state.compatFiles, (compatFile) => _marko_compiler.types.importDeclaration([], _marko_compiler.types.stringLiteral(compatFile))),
10971
+ importHydrateProgram("6", builder.build(entryFile, true)),
10888
10972
  importHydrateProgram("5", translate5.internalEntryBuilder.build(entryFile, true)),
10889
10973
  _marko_compiler.types.expressionStatement(_marko_compiler.types.callExpression(_marko_compiler.types.identifier("init6"), [])),
10890
10974
  _marko_compiler.types.expressionStatement(_marko_compiler.types.callExpression(_marko_compiler.types.identifier("init5"), []))
10891
10975
  ];
10892
10976
  } else return translate5.internalEntryBuilder.build(entryFile);
10893
- else return entry_builder_default.build(entryFile);
10977
+ else return builder.build(entryFile);
10894
10978
  },
10895
10979
  visit(file, entryFile, visitChild) {
10896
10980
  const state = entryFile[kState] ||= {
10897
10981
  has5: false,
10898
- has6: false
10982
+ has6: false,
10983
+ needsCompat: false,
10984
+ compatFiles: /* @__PURE__ */ new Set()
10899
10985
  };
10900
10986
  if (isTagsAPI(file)) {
10901
10987
  state.has6 = true;
10902
- entry_builder_default.visit(file, entryFile, visitChild);
10988
+ builder.visit(file, entryFile, visitChild);
10903
10989
  } else {
10904
10990
  state.has5 = true;
10991
+ if (file.path.node.extra?.resumesCompat) state.needsCompat = true;
10992
+ if (file.path.node.extra?.resumesClassFns) state.compatFiles.add((0, _marko_compiler_babel_utils.resolveRelativePath)(entryFile, file.opts.filename));
10905
10993
  translate5.internalEntryBuilder.visit(file, entryFile, visitChild);
10906
10994
  }
10907
10995
  }
@@ -10914,13 +11002,13 @@ function createInteropTranslator(translate5) {
10914
11002
  const entryFile = program.hub.file;
10915
11003
  const { output, entry } = entryFile.markoOpts;
10916
11004
  if (!(output === "dom" && entry === "page" || output === "hydrate")) return enterProgram?.call(this, program, state);
10917
- const visitedFiles = /* @__PURE__ */ new Set([(0, _marko_compiler_babel_utils.resolveRelativePath)(entryFile, entryFile.opts.filename)]);
10918
- entryBuilder.visit(entryFile, entryFile, function visitChild(resolved) {
10919
- if (!visitedFiles.has(resolved)) {
10920
- visitedFiles.add(resolved);
10921
- const file = (0, _marko_compiler_babel_utils.loadFileForImport)(entryFile, resolved);
10922
- if (file) entryBuilder.visit(file, entryFile, (id) => visitChild(resolveRelativeToEntry(entryFile, file, id)));
10923
- }
11005
+ const visitedFiles = /* @__PURE__ */ new Map([[(0, _marko_compiler_babel_utils.resolveRelativePath)(entryFile, entryFile.opts.filename), false]]);
11006
+ entryBuilder.visit(entryFile, entryFile, function visitChild(resolved, bundled = false) {
11007
+ const seenBundled = visitedFiles.get(resolved);
11008
+ if (seenBundled === false || seenBundled && bundled) return;
11009
+ visitedFiles.set(resolved, bundled);
11010
+ const file = (0, _marko_compiler_babel_utils.loadFileForImport)(entryFile, resolved);
11011
+ if (file) entryBuilder.visit(file, entryFile, (id, childBundled = false) => visitChild(resolveRelativeToEntry(entryFile, file, id), childBundled || bundled));
10924
11012
  });
10925
11013
  program.node.body = entryBuilder.build(entryFile);
10926
11014
  program.skip();
@@ -10936,13 +11024,13 @@ function mergeVisitors(visitor5, visitor6) {
10936
11024
  function mergeVisit(visit5, visit6) {
10937
11025
  const enter5 = getVisitorEnter(visit5);
10938
11026
  const enter6 = getVisitorEnter(visit6);
10939
- const enter = (enter5 || enter6) && function enter(path$10, state) {
10940
- return (isTagsAPI() ? enter6 : enter5)?.call(this, path$10, state);
11027
+ const enter = (enter5 || enter6) && function enter(path$9, state) {
11028
+ return (isTagsAPI() ? enter6 : enter5)?.call(this, path$9, state);
10941
11029
  };
10942
11030
  const exit5 = getVisitorExit(visit5);
10943
11031
  const exit6 = getVisitorExit(visit6);
10944
- const exit = (exit5 || exit6) && function exit(path$11, state) {
10945
- return (isTagsAPI() ? exit6 : exit5)?.call(this, path$11, state);
11032
+ const exit = (exit5 || exit6) && function exit(path$10, state) {
11033
+ return (isTagsAPI() ? exit6 : exit5)?.call(this, path$10, state);
10946
11034
  };
10947
11035
  return exit ? enter ? {
10948
11036
  enter,
@@ -11025,17 +11113,17 @@ function sequenceVisit(first, second) {
11025
11113
  if (!first || !second) return first || second;
11026
11114
  const enterFirst = getVisitorEnter(first);
11027
11115
  const enterSecond = getVisitorEnter(second);
11028
- const enter = (enterFirst || enterSecond) && function enter(path$12, state) {
11029
- const { node } = path$12;
11030
- enterFirst?.call(this, path$12, state);
11031
- if (path$12.node === node) enterSecond?.call(this, path$12, state);
11116
+ const enter = (enterFirst || enterSecond) && function enter(path$11, state) {
11117
+ const { node } = path$11;
11118
+ enterFirst?.call(this, path$11, state);
11119
+ if (path$11.node === node) enterSecond?.call(this, path$11, state);
11032
11120
  };
11033
11121
  const exitFirst = getVisitorExit(first);
11034
11122
  const exitSecond = getVisitorExit(second);
11035
- const exit = (exitFirst || exitSecond) && function exit(path$13, state) {
11036
- const { node } = path$13;
11037
- exitFirst?.call(this, path$13, state);
11038
- if (path$13.node === node) exitSecond?.call(this, path$13, state);
11123
+ const exit = (exitFirst || exitSecond) && function exit(path$12, state) {
11124
+ const { node } = path$12;
11125
+ exitFirst?.call(this, path$12, state);
11126
+ if (path$12.node === node) exitSecond?.call(this, path$12, state);
11039
11127
  };
11040
11128
  return exit ? enter ? {
11041
11129
  enter,
@@ -11083,7 +11171,7 @@ exports.analyze = analyze;
11083
11171
  exports.cheatsheet = cheatsheet;
11084
11172
  exports.createInteropTranslator = createInteropTranslator;
11085
11173
  exports.getRuntimeEntryFiles = getRuntimeEntryFiles;
11086
- exports.internalEntryBuilder = entry_builder_default;
11174
+ exports.internalEntryBuilder = builder;
11087
11175
  exports.preferAPI = preferAPI;
11088
11176
  exports.tagDiscoveryDirs = tagDiscoveryDirs;
11089
11177
  exports.taglibs = taglibs;
@@ -1,4 +1,12 @@
1
1
  import { type Config, types as t } from "@marko/compiler";
2
+ declare module "@marko/compiler/dist/types" {
3
+ interface ProgramExtra {
4
+ /** A Class API template that registers hoisted handlers for Tags resume. */
5
+ resumesClassFns?: boolean;
6
+ /** A Class API template whose Tags child revives through the compat runtime. */
7
+ resumesCompat?: boolean;
8
+ }
9
+ }
2
10
  type Taglibs = [taglibId: string, taglib: Record<string, unknown>][];
3
11
  export declare function createInteropTranslator(translate5: any): {
4
12
  version: any;
@@ -3,19 +3,33 @@ declare module "@marko/compiler/dist/types" {
3
3
  interface ProgramExtra {
4
4
  needsCompat?: boolean;
5
5
  isInteractive?: boolean;
6
+ hasClientStatement?: boolean;
6
7
  page?: boolean;
7
8
  }
8
9
  }
9
10
  interface EntryState {
10
11
  init: boolean;
12
+ load: boolean;
13
+ /** Depth of enclosing templates whose modules the bundle already loads:
14
+ * below a root everything arrives through its imports, and a lazy subtree
15
+ * is loaded by its own load entry. */
16
+ bundled: number;
17
+ roots: string[];
18
+ /** Assets of templates the bundle never loads; the entry imports them. */
11
19
  assets: Set<string>;
20
+ /** Assets that arrive through a bundled template's imports; the entry
21
+ * imports them itself only when it links nothing (a server only page). */
22
+ bundledAssets: Set<string>;
23
+ /** Whether each reached file was only ever seen below a bundled template. */
24
+ visited: Map<string, boolean>;
12
25
  }
13
26
  type EntryFile = t.BabelFile & {
14
27
  [kState]?: EntryState;
15
28
  };
29
+ type VisitChild = (id: string, bundled?: boolean) => void;
16
30
  declare const kState: unique symbol;
17
- declare const _default: {
31
+ declare const builder: {
18
32
  build(entryFile: EntryFile, exportInit?: boolean): t.Statement[];
19
- visit(file: t.BabelFile, entryFile: EntryFile, visitChild: (id: string) => void): void;
33
+ visit(file: t.BabelFile, entryFile: EntryFile, visitChild?: VisitChild): void;
20
34
  };
21
- export default _default;
35
+ export default builder;
@@ -1,11 +1,7 @@
1
1
  import { types as t } from "@marko/compiler";
2
2
  import { type BindingPropTree } from "./binding-prop-tree";
3
- import { type Binding } from "./references";
3
+ import { type Binding, type KnownExprs } from "./references";
4
4
  import { type Section } from "./sections";
5
- interface KnownExprs {
6
- known?: Record<string, KnownExprs>;
7
- value?: t.NodeExtra;
8
- }
9
5
  declare const kContentSection: unique symbol;
10
6
  declare const kChildScopeBinding: unique symbol;
11
7
  declare const kChildOffsetScopeBinding: unique symbol;
@@ -90,6 +90,9 @@ declare module "@marko/compiler/dist/types" {
90
90
  section?: Section;
91
91
  referencedBindings?: ReferencedBindings;
92
92
  downstream?: Opt<Binding>;
93
+ /** The tag-root `KnownExprs` of the call site that linked this expression
94
+ * to a downstream template's binding, for dereferencing its reasons. */
95
+ downstreamExprs?: KnownExprs;
93
96
  binding?: Binding;
94
97
  assignment?: Binding;
95
98
  assignmentTo?: Binding;
@@ -141,7 +144,7 @@ export declare const intersectionMeta: WeakMap<Intersection, {
141
144
  scopeOffset: Binding | undefined;
142
145
  }>;
143
146
  export declare const collapsedIntersectionSource: WeakMap<Intersection, Binding>;
144
- export declare function setBindingDownstream(binding: Binding, expr: boolean | Opt<t.NodeExtra>): void;
147
+ export declare function setBindingDownstream(binding: Binding, expr: boolean | Opt<t.NodeExtra>, exprs?: KnownExprs): void;
145
148
  export declare function createSources(state: Sources["state"], param: Sources["param"], global?: Sources["global"]): Sources;
146
149
  export declare function compareSources(a: Sources, b: Sources): number;
147
150
  export declare function mergeSources(a: undefined | Sources, b: undefined | Sources): Sources | undefined;
@@ -191,3 +194,9 @@ export declare function isRegisteredFnExtra(extra: t.NodeExtra | undefined): ext
191
194
  export declare function getCanonicalExtra<T extends t.NodeExtra>(extra: T): T;
192
195
  export declare function getAllSerializeReasonsForExtra(extra: t.NodeExtra): undefined | SerializeReason;
193
196
  export declare function getAllSerializeReasonsForBinding(binding: Binding, properties?: Opt<string> | true): undefined | SerializeReason;
197
+ export interface KnownExprs {
198
+ known?: Record<string, KnownExprs>;
199
+ value?: t.NodeExtra;
200
+ }
201
+ export declare function mapParamReasonToExpr(exprs: KnownExprs, reason: boolean | Opt<InputBinding | ParamBinding>): true | Many<t.NodeExtra> | t.NodeExtra | undefined;
202
+ export declare function mapParamBindingToExpr(exprs: KnownExprs, binding: InputBinding | ParamBinding): Opt<t.NodeExtra>;
@@ -5,7 +5,7 @@ import * as ContentType from "./constants/content-type";
5
5
  import type * as Step from "./constants/step";
6
6
  import * as StructureKind from "./constants/structure-kind";
7
7
  import { type Opt, Sorted } from "./optional";
8
- import { type Binding, type InputBinding, type ParamBinding, type ReferencedBindings, type Sources } from "./references";
8
+ import { type Binding, type InputBinding, type KnownExprs, type ParamBinding, type ReferencedBindings, type Sources } from "./references";
9
9
  import { type SerializeReason } from "./serialize-reasons";
10
10
  export interface ParamSerializeReasonGroup {
11
11
  id: symbol;
@@ -50,6 +50,7 @@ export interface Section {
50
50
  loc: t.SourceLocation | undefined;
51
51
  depth: number;
52
52
  parent: Section | undefined;
53
+ program: Section;
53
54
  sectionAccessor: {
54
55
  binding: Binding;
55
56
  prefix: AccessorPrefix;
@@ -71,6 +72,7 @@ export interface Section {
71
72
  downstreamBinding: {
72
73
  binding: Binding;
73
74
  properties: Opt<string>;
75
+ exprs: KnownExprs | undefined;
74
76
  } | false | undefined;
75
77
  hasAbortSignal: boolean;
76
78
  /** Count of distinct `$signal` expression roots; analyze allocates each
@@ -1,7 +1,7 @@
1
1
  import { types as t } from "@marko/compiler";
2
2
  import { AccessorPrefix, AccessorProp } from "../../common/types";
3
3
  import { type Opt } from "./optional";
4
- import { type Binding, type ReferencedBindings, type Sources } from "./references";
4
+ import { type Binding, type KnownExprs, type ReferencedBindings, type Sources } from "./references";
5
5
  import type { Section } from "./sections";
6
6
  export type SerializeReasons = true | [Sources, ...Sources[]];
7
7
  export type SerializeReason = true | Sources;
@@ -19,6 +19,8 @@ export declare function getSerializeReason(section: Section, prop?: Binding | Ac
19
19
  export declare function getSerializeSourcesForExpr(expr: t.NodeExtra): Sources | undefined;
20
20
  export declare function getSerializeSourcesForExprs(exprs: Opt<t.NodeExtra> | boolean): true | Sources | undefined;
21
21
  export declare function getSerializeSourcesForRef(ref: ReferencedBindings): Sources | undefined;
22
+ export declare function mapCrossProgramReason(program: Section, reason: Sources, exprs: KnownExprs | undefined): SerializeReason | undefined;
23
+ export declare function mapDownstreamReason(program: Section, reason: Sources, exprs: KnownExprs): SerializeReason | undefined;
22
24
  export declare function mergeSerializeReasons(a: SerializeReason, b: undefined | SerializeReason): SerializeReason;
23
25
  export declare function mergeSerializeReasons(a: undefined | SerializeReason, b: SerializeReason): SerializeReason;
24
26
  export declare function mergeSerializeReasons(a: undefined | SerializeReason, b: undefined | SerializeReason): SerializeReason | undefined;
@@ -1,5 +1,5 @@
1
1
  import { types as t } from "@marko/compiler";
2
- import type { Binding } from "./references";
2
+ import type { Binding, KnownExprs } from "./references";
3
3
  import { type Section } from "./sections";
4
- export declare function setTagDownstream(tag: t.NodePath<t.MarkoTag>, binding: undefined | Binding): void;
4
+ export declare function setTagDownstream(tag: t.NodePath<t.MarkoTag>, binding: undefined | Binding, exprs?: KnownExprs): void;
5
5
  export declare function finalizeTagDownstreams(section: Section): void;
@@ -4,6 +4,8 @@ import * as TagNameType from "./constants/tag-name-type";
4
4
  declare module "@marko/compiler/dist/types" {
5
5
  interface ProgramExtra {
6
6
  featureType?: "class" | "tags";
7
+ /** Set by the Class API translator when Tags content resumes below here. */
8
+ hydratesTags?: boolean;
7
9
  }
8
10
  interface MarkoTagExtra {
9
11
  tagNameType?: TagNameType;
@@ -2,6 +2,10 @@ import { types as t } from "@marko/compiler";
2
2
  import type { LoadTrigger } from "../../html/assets";
3
3
  import { type ResolvedExport } from "./function";
4
4
  declare module "@marko/compiler/dist/types" {
5
+ interface ProgramExtra {
6
+ /** Absolute filenames of templates this one imports with `load`. */
7
+ loadImports?: Set<string>;
8
+ }
5
9
  interface NodeExtra {
6
10
  tagImport?: string;
7
11
  loadImport?: LoadImportConfig;
@@ -5,6 +5,7 @@ export declare let scopeIdentifier: t.Identifier;
5
5
  export declare let localsIdentifier: t.Identifier;
6
6
  declare module "@marko/compiler/dist/types" {
7
7
  interface ProgramExtra {
8
+ hasResumes?: boolean;
8
9
  domExports?: {
9
10
  template: string;
10
11
  walks: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marko",
3
- "version": "6.3.44",
3
+ "version": "6.3.45",
4
4
  "description": "Optimized runtime for Marko templates.",
5
5
  "keywords": [
6
6
  "api",
@@ -51,14 +51,14 @@
51
51
  }
52
52
  },
53
53
  "dependencies": {
54
- "@marko/compiler": "^5.42.2",
54
+ "@marko/compiler": "^5.42.3",
55
55
  "csstype": "^3.2.3",
56
56
  "fastest-levenshtein": "^1.0.16",
57
57
  "magic-string": "^0.30.21"
58
58
  },
59
59
  "devDependencies": {
60
- "@marko/runtime-tags": "npm:marko@6.3.44",
61
- "marko": "5.39.36"
60
+ "marko": "5.39.37",
61
+ "@marko/runtime-tags": "npm:marko@6.3.45"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=22"