@rozie/cli 0.1.0 → 0.1.1

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.
@@ -10075,6 +10075,66 @@ function subtreeReads(node, accessor, name) {
10075
10075
  return found;
10076
10076
  }
10077
10077
  /**
10078
+ * Returns true if the subtree rooted at `node` contains a BARE Identifier READ of
10079
+ * `name` — the exact shape the Vue `$computed` lowering wraps to `<name>.value`.
10080
+ *
10081
+ * The Vue trigger condition for the `$computed` shadow bug: a `$computed` name is
10082
+ * read as a BARE identifier in source (there is NO `$computed.<name>` accessor
10083
+ * member to gate on), and the downstream Identifier visitor `.value`-wraps every
10084
+ * such bare read. A colliding param/local only mis-captures that wrap when it
10085
+ * actually performs a bare read of `name` within its scope — so a `binding`
10086
+ * trigger (mere existence) would over-apply (renaming an unused same-named param
10087
+ * → corpus drift), and the `accessor` trigger cannot fire (no member access).
10088
+ *
10089
+ * Excludes the SAME non-read positions the Vue Identifier visitor skips, so the
10090
+ * gate matches the rewrite exactly: declaration ids, non-computed member
10091
+ * properties, object keys (shorthand or not), TS type positions, function names,
10092
+ * function params, and import/export specifier ids. A computed member property
10093
+ * (`obj[name]`) IS a bare read and counts.
10094
+ *
10095
+ * Hand-rolled own-child walk (the `subtreeReads` twin): a direct walk with
10096
+ * parent-context tracking is simpler than a Program-rooted Babel traverse and
10097
+ * has no rooting constraint.
10098
+ */
10099
+ function subtreeReadsBareIdentifier(node, name) {
10100
+ if (!node) return false;
10101
+ let found = false;
10102
+ function walk(n, parent) {
10103
+ if (found || !n || typeof n !== "object" || !("type" in n)) return;
10104
+ if (_babel_types.isIdentifier(n) && n.name === name) {
10105
+ if (isBareRead(n, parent)) {
10106
+ found = true;
10107
+ return;
10108
+ }
10109
+ }
10110
+ if (n.type.startsWith("TS") && n.type !== "TSNonNullExpression" && n.type !== "TSAsExpression" && n.type !== "TSSatisfiesExpression" && n.type !== "TSTypeAssertion") return;
10111
+ for (const key of Object.keys(n)) {
10112
+ if (key === "loc" || key === "start" || key === "end" || key === "leadingComments" || key === "trailingComments" || key === "innerComments") continue;
10113
+ const v = n[key];
10114
+ if (Array.isArray(v)) {
10115
+ for (const item of v) if (item && typeof item === "object" && "type" in item) walk(item, n);
10116
+ } else if (v && typeof v === "object" && "type" in v) walk(v, n);
10117
+ }
10118
+ }
10119
+ function isBareRead(id, parent) {
10120
+ if (!parent) return true;
10121
+ if (_babel_types.isVariableDeclarator(parent) && parent.id === id) return false;
10122
+ if ((_babel_types.isMemberExpression(parent) || _babel_types.isOptionalMemberExpression(parent)) && parent.property === id && !parent.computed) return false;
10123
+ if (_babel_types.isObjectProperty(parent) && parent.key === id && !parent.computed) return false;
10124
+ if (_babel_types.isFunctionDeclaration(parent) && parent.id === id) return false;
10125
+ if (_babel_types.isFunctionExpression(parent) && parent.id === id) return false;
10126
+ if (_babel_types.isFunction(parent) && parent.params.includes(id)) return false;
10127
+ if (_babel_types.isImportSpecifier(parent)) return false;
10128
+ if (_babel_types.isImportDefaultSpecifier(parent)) return false;
10129
+ if (_babel_types.isImportNamespaceSpecifier(parent)) return false;
10130
+ if (_babel_types.isExportSpecifier(parent)) return false;
10131
+ if (_babel_types.isLabeledStatement(parent) && parent.label === id) return false;
10132
+ return true;
10133
+ }
10134
+ walk(node, null);
10135
+ return found;
10136
+ }
10137
+ /**
10078
10138
  * THE UNIFIED PASS. Renames any USER binding (function param or
10079
10139
  * `const`/`let`/`var` declarator) that collides with a generated symbol to
10080
10140
  * `<name>$local`, atomically across its binding scope, gated only-on-collision
@@ -10094,6 +10154,7 @@ function deconflictGeneratedSymbols(program, groups, protectedNames = /* @__PURE
10094
10154
  if (!group.names.has(name)) return false;
10095
10155
  if (protectedNames.has(name)) return false;
10096
10156
  if (group.trigger.kind === "binding") return true;
10157
+ if (group.trigger.kind === "bare-read") return subtreeReadsBareIdentifier(scopeBlock, name);
10097
10158
  return subtreeReads(scopeBlock, group.trigger.accessor, name);
10098
10159
  };
10099
10160
  traverse$19(program, {
@@ -10107,6 +10168,7 @@ function deconflictGeneratedSymbols(program, groups, protectedNames = /* @__PURE
10107
10168
  if (!patternIntroducesBinding$3(id, name)) continue;
10108
10169
  const binding = path.scope.getBinding(name);
10109
10170
  const ownerScope = binding ? binding.scope : path.scope;
10171
+ if (group.trigger.kind === "bare-read" && _babel_types.isProgram(ownerScope.block)) continue;
10110
10172
  if (collides(group, name, ownerScope.block)) ownerScope.rename(name, alias(name));
10111
10173
  }
10112
10174
  },
@@ -18732,17 +18794,53 @@ function bindingNames(stmt) {
18732
18794
  function importLocalNames(imp) {
18733
18795
  return imp.specifiers.map((s) => s.local.name);
18734
18796
  }
18797
+ /**
18798
+ * Root identifier of a TS entity name (`Foo` or `Foo.Bar.Baz` → `Foo`). A
18799
+ * `TSImportType` (`import('pkg').Foo`) has no root local identifier — returns
18800
+ * null (its module surface is already self-contained, never a hoist target).
18801
+ */
18802
+ function rootTypeIdentifier(name) {
18803
+ let node = name;
18804
+ while (node && _babel_types.isTSQualifiedName(node)) node = node.left;
18805
+ return node && _babel_types.isIdentifier(node) ? node.name : null;
18806
+ }
18735
18807
  /** Referenced identifier names within a statement (excludes bindings/keys). */
18736
18808
  function referencedNames(stmt) {
18737
18809
  const out = /* @__PURE__ */ new Set();
18738
18810
  try {
18739
- traverse$18(_babel_types.file(_babel_types.program([stmt])), { ReferencedIdentifier(path) {
18740
- out.add(path.node.name);
18741
- } });
18811
+ traverse$18(_babel_types.file(_babel_types.program([stmt])), {
18812
+ ReferencedIdentifier(path) {
18813
+ out.add(path.node.name);
18814
+ },
18815
+ TSTypeReference(path) {
18816
+ const root = rootTypeIdentifier(path.node.typeName);
18817
+ if (root) out.add(root);
18818
+ },
18819
+ TSTypeQuery(path) {
18820
+ const root = rootTypeIdentifier(path.node.exprName);
18821
+ if (root) out.add(root);
18822
+ }
18823
+ });
18742
18824
  } catch {}
18743
18825
  return out;
18744
18826
  }
18745
18827
  /**
18828
+ * Effective import kind of a single specifier. A specifier is type-only when
18829
+ * EITHER the declaration is `import type { … }` (declKind === 'type', in which
18830
+ * case Babel reports the per-specifier `importKind` as `'value'`) OR the
18831
+ * specifier itself is the inline `import { type X }` form (spec.importKind ===
18832
+ * 'type'). The old `spec.importKind ? spec.importKind : declKind` form let the
18833
+ * `'value'` per-specifier kind of a declaration-level `import type` mask the
18834
+ * declaration's type-ness, so a hoisted `import type { T }` lost its type-only
18835
+ * marker and emitted as a runtime `import { T }` (IN-02). Value imports are
18836
+ * unaffected (both kinds are `'value'`).
18837
+ */
18838
+ function effectiveImportKind(declKind, spec) {
18839
+ if (declKind === "type") return "type";
18840
+ if (_babel_types.isImportSpecifier(spec) && spec.importKind === "type") return "type";
18841
+ return "value";
18842
+ }
18843
+ /**
18746
18844
  * Dedup key for a single import specifier per the R4 tuple:
18747
18845
  * (source, importKind, default|namespace|named:imported, local). Including the
18748
18846
  * LOCAL name keeps `import { thing }` and `import { thing as aliased }` distinct
@@ -18750,7 +18848,7 @@ function referencedNames(stmt) {
18750
18848
  * host and partial collapses to ONE statement.
18751
18849
  */
18752
18850
  function specifierKey(source, declKind, spec) {
18753
- const kind = (_babel_types.isImportSpecifier(spec) && spec.importKind ? spec.importKind : declKind) || "value";
18851
+ const kind = effectiveImportKind(declKind, spec);
18754
18852
  if (_babel_types.isImportDefaultSpecifier(spec)) return `${source}\0${kind}\0default\0${spec.local.name}`;
18755
18853
  if (_babel_types.isImportNamespaceSpecifier(spec)) return `${source}\0${kind}\0namespace\0${spec.local.name}`;
18756
18854
  return `${source}\0${kind}\0named:${_babel_types.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value}\0${spec.local.name}`;
@@ -18760,12 +18858,12 @@ function specifierKey(source, declKind, spec) {
18760
18858
  * deduped by {@link specifierKey} and grouped by (source, importKind) so the
18761
18859
  * splice produces idiomatic merged `import { a, b } from 'src'` statements.
18762
18860
  */
18763
- function hoistSpecifier(ctx, sourceNode, declKind, spec) {
18861
+ function hoistSpecifier(ctx, sourceNode, declKind, spec, sourceImport) {
18764
18862
  const source = sourceNode.value;
18765
18863
  const key = specifierKey(source, declKind, spec);
18766
18864
  if (ctx.hoistKeys.has(key)) return;
18767
18865
  ctx.hoistKeys.add(key);
18768
- const groupKind = (_babel_types.isImportSpecifier(spec) && spec.importKind ? spec.importKind : declKind) || "value";
18866
+ const groupKind = effectiveImportKind(declKind, spec);
18769
18867
  const groupKey = `${source}\0${groupKind}`;
18770
18868
  const existing = ctx.hoistGroups.get(groupKey);
18771
18869
  if (existing) {
@@ -18774,9 +18872,427 @@ function hoistSpecifier(ctx, sourceNode, declKind, spec) {
18774
18872
  }
18775
18873
  const decl = _babel_types.importDeclaration([spec], sourceNode);
18776
18874
  if (groupKind === "type") decl.importKind = "type";
18875
+ if (sourceImport) {
18876
+ if (sourceImport.loc) decl.loc = sourceImport.loc;
18877
+ if (sourceImport.trailingComments) decl.trailingComments = sourceImport.trailingComments;
18878
+ if (sourceImport.innerComments) decl.innerComments = sourceImport.innerComments;
18879
+ }
18777
18880
  ctx.hoistGroups.set(groupKey, decl);
18778
18881
  ctx.hoistImports.push(decl);
18779
18882
  }
18883
+ /**
18884
+ * Shift a single Babel `Position` object's `.line` by `offset`, exactly once.
18885
+ *
18886
+ * CRITICAL (Phase 55-04 bug fix): `@babel/parser` SHARES one `Position` object
18887
+ * across the `loc.start`/`loc.end` of nested nodes that begin/end at the same
18888
+ * source position — e.g. a `const f = () => {...}` shares ONE `loc.end` object
18889
+ * between the VariableDeclaration, VariableDeclarator, ArrowFunctionExpression
18890
+ * and BlockStatement (verified: all four `.loc.end` are `===`). The earlier
18891
+ * `seen`-keyed-on-the-`loc`-WRAPPER dedupe therefore shifted such a shared
18892
+ * `Position` once PER wrapping node (4× for the example above), corrupting
18893
+ * `loc.end.line` to `original + 4·offset` (~13985 for a host-line-3502 decl).
18894
+ * That blew the trailing-comment delta hugely negative, collapsing a between-
18895
+ * declaration comment onto the prior closing brace (`}; // comment`). Deduping on
18896
+ * the `Position` object itself shifts each unique position exactly once. Never
18897
+ * throws (D-04).
18898
+ */
18899
+ function shiftPositionLine(pos, offset, shifted) {
18900
+ if (!pos || shifted.has(pos)) return;
18901
+ shifted.add(pos);
18902
+ pos.line += offset;
18903
+ }
18904
+ /**
18905
+ * Stash a node's `.rzts` origin (once, idempotent) then shift its `loc` lines by
18906
+ * `offset`. Byte offsets (`loc.start.index`/`loc.end.index`) and `loc.filename`
18907
+ * are NEVER touched. `shifted` dedupes shared `Position` objects (see
18908
+ * {@link shiftPositionLine}) so a position aliased across nested nodes — or a
18909
+ * comment attached to two adjacent statements — is shifted exactly once
18910
+ * (Pitfall 2). Never throws (D-04).
18911
+ */
18912
+ function stashAndShiftNode(node, offset, shifted) {
18913
+ const loc = node.loc;
18914
+ if (!loc) return;
18915
+ const extra = node.extra ?? {};
18916
+ if (!("__roziePartialOrigin" in extra)) {
18917
+ const startAlreadyShifted = shifted.has(loc.start);
18918
+ node.extra = {
18919
+ ...extra,
18920
+ __roziePartialOrigin: {
18921
+ line: loc.start.line - (startAlreadyShifted ? offset : 0),
18922
+ column: loc.start.column,
18923
+ filename: loc.filename
18924
+ }
18925
+ };
18926
+ }
18927
+ shiftPositionLine(loc.start, offset, shifted);
18928
+ shiftPositionLine(loc.end, offset, shifted);
18929
+ }
18930
+ /** As {@link stashAndShiftNode} but for a comment (origin stashed on the comment). */
18931
+ function stashAndShiftComment(comment, offset, shifted) {
18932
+ const loc = comment.loc;
18933
+ if (!loc) return;
18934
+ const c = comment;
18935
+ if (c.__roziePartialOrigin === void 0) {
18936
+ const startAlreadyShifted = shifted.has(loc.start);
18937
+ c.__roziePartialOrigin = {
18938
+ line: loc.start.line - (startAlreadyShifted ? offset : 0),
18939
+ column: loc.start.column,
18940
+ filename: loc.filename
18941
+ };
18942
+ }
18943
+ shiftPositionLine(loc.start, offset, shifted);
18944
+ shiftPositionLine(loc.end, offset, shifted);
18945
+ }
18946
+ /** Shift every leading/trailing/inner comment attached to `node`. */
18947
+ function shiftAttachedComments(node, offset, shifted) {
18948
+ for (const c of node.leadingComments ?? []) stashAndShiftComment(c, offset, shifted);
18949
+ for (const c of node.trailingComments ?? []) stashAndShiftComment(c, offset, shifted);
18950
+ for (const c of node.innerComments ?? []) stashAndShiftComment(c, offset, shifted);
18951
+ }
18952
+ /** The line a block's first emitted token occupies: its banner comment if it has
18953
+ * leading comments, else the node itself. */
18954
+ function blockFirstEmitLine(firstNode) {
18955
+ const firstLeading = firstNode.leadingComments?.[0]?.loc;
18956
+ return firstLeading ? firstLeading.start.line : firstNode.loc.start.line;
18957
+ }
18958
+ /**
18959
+ * Phase 56-R10 — true when `stmt` is a sigil DIRECTIVE that every target's residual-body
18960
+ * emit STRIPS (it is consumed into a non-residual section: lifecycle / context / computed /
18961
+ * expose / watch). Mirrors the strip lists in each `emitResidualScriptBody`
18962
+ * (`$onMount`/`$onUnmount`/`$onUpdate`/`$watch`/`$expose`/`$provide` ExpressionStatements,
18963
+ * and `$computed`/`$inject` VariableDeclarations).
18964
+ *
18965
+ * USED ONLY to decide whether a spliced LEADING-comment run's IMMEDIATE source predecessor
18966
+ * survives in the residual body. When that predecessor is STRIPPED (e.g. the real DataTable
18967
+ * `exposeStateVerbs` run sits below a `$provide(...)`), the inline-authored form attaches the
18968
+ * boundary comment to the predecessor's `trailingComments` + the spliced decl's
18969
+ * `leadingComments` (shared object), but per-statement generation drops the predecessor's
18970
+ * trailing copy WITH the stripped statement → the comment SINGLE-emits. The vue/svelte splice
18971
+ * mirror would otherwise re-create that prev-trailing copy and DOUBLE it (the R10 bug). When
18972
+ * the predecessor SURVIVES (a plain `let`/`const`, e.g. `let expandedTouched` above
18973
+ * `groupingActiveDefault`), both copies survive → the inline form DOUBLES and the mirror must
18974
+ * keep doing so. Never throws.
18975
+ */
18976
+ function isStrippedSigilDirective(stmt) {
18977
+ if (_babel_types.isExpressionStatement(stmt) && _babel_types.isCallExpression(stmt.expression)) {
18978
+ const callee = stmt.expression.callee;
18979
+ if (_babel_types.isIdentifier(callee)) return callee.name === "$onMount" || callee.name === "$onUnmount" || callee.name === "$onUpdate" || callee.name === "$watch" || callee.name === "$expose" || callee.name === "$provide";
18980
+ return false;
18981
+ }
18982
+ if (_babel_types.isVariableDeclaration(stmt) && stmt.declarations.length > 0) return stmt.declarations.every((d) => d.init !== null && d.init !== void 0 && _babel_types.isCallExpression(d.init) && _babel_types.isIdentifier(d.init.callee) && (d.init.callee.name === "$computed" || d.init.callee.name === "$inject"));
18983
+ return false;
18984
+ }
18985
+ /**
18986
+ * Measure the ORIGINAL source gap above a decl run's first emit token (D-02, R2).
18987
+ *
18988
+ * Returns the `prevEnd + gap` delta the run should flow at: the distance, in source
18989
+ * lines, from the run's first emit token (its banner comment if any, else its first
18990
+ * node) DOWN from the END of the nearest preceding node IN THE SAME SOURCE FILE — a
18991
+ * same-file freshly-hoisted import (`sameFileHoists`) or the prior same-file decl run's
18992
+ * last node (`prevRunLastNode`). `gap = firstEmitLine − precedingEnd` so a zero-blank
18993
+ * adjacency yields `1` (next line) and N blanks yield `N+1` (no clamp). All `loc`s are
18994
+ * still PARTIAL-LOCAL here (the normalize shift runs later), so this reproduces the
18995
+ * partial's own pre-extraction layout — which, by the extraction rule, equals the host
18996
+ * adjacency the inline oracle has (Pitfall 3: measure source-side, NOT the host seam).
18997
+ *
18998
+ * When the run's first decl has NO same-file predecessor (a file-top nested-partial
18999
+ * decl, e.g. HostD's `inner` at the top of its own file), there is no source delta to
19000
+ * measure, so this falls back to the legacy default `2` — preserving the pre-D-02
19001
+ * behavior for that shape (no regression). Never throws.
19002
+ */
19003
+ function measureOriginalGap(firstNode, sameFileHoists, prevRunLastNode) {
19004
+ const loc = firstNode.loc;
19005
+ if (!loc) return 2;
19006
+ const firstEmitLine = blockFirstEmitLine(firstNode);
19007
+ const fname = loc.filename;
19008
+ let precedingEnd = 0;
19009
+ for (const h of sameFileHoists) {
19010
+ const hl = h.loc;
19011
+ if (hl && hl.filename === fname && hl.end.line < firstEmitLine) precedingEnd = Math.max(precedingEnd, hl.end.line);
19012
+ }
19013
+ const pl = prevRunLastNode?.loc;
19014
+ if (pl && pl.filename === fname && pl.end.line < firstEmitLine) precedingEnd = Math.max(precedingEnd, pl.end.line);
19015
+ if (precedingEnd === 0) return 2;
19016
+ return Math.max(1, firstEmitLine - precedingEnd);
19017
+ }
19018
+ /**
19019
+ * Shift every node + attached comment in a block by `offset`, deduping on the
19020
+ * underlying `Position` objects (see {@link shiftPositionLine}). Never throws.
19021
+ *
19022
+ * WR-01: `shifted` is supplied by the CALLER and SHARED across every block in one
19023
+ * normalize pass. A between-statement comment can be aliased across two blocks —
19024
+ * `@babel/parser` attaches the comment between a hoisted import and the first decl
19025
+ * to BOTH the import's `trailingComments` and the decl's `leadingComments` (the
19026
+ * SAME `Position` objects). When the hoist and that decl run land in separate
19027
+ * blocks, a per-block `shifted` set would shift the aliased comment TWICE (once per
19028
+ * block), corrupting its emit line. A shared set shifts each unique `Position`
19029
+ * exactly once. The adjacent hoist/decl offsets are equal by construction (the
19030
+ * sequential decl anchor is derived from the shifted hoist end + the same gap the
19031
+ * partial's import→decl delta encodes), so first-touch-wins is the correct offset.
19032
+ */
19033
+ function shiftBlock(block, offset, shifted) {
19034
+ for (const top of block.nodes) {
19035
+ stashAndShiftNode(top, offset, shifted);
19036
+ shiftAttachedComments(top, offset, shifted);
19037
+ try {
19038
+ traverse$18(_babel_types.file(_babel_types.program([top])), { enter(path) {
19039
+ stashAndShiftNode(path.node, offset, shifted);
19040
+ shiftAttachedComments(path.node, offset, shifted);
19041
+ } });
19042
+ } catch {}
19043
+ }
19044
+ }
19045
+ /** Shift a single comment's loc lines by `offset` WITHOUT stashing a partial origin. */
19046
+ function shiftCommentLinesOnly(comment, offset, shifted) {
19047
+ const loc = comment.loc;
19048
+ if (!loc) return;
19049
+ shiftPositionLine(loc.start, offset, shifted);
19050
+ shiftPositionLine(loc.end, offset, shifted);
19051
+ }
19052
+ /**
19053
+ * Phase 56-R8 — shift a HOST node's loc + attached/nested comments by `offset`,
19054
+ * deduping on the underlying `Position` objects (shared `shifted` set, like
19055
+ * {@link shiftBlock}). Never throws (D-04).
19056
+ *
19057
+ * Unlike {@link shiftBlock} / {@link stashAndShiftNode}, this does NOT stash a
19058
+ * `__roziePartialOrigin`: a host statement belongs to the HOST `.rozie` file, so its
19059
+ * `loc` is the host source-map anchor and must NOT be re-keyed onto a partial origin
19060
+ * (`buildPartialLineOffsets` is per-FILE first-stash-wins and would mis-offset every
19061
+ * other host segment). This is invoked ONLY for an after-side host run that carries a
19062
+ * GENUINE intended blank below a spliced run (`afterGap >= 2`) — the gap-1 trailing
19063
+ * seam. Such runs never appear in any built source-map gate today (dist-parity compiles
19064
+ * with `sourceMap: false`; the R5 smoke fixtures have no `afterGap >= 2` host run), so
19065
+ * the line shift is invisible to the source-map gates while making the @babel/generator
19066
+ * blank-line delta reproduce the source's after-side blank on vue/svelte/solid. The
19067
+ * host node's residual source-map LINE imperfection for such runs is the same class of
19068
+ * `userCodeLineOffset` limitation already documented in deferred-items.md (#1).
19069
+ */
19070
+ function shiftHostNodeLines(node, offset, shifted) {
19071
+ if (offset === 0) return;
19072
+ const apply = (n) => {
19073
+ if (n.loc) {
19074
+ shiftPositionLine(n.loc.start, offset, shifted);
19075
+ shiftPositionLine(n.loc.end, offset, shifted);
19076
+ }
19077
+ for (const c of n.leadingComments ?? []) shiftCommentLinesOnly(c, offset, shifted);
19078
+ for (const c of n.trailingComments ?? []) shiftCommentLinesOnly(c, offset, shifted);
19079
+ for (const c of n.innerComments ?? []) shiftCommentLinesOnly(c, offset, shifted);
19080
+ };
19081
+ apply(node);
19082
+ try {
19083
+ traverse$18(_babel_types.file(_babel_types.program([node])), { enter(path) {
19084
+ apply(path.node);
19085
+ } });
19086
+ } catch {}
19087
+ }
19088
+ /**
19089
+ * FINAL inline-pass step (Phase 55) — decouple the line `@babel/generator` reads
19090
+ * for blank-line/comment placement from the line it reads for the source-map
19091
+ * origin, for every spliced partial node.
19092
+ *
19093
+ * Rationale (RESEARCH Key Finding 1): under `retainLines:false` the generator's
19094
+ * comment-adjacency + blank-line math reads `node.loc.start.line` and
19095
+ * `comment.loc.start.line` only as DELTAS; only the host↔partial (and
19096
+ * partial↔partial) BOUNDARY deltas are wrong (a spliced node carries a small
19097
+ * `.rzts`-local line discontinuous with its host neighbour). A CONSTANT per-block
19098
+ * offset preserves each block's intra deltas; the right boundary delta is restored
19099
+ * by anchoring each block SEQUENTIALLY in residual-body order.
19100
+ *
19101
+ * SEQUENTIAL ANCHOR (Phase 55-04): each block's first emitted line (its banner
19102
+ * comment, else its first node) is anchored ONE blank line below the PRECEDING
19103
+ * statement in the final body. Anchoring every block at its own replaced import
19104
+ * line (Plan 02's approach) collapsed multi-partial hosts: three consecutive
19105
+ * mid-body imports made expand/group/facet pile onto adjacent host lines, so each
19106
+ * 30-45-line block overlapped the next and the partial↔partial comment deltas went
19107
+ * negative (`}; // banner` collapse). Flowing the blocks sequentially reproduces the
19108
+ * inline-authored contiguous layout. The first block (or any block preceded only by
19109
+ * un-located content) falls back to its replaced import's host line.
19110
+ *
19111
+ * WR-01 (whole-program byte-identity): the walk runs over the FULL emit body
19112
+ * (`[...hoistImports, ...residualBody]`), NOT just the residual body, so a
19113
+ * freshly-hoisted import flows in true emit order ahead of the decls. Consecutive
19114
+ * IMPORT blocks anchor CONTIGUOUSLY (gap 1 — no blank between imports); every other
19115
+ * run anchors one blank line below the preceding statement (gap 2). Because each
19116
+ * source-file decl run is now its OWN block (see {@link SplicedEmitBlock}), a
19117
+ * nested-partial decl run flows one blank line below the parent decl run rather than
19118
+ * inheriting the hoist's incommensurate file-top offset.
19119
+ *
19120
+ * TWO PASSES (WR-01): a between-statement comment is aliased across blocks — the
19121
+ * `Position` that is a hoisted import's `trailingComments[i]` is the SAME object as
19122
+ * the next decl's `leadingComments[i]`. If offsets were applied while walking, the
19123
+ * hoist block would shift that comment, and the decl block's `blockFirstEmitLine`
19124
+ * would then read the ALREADY-SHIFTED comment line and derive a wrong offset
19125
+ * (collapsing the comment onto the decl). So PASS 1 MEASURES every block's offset
19126
+ * from ORIGINAL (unmutated) lines, tracking the running emit end arithmetically;
19127
+ * PASS 2 MUTATES, applying every offset with ONE shared dedup set so each aliased
19128
+ * `Position` shifts exactly once (adjacent hoist/decl offsets are equal by
19129
+ * construction, so first-touch-wins is the correct line).
19130
+ *
19131
+ * Runs AFTER all diagnostics are collected (they captured true `.rzts` byte loc via
19132
+ * `nodeLoc`, which reads `node.start`/`node.end`, NOT `loc.{line,column}`), so the
19133
+ * R7 error-frame path is untouched (Pitfall 1). Never throws (D-04).
19134
+ */
19135
+ function normalizeSplicedEmitLines(body, blocks) {
19136
+ const nodeToBlock = /* @__PURE__ */ new Map();
19137
+ for (const b of blocks) for (const n of b.nodes) nodeToBlock.set(n, b);
19138
+ const measured = /* @__PURE__ */ new Set();
19139
+ const plan = [];
19140
+ let prevEnd = 0;
19141
+ let prevWasImport = false;
19142
+ let prevWasBlock = false;
19143
+ let prevBlockAnchorLine = 0;
19144
+ let hostRunOffset = 0;
19145
+ let seamAfterGap;
19146
+ let prevWasHostStmt = false;
19147
+ let prevHostOrigEnd = 0;
19148
+ let prevHostStmt = null;
19149
+ for (const stmt of body) {
19150
+ const block = nodeToBlock.get(stmt);
19151
+ if (block) {
19152
+ if (measured.has(block)) continue;
19153
+ measured.add(block);
19154
+ const firstNode = block.nodes.find((n) => n.loc);
19155
+ if (!firstNode?.loc) continue;
19156
+ const isImportBlock = _babel_types.isImportDeclaration(block.nodes[0]);
19157
+ let gap = isImportBlock && prevWasImport ? 1 : block.originalGap;
19158
+ let stampLeadingSeamStripped = false;
19159
+ if (!isImportBlock && prevWasHostStmt) {
19160
+ const beforeGap = block.anchorLine - prevHostOrigEnd;
19161
+ if (blockFirstEmitLine(firstNode) < firstNode.loc.start.line) {
19162
+ if (beforeGap >= 1 && beforeGap < gap) gap = beforeGap;
19163
+ if (prevHostStmt && isStrippedSigilDirective(prevHostStmt)) stampLeadingSeamStripped = true;
19164
+ }
19165
+ }
19166
+ const offset = (prevEnd > 0 ? prevEnd + gap : block.anchorLine) - blockFirstEmitLine(firstNode);
19167
+ let maxEnd = 0;
19168
+ for (const n of block.nodes) if (n.loc) maxEnd = Math.max(maxEnd, n.loc.end.line);
19169
+ prevEnd = maxEnd + offset;
19170
+ prevWasImport = isImportBlock;
19171
+ prevWasBlock = true;
19172
+ prevWasHostStmt = false;
19173
+ prevHostStmt = null;
19174
+ prevBlockAnchorLine = block.anchorLine;
19175
+ plan.push(stampLeadingSeamStripped ? {
19176
+ block,
19177
+ offset,
19178
+ leadingSeamPrevStripped: true
19179
+ } : {
19180
+ block,
19181
+ offset
19182
+ });
19183
+ continue;
19184
+ }
19185
+ if (stmt.loc) {
19186
+ const firstEmit = blockFirstEmitLine(stmt);
19187
+ let offset;
19188
+ if (prevWasBlock) {
19189
+ const afterGap = firstEmit - prevBlockAnchorLine;
19190
+ if (afterGap >= 2) {
19191
+ offset = prevEnd + afterGap - firstEmit;
19192
+ seamAfterGap = afterGap;
19193
+ } else offset = 0;
19194
+ hostRunOffset = offset;
19195
+ } else offset = hostRunOffset;
19196
+ if (offset !== 0) plan.push(seamAfterGap !== void 0 ? {
19197
+ hostNode: stmt,
19198
+ offset,
19199
+ afterGap: seamAfterGap
19200
+ } : {
19201
+ hostNode: stmt,
19202
+ offset
19203
+ });
19204
+ prevEnd = stmt.loc.end.line + offset;
19205
+ seamAfterGap = void 0;
19206
+ prevWasImport = _babel_types.isImportDeclaration(stmt);
19207
+ prevWasBlock = false;
19208
+ prevWasHostStmt = true;
19209
+ prevHostOrigEnd = stmt.loc.end.line;
19210
+ prevHostStmt = stmt;
19211
+ }
19212
+ }
19213
+ for (const block of blocks) {
19214
+ if (measured.has(block)) continue;
19215
+ measured.add(block);
19216
+ const firstNode = block.nodes.find((n) => n.loc);
19217
+ if (!firstNode?.loc) continue;
19218
+ plan.push({
19219
+ block,
19220
+ offset: block.anchorLine - blockFirstEmitLine(firstNode)
19221
+ });
19222
+ }
19223
+ const shifted = /* @__PURE__ */ new Set();
19224
+ for (const entry of plan) if ("block" in entry) {
19225
+ shiftBlock(entry.block, entry.offset, shifted);
19226
+ if (entry.leadingSeamPrevStripped) {
19227
+ const firstNode = entry.block.nodes.find((n) => n.loc);
19228
+ if (firstNode) firstNode.extra = {
19229
+ ...firstNode.extra ?? {},
19230
+ __rozieLeadingSeamPrevStripped: true
19231
+ };
19232
+ }
19233
+ } else {
19234
+ shiftHostNodeLines(entry.hostNode, entry.offset, shifted);
19235
+ if (entry.afterGap !== void 0) {
19236
+ const extra = entry.hostNode.extra ?? {};
19237
+ entry.hostNode.extra = {
19238
+ ...extra,
19239
+ __rozieAfterGap: entry.afterGap
19240
+ };
19241
+ }
19242
+ }
19243
+ }
19244
+ /**
19245
+ * Phase 56 (Shape-3, R4) — un-FLOAT a hoisted import's between-statement comment that
19246
+ * has separated from its owning declaration.
19247
+ *
19248
+ * `hoistSpecifier` copies a partial import's `trailingComments` onto the HOISTED import
19249
+ * node so a comment authored BETWEEN that import and the next surviving decl rides the
19250
+ * import to the host module-top (Phase 55 byte-identity). @babel/parser attaches such a
19251
+ * between-statement comment to BOTH neighbours: the import's `trailingComments` AND the
19252
+ * following decl's `leadingComments` (the SAME comment object). That copy is CORRECT
19253
+ * only when the import and its decl stay ADJACENT in the final body (e.g. HostC: the
19254
+ * hoisted `clamp` import is immediately followed by the `double` decl it shares the
19255
+ * comment with — the inline oracle ALSO has the comment on both neighbours, so
19256
+ * per-statement targets double it identically).
19257
+ *
19258
+ * When the spliced decl lands NON-adjacent to its hoisted import — a host statement
19259
+ * (e.g. a reassigned module-`let`) sits between them (HostH/the DataTable P15
19260
+ * `editTransition` after-`let` seam) — the import's copy FLOATS the comment to
19261
+ * module-top, away from the decl. The inline oracle keeps the comment ONLY on the decl
19262
+ * (its import is authored far above, never adjacent), so the float is a partial-vs-inline
19263
+ * divergence on ALL six targets (svelte/vue double it at the wrong place, react/angular/
19264
+ * lit drop it, solid dedups). This pass restores the inline placement by STRIPPING the
19265
+ * floated comment from the import's `trailingComments`; the decl keeps it on its
19266
+ * `leadingComments` (object identity preserved), and the per-target emitters then handle
19267
+ * the decl-attached comment exactly as they do for the inline oracle (svelte/vue's
19268
+ * `mirrorSpliceBoundaryComments` restores the doubling at the decl seam).
19269
+ *
19270
+ * Runs BEFORE `normalizeSplicedEmitLines` so the corrected attachment is in place when
19271
+ * the emit-line shift runs. A comment is treated as FLOATED iff it is shared (object
19272
+ * identity) with some body node's `leadingComments` whose owner is NOT the import's
19273
+ * immediate body-successor; a genuine import-only trailing comment (owned by no decl's
19274
+ * leadingComments) and the adjacent-decl case (HostC) are both left untouched. Never
19275
+ * throws (D-04).
19276
+ */
19277
+ function defloatHoistedImportComments(body, hoistImports) {
19278
+ if (hoistImports.length === 0) return;
19279
+ const hoistSet = new Set(hoistImports);
19280
+ const leadOwnerIndex = /* @__PURE__ */ new Map();
19281
+ for (let i = 0; i < body.length; i++) for (const c of body[i].leadingComments ?? []) if (!leadOwnerIndex.has(c)) leadOwnerIndex.set(c, i);
19282
+ for (let hi = 0; hi < body.length; hi++) {
19283
+ const node = body[hi];
19284
+ if (!hoistSet.has(node)) continue;
19285
+ const trailing = node.trailingComments;
19286
+ if (!trailing || trailing.length === 0) continue;
19287
+ const kept = trailing.filter((c) => {
19288
+ const owner = leadOwnerIndex.get(c);
19289
+ if (owner === void 0) return true;
19290
+ if (owner === hi + 1) return true;
19291
+ return false;
19292
+ });
19293
+ if (kept.length !== trailing.length) node.trailingComments = kept.length > 0 ? kept : null;
19294
+ }
19295
+ }
18780
19296
  /** Build the named imported-name list for a (host or nested) partial import. */
18781
19297
  function namedImports(imp) {
18782
19298
  const out = [];
@@ -18839,6 +19355,7 @@ function inlineResolvedPartial(absPath, importedNames, importStmt, importerFile,
18839
19355
  const nestedImports = [];
18840
19356
  /** local name -> nested partial resolved path + the imported name. */
18841
19357
  const nestedLocalMap = /* @__PURE__ */ new Map();
19358
+ const reExports = [];
18842
19359
  let order = 0;
18843
19360
  for (const stmt of partialBody) {
18844
19361
  if (_babel_types.isImportDeclaration(stmt)) {
@@ -18864,9 +19381,47 @@ function inlineResolvedPartial(absPath, importedNames, importStmt, importerFile,
18864
19381
  } else moduleImports.push(stmt);
18865
19382
  continue;
18866
19383
  }
19384
+ if (_babel_types.isExportNamedDeclaration(stmt) && !stmt.declaration && stmt.source) {
19385
+ const src = stmt.source;
19386
+ const declTypeOnly = stmt.exportKind === "type";
19387
+ for (const spec of stmt.specifiers) if (_babel_types.isExportSpecifier(spec)) {
19388
+ const exportedName = _babel_types.isIdentifier(spec.exported) ? spec.exported.name : spec.exported.value;
19389
+ const kind = declTypeOnly || spec.exportKind === "type" ? "type" : "value";
19390
+ const localId = spec.local;
19391
+ reExports.push({
19392
+ exportedName,
19393
+ source: src,
19394
+ kind,
19395
+ build: () => _babel_types.importSpecifier(_babel_types.identifier(exportedName), _babel_types.identifier(localId.name))
19396
+ });
19397
+ } else if (_babel_types.isExportNamespaceSpecifier(spec)) {
19398
+ const nsName = spec.exported.name;
19399
+ reExports.push({
19400
+ exportedName: nsName,
19401
+ source: src,
19402
+ kind: declTypeOnly ? "type" : "value",
19403
+ build: () => _babel_types.importNamespaceSpecifier(_babel_types.identifier(nsName))
19404
+ });
19405
+ }
19406
+ continue;
19407
+ }
19408
+ if (_babel_types.isExportAllDeclaration(stmt)) {
19409
+ ctx.diagnostics.push({
19410
+ code: RozieErrorCode.PARTIAL_UNSUPPORTED_IMPORT_FORM,
19411
+ severity: "error",
19412
+ message: `Script partial '${absPath}' uses \`export * from '${stmt.source.value}'\`. A star re-export has no statically-known named surface to inline — a partial is a compile-time inline, so each re-exported symbol must be named (e.g. \`export { foo } from '${stmt.source.value}'\`).`,
19413
+ loc: nodeLoc(stmt),
19414
+ ...absPath ? { filename: absPath } : {},
19415
+ hint: `Replace the star re-export with explicit named re-exports: export { foo, bar } from '${stmt.source.value}'.`
19416
+ });
19417
+ continue;
19418
+ }
18867
19419
  let bare = null;
18868
- if (_babel_types.isExportNamedDeclaration(stmt) && stmt.declaration) bare = stmt.declaration;
18869
- else if (_babel_types.isVariableDeclaration(stmt) || _babel_types.isFunctionDeclaration(stmt) || _babel_types.isClassDeclaration(stmt) || _babel_types.isTSInterfaceDeclaration(stmt) || _babel_types.isTSTypeAliasDeclaration(stmt) || _babel_types.isTSEnumDeclaration(stmt) || _babel_types.isTSDeclareFunction(stmt)) bare = stmt;
19420
+ if (_babel_types.isExportNamedDeclaration(stmt) && stmt.declaration) {
19421
+ bare = stmt.declaration;
19422
+ const prevStmt = partialBody[partialBody.indexOf(stmt) - 1];
19423
+ if ((prevStmt && _babel_types.isImportDeclaration(prevStmt) && !PARTIAL_EXT.test(prevStmt.source.value) && prevStmt.trailingComments && stmt.leadingComments ? stmt.leadingComments.some((c) => prevStmt.trailingComments.includes(c)) : false) && stmt.leadingComments && stmt.leadingComments.length > 0 && (!bare.leadingComments || bare.leadingComments.length === 0)) bare.leadingComments = stmt.leadingComments;
19424
+ } else if (_babel_types.isVariableDeclaration(stmt) || _babel_types.isFunctionDeclaration(stmt) || _babel_types.isClassDeclaration(stmt) || _babel_types.isTSInterfaceDeclaration(stmt) || _babel_types.isTSTypeAliasDeclaration(stmt) || _babel_types.isTSEnumDeclaration(stmt) || _babel_types.isTSDeclareFunction(stmt)) bare = stmt;
18870
19425
  if (!bare) continue;
18871
19426
  const names = bindingNames(bare);
18872
19427
  if (names.length === 0) continue;
@@ -18919,8 +19474,9 @@ function inlineResolvedPartial(absPath, importedNames, importStmt, importerFile,
18919
19474
  }
18920
19475
  for (const imp of moduleImports) {
18921
19476
  const declKind = imp.importKind ?? "value";
18922
- for (const spec of imp.specifiers) if (referencedAll.has(spec.local.name)) hoistSpecifier(ctx, imp.source, declKind, spec);
19477
+ for (const spec of imp.specifiers) if (referencedAll.has(spec.local.name)) hoistSpecifier(ctx, imp.source, declKind, spec, imp);
18923
19478
  }
19479
+ for (const re of reExports) if (importedNames.includes(re.exportedName) || referencedAll.has(re.exportedName)) hoistSpecifier(ctx, re.source, re.kind, re.build());
18924
19480
  const out = [];
18925
19481
  for (const decl of includedSorted) {
18926
19482
  const collidingNames = [];
@@ -19010,6 +19566,7 @@ function inlineScriptPartials(file, opts = {}) {
19010
19566
  }
19011
19567
  }
19012
19568
  const splicedAbs = /* @__PURE__ */ new Set();
19569
+ const splicedBlocks = [];
19013
19570
  const newBody = [];
19014
19571
  for (const stmt of file.program.body) if (_babel_types.isImportDeclaration(stmt) && PARTIAL_EXT.test(stmt.source.value)) {
19015
19572
  const absPath = hostPartialAbs.get(stmt) ?? null;
@@ -19038,9 +19595,38 @@ function inlineScriptPartials(file, opts = {}) {
19038
19595
  if (splicedAbs.has(absPath)) continue;
19039
19596
  splicedAbs.add(absPath);
19040
19597
  const unionNames = hostPartialNames.get(absPath) ?? namedImports(stmt);
19041
- newBody.push(...inlineResolvedPartial(absPath, unionNames, stmt, fromFile, ctx));
19598
+ const hoistBefore = ctx.hoistImports.length;
19599
+ const spliced = inlineResolvedPartial(absPath, unionNames, stmt, fromFile, ctx);
19600
+ const newHoists = ctx.hoistImports.slice(hoistBefore);
19601
+ if (stmt.loc) {
19602
+ const anchorLine = stmt.loc.start.line;
19603
+ for (const hoist of newHoists) splicedBlocks.push({
19604
+ nodes: [hoist],
19605
+ anchorLine,
19606
+ originalGap: 1
19607
+ });
19608
+ let runStart = 0;
19609
+ let prevRunLastNode = null;
19610
+ while (runStart < spliced.length) {
19611
+ const fname = spliced[runStart].loc?.filename ?? null;
19612
+ let runEnd = runStart + 1;
19613
+ while (runEnd < spliced.length && (spliced[runEnd].loc?.filename ?? null) === fname) runEnd++;
19614
+ const nodes = spliced.slice(runStart, runEnd);
19615
+ const originalGap = measureOriginalGap(nodes.find((n) => n.loc) ?? nodes[0], newHoists, prevRunLastNode);
19616
+ splicedBlocks.push({
19617
+ nodes,
19618
+ anchorLine,
19619
+ originalGap
19620
+ });
19621
+ prevRunLastNode = nodes[nodes.length - 1] ?? prevRunLastNode;
19622
+ runStart = runEnd;
19623
+ }
19624
+ }
19625
+ newBody.push(...spliced);
19042
19626
  } else newBody.push(stmt);
19043
19627
  file.program.body = [...ctx.hoistImports, ...newBody];
19628
+ defloatHoistedImportComments(file.program.body, ctx.hoistImports);
19629
+ normalizeSplicedEmitLines(file.program.body, splicedBlocks);
19044
19630
  return {
19045
19631
  ast: file,
19046
19632
  diagnostics
@@ -21347,6 +21933,27 @@ function rewriteRozieIdentifiers$4(program, ir, diagnostics) {
21347
21933
  loc: ref.sourceLoc
21348
21934
  });
21349
21935
  normalizeModelAccessor$4(program);
21936
+ const vueProtected = new Set((ir.expose ?? []).map((e) => e.name));
21937
+ deconflictGeneratedSymbols(program, [
21938
+ {
21939
+ names: modelProps,
21940
+ trigger: {
21941
+ kind: "accessor",
21942
+ accessor: "$props"
21943
+ }
21944
+ },
21945
+ {
21946
+ names: dataNames,
21947
+ trigger: {
21948
+ kind: "accessor",
21949
+ accessor: "$data"
21950
+ }
21951
+ },
21952
+ {
21953
+ names: computedNames,
21954
+ trigger: { kind: "bare-read" }
21955
+ }
21956
+ ], vueProtected);
21350
21957
  traverse$17(program, {
21351
21958
  MemberExpression(path) {
21352
21959
  /* v8 ignore next -- defensive: MemberExpression nodes do not occur in TS type position */
@@ -21867,6 +22474,80 @@ function arrowBody$4(body) {
21867
22474
  return genCode$9(_babel_types.arrowFunctionExpression([], body));
21868
22475
  }
21869
22476
  /**
22477
+ * Phase 55-04 (literal byte-identity) — reproduce the inline-authored comment
22478
+ * doubling at a script-partial splice boundary.
22479
+ *
22480
+ * In an inline-authored `<script>`, a comment block BETWEEN two statements is
22481
+ * attached by `@babel/parser` to BOTH neighbours (the earlier statement's
22482
+ * `trailingComments` AND the later statement's `leadingComments`). The `.rzts`
22483
+ * script-partial splice attaches the boundary banner ONLY to the spliced node's
22484
+ * `leadingComments` — the preceding statement lives in a different source file and
22485
+ * carries no matching trailing comment. Vue emits the residual body one statement
22486
+ * at a time (`stmts.map((s) => genCode(s)).join('\n')`), so each `genCode` call has
22487
+ * its own comment-dedup set: in the inline form the boundary banner therefore
22488
+ * prints TWICE (once as the previous statement's trailing, once as the next
22489
+ * statement's leading), with a blank line after the previous closing brace.
22490
+ * Re-mirroring the spliced node's leading comments back onto the preceding
22491
+ * statement's trailing comments restores that byte-for-byte.
22492
+ *
22493
+ * Fires at a genuine splice boundary in EITHER direction (Phase 56 R1 broadened
22494
+ * the trigger): the CURRENT statement is spliced (`cur.extra.__roziePartialOrigin`
22495
+ * — the Phase 55 leading seam) OR the PREVIOUS statement is spliced and CUR is an
22496
+ * inline host successor carrying the leading comment (the R1 TRAILING seam). In
22497
+ * both cases CUR's leading comments are mirrored onto PREV's trailing comments
22498
+ * UNLESS already shared (within-partial statement pairs share the same comment
22499
+ * objects; host-only pairs — neither node spliced — are left exactly as authored).
22500
+ * `normalizeSplicedEmitLines` (core) has already anchored the seam spacing, so the
22501
+ * mirrored trailing copy spaces correctly.
22502
+ */
22503
+ function mirrorSpliceBoundaryComments$2(stmts) {
22504
+ for (let i = 1; i < stmts.length; i++) {
22505
+ const cur = stmts[i];
22506
+ const prev = stmts[i - 1];
22507
+ const curExtra = cur.extra;
22508
+ const prevExtra = prev.extra;
22509
+ const curSpliced = curExtra?.__roziePartialOrigin !== void 0;
22510
+ const prevSpliced = prevExtra?.__roziePartialOrigin !== void 0;
22511
+ if (!curSpliced && !prevSpliced) continue;
22512
+ const lead = cur.leadingComments;
22513
+ const prevTrail = prev.trailingComments;
22514
+ if (curSpliced && !prevSpliced && (!lead || lead.length === 0) && prevTrail && prevTrail.length > 0) {
22515
+ cur.leadingComments = [...prevTrail];
22516
+ continue;
22517
+ }
22518
+ if (!lead || lead.length === 0) continue;
22519
+ if (curSpliced && curExtra?.__rozieLeadingSeamPrevStripped === true) continue;
22520
+ const lastLead = lead[lead.length - 1];
22521
+ if (prevTrail && prevTrail.length > 0 && prevTrail[prevTrail.length - 1] === lastLead) continue;
22522
+ let toAppend = lead;
22523
+ if (prevSpliced && !curSpliced) {
22524
+ const afterGap = curExtra?.__rozieAfterGap;
22525
+ const anchorLine = (prev.loc?.end.line ?? 0) + (typeof afterGap === "number" ? afterGap : 1);
22526
+ const baseLine = lead[0]?.loc?.start.line;
22527
+ toAppend = lead.map((c) => {
22528
+ if (!c.loc) return { ...c };
22529
+ const startLine = baseLine === void 0 ? anchorLine : anchorLine + (c.loc.start.line - baseLine);
22530
+ const endLine = baseLine === void 0 ? anchorLine : anchorLine + (c.loc.end.line - baseLine);
22531
+ return {
22532
+ ...c,
22533
+ loc: {
22534
+ ...c.loc,
22535
+ start: {
22536
+ ...c.loc.start,
22537
+ line: startLine
22538
+ },
22539
+ end: {
22540
+ ...c.loc.end,
22541
+ line: endLine
22542
+ }
22543
+ }
22544
+ };
22545
+ });
22546
+ }
22547
+ prev.trailingComments = [...prevTrail ?? [], ...toAppend];
22548
+ }
22549
+ }
22550
+ /**
21870
22551
  * Render a PropTypeAnnotation as a TypeScript type string.
21871
22552
  *
21872
22553
  * Reference examples produce these patterns:
@@ -22321,6 +23002,7 @@ function emitResidualScriptBody$1(clonedProgram, consumedLifecycleIndices) {
22321
23002
  }
22322
23003
  stmts.push(stmt);
22323
23004
  }
23005
+ mirrorSpliceBoundaryComments$2(stmts);
22324
23006
  return {
22325
23007
  code: stmts.map((s) => genCode$9(s)).join("\n"),
22326
23008
  stmts
@@ -24962,7 +25644,8 @@ function remapping$1(input, loader, options) {
24962
25644
  * (the "parent" map) via @ampproject/remapping. Result: a single Source Map
24963
25645
  * v3 that resolves emitted-output positions all the way back to .rozie.
24964
25646
  *
24965
- * Used by all 4 target compose.ts files. Replaces the per-target
25647
+ * Used by all 6 target compose.ts files (react/vue/svelte/solid/lit/angular).
25648
+ * Replaces the per-target
24966
25649
  * single-segment re-projection hack (Phase 3 WR-01 / Phase 4 Plan 04-05
24967
25650
  * Task 1) removed in P2 (D-109).
24968
25651
  *
@@ -24975,7 +25658,114 @@ function remapping$1(input, loader, options) {
24975
25658
  * @experimental — shape may change before v1.0
24976
25659
  */
24977
25660
  const remapping = typeof remapping$1 === "function" ? remapping$1 : remapping$1.default;
25661
+ /** Source-file extension test: a spliced script partial origin (Phase 54/55). */
25662
+ function isPartialSource(src) {
25663
+ return !!src && (src.endsWith(".rzts") || src.endsWith(".rzjs"));
25664
+ }
25665
+ /**
25666
+ * Recover a partial source's constant emit-line offset from the table. The table
25667
+ * is keyed by the absolute `loc.filename` stashed at splice time; a child map's
25668
+ * `sources` entry is the same absolute path, so an EXACT lookup is tried first.
25669
+ *
25670
+ * WR-03: the defensive fallback is restricted to a path-SEGMENT-BOUNDARY match
25671
+ * (`src.endsWith('/' + file) || file.endsWith('/' + src)`) so a partial whose path
25672
+ * is a bare substring of another's (e.g. `xa.rzts` vs `a.rzts`) never collides, and
25673
+ * — critically — if TWO OR MORE table entries match the boundary test (an ambiguous
25674
+ * bare basename shared across nested dirs) the lookup BAILS (returns `undefined`)
25675
+ * rather than mis-attributing one partial's offset to another. Returns `undefined`
25676
+ * when no unambiguous offset is known (mapping left as-is — D-04).
25677
+ */
25678
+ function lookupPartialOffset(src, offsets) {
25679
+ if (!src || !offsets || offsets.size === 0) return void 0;
25680
+ const exact = offsets.get(src);
25681
+ if (exact !== void 0) return exact;
25682
+ let match;
25683
+ let matchCount = 0;
25684
+ for (const [file, off] of offsets) if (src.endsWith(`/${file}`) || file.endsWith(`/${src}`)) {
25685
+ match = off;
25686
+ matchCount++;
25687
+ }
25688
+ return matchCount === 1 ? match : void 0;
25689
+ }
25690
+ /**
25691
+ * Phase 55 Plan 03 (SC-2) — build the per-partial-file constant emit-line offset
25692
+ * table from the lowered script AST.
25693
+ *
25694
+ * Plan 02 shifted each spliced node's `loc.start.line` to a host-contiguous emit
25695
+ * value while stashing the true `.rzts` origin on `extra.__roziePartialOrigin`.
25696
+ * The per-partial offset is therefore the (constant) delta
25697
+ * `loc.start.line − __roziePartialOrigin.line`; subtracting it from a mapping's
25698
+ * original line restores the `.rzts`-local line. The offset is constant per
25699
+ * spliced block (Plan 02 anchored it), so one entry per partial `source` file
25700
+ * suffices — the first stashed node per file wins.
25701
+ *
25702
+ * Walks `scriptAst.program.body` (the spliced statements sit at top level) and
25703
+ * their attached leading/trailing/inner comments (comments carry the stash
25704
+ * directly). Never throws (D-04): a missing/zero stash is skipped, a null AST
25705
+ * yields an empty table.
25706
+ *
25707
+ * IN-02: the `innerComments` loop mirrors `shiftAttachedComments`
25708
+ * (inlineScriptPartials.ts) which walks leading/trailing/inner. In practice the
25709
+ * offset is per-file and first-stash-wins, so a leading/trailing comment or the
25710
+ * decl itself almost always supplies it — the inner loop is additive and harmless,
25711
+ * kept only so the stash-channel scan is symmetric with where stashes are written.
25712
+ */
25713
+ function buildPartialLineOffsets(scriptAst) {
25714
+ const out = /* @__PURE__ */ new Map();
25715
+ const body = scriptAst?.program?.body;
25716
+ if (!body) return out;
25717
+ const record = (carrier, locLine) => {
25718
+ const origin = carrier?.__roziePartialOrigin;
25719
+ if (!origin || locLine === void 0) return;
25720
+ const key = origin.filename;
25721
+ if (!key || out.has(key)) return;
25722
+ out.set(key, locLine - origin.line);
25723
+ };
25724
+ for (const node of body) {
25725
+ const extra = node.extra;
25726
+ record(extra, node.loc?.start.line);
25727
+ for (const c of node.leadingComments ?? []) record(c, c.loc?.start.line);
25728
+ for (const c of node.trailingComments ?? []) record(c, c.loc?.start.line);
25729
+ for (const c of node.innerComments ?? []) record(c, c.loc?.start.line);
25730
+ }
25731
+ return out;
25732
+ }
24978
25733
  /**
25734
+ * Phase 55 Plan 03 — per-target script-map flow survey (Task 1, Assumption A3).
25735
+ *
25736
+ * SURVEY RESULT (confirmed on disk 2026-06-20):
25737
+ *
25738
+ * 1. CONVERGENCE — all SIX per-target `sourcemap/compose.ts` wrappers
25739
+ * (react/vue/svelte/solid/lit/angular) import THIS `composeMaps` and route
25740
+ * their @babel/generator <script> child map through it as `children[0]`. No
25741
+ * target hand-rolls a second map merge; `composeMaps` is the single
25742
+ * convergence point, so the spliced-line restore arithmetic lives here ONCE
25743
+ * (D-03/D-05 uniformity) — never in a per-target wrapper or `emitScript.ts`.
25744
+ *
25745
+ * 2. ACTIVE MAP PATH — the five map-EMITTING targets (react/vue/svelte/solid/
25746
+ * angular) all pass a defined `userCodeLineOffset`, so each takes the
25747
+ * `userCodeLineOffset` branch below (step 3). Empirically, that branch
25748
+ * discarded the child map's per-node `sources` (it hardcoded `[opts.filename]`),
25749
+ * collapsing a spliced node's `.rzts` origin to the host `.rozie` — which is
25750
+ * exactly why the SC-2 line-fidelity smoke test was red/skipped. The restore
25751
+ * therefore lives in step 3. The step-4 remapping path is NOT exercised by
25752
+ * partials (every map-emitting target passes `userCodeLineOffset`), so it is
25753
+ * deliberately left untouched rather than carrying speculative dead code.
25754
+ *
25755
+ * 3. TABLE BUILD SITE — each of those five `emitXxx.ts` holds the lowered `ir`
25756
+ * (whose `ir.setupBody.scriptProgram` script AST carries the spliced nodes'
25757
+ * `extra.__roziePartialOrigin` stashes from Plan 02). Each builds the
25758
+ * `partialLineOffsets` table via {@link buildPartialLineOffsets} and threads
25759
+ * it into the `ComposeOpts` it already constructs. The table derives from the
25760
+ * IR, NOT from generator output — so NO `emitScript.ts` (the @babel/generator
25761
+ * caller) is touched (D-03).
25762
+ *
25763
+ * 4. LIT EXCEPTION — `packages/targets/lit/src/emitLit.ts` returns `map: null`
25764
+ * in v1 and does NOT call `composeSourceMap` (the wrapper is implemented but
25765
+ * dead until Phase 7 wires it). Lit therefore has no build+set site; its
25766
+ * wrapper still receives the `partialLineOffsets` field for forward-compat so
25767
+ * the restore flows automatically once Lit's map path is connected.
25768
+ *
24979
25769
  * Merge the shell map + zero-or-more child maps into a single Source Map v3
24980
25770
  * anchored to .rozie. Defensively re-asserts sources/sourcesContent per
24981
25771
  * Pitfall 2 mitigation.
@@ -24994,6 +25784,27 @@ function composeMaps(opts) {
24994
25784
  }
24995
25785
  if (opts.userCodeLineOffset !== void 0) {
24996
25786
  const childMap = opts.children[0].map;
25787
+ const childSources = childMap.sources ?? [];
25788
+ if (childSources.some((s) => isPartialSource(s)) && !!opts.partialLineOffsets && opts.partialLineOffsets.size > 0) {
25789
+ const decoded = decode(childMap.mappings);
25790
+ for (const line of decoded) for (const seg of line) if (seg.length >= 4) {
25791
+ const full = seg;
25792
+ const src = childSources[full[1]];
25793
+ const off = lookupPartialOffset(src, opts.partialLineOffsets);
25794
+ if (off !== void 0) full[2] = Math.max(0, full[2] - off);
25795
+ }
25796
+ const restoredMappings = encode(decoded);
25797
+ const sources = childSources.slice();
25798
+ const sourcesContent = sources.map((s, i) => s === opts.filename ? opts.source : childMap.sourcesContent?.[i] ?? null);
25799
+ return {
25800
+ version: 3,
25801
+ file: `${opts.filename}${opts.fileExt}`,
25802
+ sources,
25803
+ sourcesContent,
25804
+ names: childMap.names ?? [],
25805
+ mappings: ";".repeat(opts.userCodeLineOffset) + restoredMappings
25806
+ };
25807
+ }
24997
25808
  return {
24998
25809
  version: 3,
24999
25810
  file: `${opts.filename}${opts.fileExt}`,
@@ -25027,7 +25838,8 @@ function composeSourceMap$4(ms, opts) {
25027
25838
  outputOffset: opts.scriptOutputOffset
25028
25839
  }] : [],
25029
25840
  fileExt: ".vue",
25030
- userCodeLineOffset: opts.userCodeLineOffset
25841
+ userCodeLineOffset: opts.userCodeLineOffset,
25842
+ partialLineOffsets: opts.partialLineOffsets
25031
25843
  });
25032
25844
  }
25033
25845
  //#endregion
@@ -25212,7 +26024,8 @@ function emitVue(ir, opts = {}) {
25212
26024
  source: opts.source,
25213
26025
  scriptMap: shellScriptMap,
25214
26026
  scriptOutputOffset,
25215
- userCodeLineOffset
26027
+ userCodeLineOffset,
26028
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
25216
26029
  }) : null,
25217
26030
  diagnostics: [
25218
26031
  ...scriptDiags,
@@ -29153,7 +29966,8 @@ function composeClassName(attrs, ctx) {
29153
29966
  });
29154
29967
  else segments.push({
29155
29968
  kind: "plainBinding",
29156
- expr: a.expression
29969
+ expr: a.expression,
29970
+ wrapForDisplay: a.wrapForDisplay
29157
29971
  });
29158
29972
  else if (a.kind === "spreadBinding") throw new Error(`React target: spreadBinding not valid in class array context (Phase 14).`);
29159
29973
  else segments.push({
@@ -29175,6 +29989,10 @@ function composeClassName(attrs, ctx) {
29175
29989
  const seg = segments[0];
29176
29990
  const staticSegs = decomposeStaticClassExpr(seg.expr);
29177
29991
  if (staticSegs) return renderInterpolatedClass(staticSegs, ctx);
29992
+ if (seg.wrapForDisplay) {
29993
+ ctx.collectors.runtime.add("clsx");
29994
+ return `clsx(${renderExpr$1(seg.expr, ir)})`;
29995
+ }
29178
29996
  return renderExpr$1(seg.expr, ir);
29179
29997
  }
29180
29998
  if (segments.length === 1 && segments[0].kind === "interpolated") {
@@ -35009,7 +35827,8 @@ function composeSourceMap$3(ms, opts) {
35009
35827
  outputOffset: opts.scriptOutputOffset
35010
35828
  }] : [],
35011
35829
  fileExt: ".tsx",
35012
- userCodeLineOffset: opts.userCodeLineOffset
35830
+ userCodeLineOffset: opts.userCodeLineOffset,
35831
+ partialLineOffsets: opts.partialLineOffsets
35013
35832
  });
35014
35833
  }
35015
35834
  //#endregion
@@ -35112,7 +35931,8 @@ function emitReact(ir, opts = {}) {
35112
35931
  source: opts.source,
35113
35932
  scriptMap: shellScriptMap,
35114
35933
  scriptOutputOffset,
35115
- userCodeLineOffset
35934
+ userCodeLineOffset,
35935
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
35116
35936
  }) : null,
35117
35937
  diagnostics: [
35118
35938
  ...scriptDiags,
@@ -36106,6 +36926,80 @@ function arrowBody$2(body) {
36106
36926
  return genCode$5(_babel_types.arrowFunctionExpression([], body));
36107
36927
  }
36108
36928
  /**
36929
+ * Phase 55-04 (literal byte-identity) — reproduce the inline-authored comment
36930
+ * doubling at a script-partial splice boundary.
36931
+ *
36932
+ * In an inline-authored `<script>`, a comment block BETWEEN two statements is
36933
+ * attached by `@babel/parser` to BOTH neighbours (the earlier statement's
36934
+ * `trailingComments` AND the later statement's `leadingComments`). The `.rzts`
36935
+ * script-partial splice attaches the boundary banner ONLY to the spliced node's
36936
+ * `leadingComments` — the preceding statement lives in a different source file and
36937
+ * carries no matching trailing comment. Svelte emits the residual body one
36938
+ * statement at a time (`stmts.map((s) => genCode(s)).join('\n')`), so each
36939
+ * `genCode` call has its own comment-dedup set: in the inline form the boundary
36940
+ * banner therefore prints TWICE (once as the previous statement's trailing, once
36941
+ * as the next statement's leading), with a blank line after the previous closing
36942
+ * brace. Re-mirroring the spliced node's leading comments back onto the preceding
36943
+ * statement's trailing comments restores that byte-for-byte.
36944
+ *
36945
+ * Fires at a genuine splice boundary in EITHER direction (Phase 56 R1 broadened
36946
+ * the trigger): the CURRENT statement is spliced (`cur.extra.__roziePartialOrigin`
36947
+ * — the Phase 55 leading seam) OR the PREVIOUS statement is spliced and CUR is an
36948
+ * inline host successor carrying the leading comment (the R1 TRAILING seam). In
36949
+ * both cases CUR's leading comments are mirrored onto PREV's trailing comments
36950
+ * UNLESS already shared (within-partial statement pairs share the same comment
36951
+ * objects; host-only pairs — neither node spliced — are left exactly as authored).
36952
+ * `normalizeSplicedEmitLines` (core) has already anchored the seam spacing, so the
36953
+ * mirrored trailing copy spaces correctly.
36954
+ */
36955
+ function mirrorSpliceBoundaryComments$1(stmts) {
36956
+ for (let i = 1; i < stmts.length; i++) {
36957
+ const cur = stmts[i];
36958
+ const prev = stmts[i - 1];
36959
+ const curExtra = cur.extra;
36960
+ const prevExtra = prev.extra;
36961
+ const curSpliced = curExtra?.__roziePartialOrigin !== void 0;
36962
+ const prevSpliced = prevExtra?.__roziePartialOrigin !== void 0;
36963
+ if (!curSpliced && !prevSpliced) continue;
36964
+ const lead = cur.leadingComments;
36965
+ const prevTrail = prev.trailingComments;
36966
+ if (curSpliced && !prevSpliced && (!lead || lead.length === 0) && prevTrail && prevTrail.length > 0) {
36967
+ cur.leadingComments = [...prevTrail];
36968
+ continue;
36969
+ }
36970
+ if (!lead || lead.length === 0) continue;
36971
+ if (curSpliced && curExtra?.__rozieLeadingSeamPrevStripped === true) continue;
36972
+ const lastLead = lead[lead.length - 1];
36973
+ if (prevTrail && prevTrail.length > 0 && prevTrail[prevTrail.length - 1] === lastLead) continue;
36974
+ let toAppend = lead;
36975
+ if (prevSpliced && !curSpliced) {
36976
+ const afterGap = curExtra?.__rozieAfterGap;
36977
+ const anchorLine = (prev.loc?.end.line ?? 0) + (typeof afterGap === "number" ? afterGap : 1);
36978
+ const baseLine = lead[0]?.loc?.start.line;
36979
+ toAppend = lead.map((c) => {
36980
+ if (!c.loc) return { ...c };
36981
+ const startLine = baseLine === void 0 ? anchorLine : anchorLine + (c.loc.start.line - baseLine);
36982
+ const endLine = baseLine === void 0 ? anchorLine : anchorLine + (c.loc.end.line - baseLine);
36983
+ return {
36984
+ ...c,
36985
+ loc: {
36986
+ ...c.loc,
36987
+ start: {
36988
+ ...c.loc.start,
36989
+ line: startLine
36990
+ },
36991
+ end: {
36992
+ ...c.loc.end,
36993
+ line: endLine
36994
+ }
36995
+ }
36996
+ };
36997
+ });
36998
+ }
36999
+ prev.trailingComments = [...prevTrail ?? [], ...toAppend];
37000
+ }
37001
+ }
37002
+ /**
36109
37003
  * Render a PropTypeAnnotation as a TypeScript type string. Mirrors the Vue
36110
37004
  * target's renderType helper.
36111
37005
  */
@@ -36544,6 +37438,7 @@ function emitResidualScriptBody(clonedProgram, consumedLifecycleIndices, exposeN
36544
37438
  }
36545
37439
  stmts.push(stmt);
36546
37440
  }
37441
+ mirrorSpliceBoundaryComments$1(stmts);
36547
37442
  return {
36548
37443
  code: stmts.map((s) => {
36549
37444
  if (exposeNames.size > 0 && isExposedTopLevelDecl(s, exposeNames)) {
@@ -37207,6 +38102,14 @@ function emitSingleAttr$1(attr, ctx) {
37207
38102
  if (styleObjectLowered !== null) return styleObjectLowered;
37208
38103
  const expr = rewriteTemplateExpression$3(attr.expression, ir);
37209
38104
  const outName = resolveAttrName(attr.name, ctx);
38105
+ if (attr.name === "style") {
38106
+ ctx.runtimeImports?.add("rozieStyle");
38107
+ return `${outName}={rozieStyle(${expr})}`;
38108
+ }
38109
+ if (attr.name === "class" && attr.wrapForDisplay && !_babel_types.isObjectExpression(attr.expression) && !_babel_types.isTemplateLiteral(attr.expression) && shouldWrapSvelteAttrBinding(attr.name, attr.expression, ctx)) {
38110
+ ctx.runtimeImports?.add("rozieClass");
38111
+ return `${outName}={rozieClass(${expr})}`;
38112
+ }
37210
38113
  if (attr.wrapForDisplay && shouldWrapSvelteAttrBinding(attr.name, attr.expression, ctx)) {
37211
38114
  ctx.runtimeImports?.add("rozieAttr");
37212
38115
  return `${outName}={rozieAttr(${expr})}`;
@@ -37234,12 +38137,16 @@ function emitSingleAttr$1(attr, ctx) {
37234
38137
  * Convert a single AttributeBinding into a JS expression string suitable for
37235
38138
  * inclusion in an array (used by class/style merge below).
37236
38139
  */
37237
- function attrToArraySegment$1(attr, ir, runtimeImports) {
38140
+ function attrToArraySegment$1(attr, ir, runtimeImports, isClass = false) {
37238
38141
  if (attr.kind === "twoWayBinding") throw new Error(`Svelte target: twoWayBinding not valid in class/style array context (Phase 07.3 Wave 3 Plan 07.3-04).`);
37239
38142
  if (attr.kind === "static") return JSON.stringify(attr.value);
37240
38143
  if (attr.kind === "binding") {
37241
38144
  const code = rewriteTemplateExpression$3(attr.expression, ir);
37242
38145
  if (attr.wrapForDisplay && !_babel_types.isObjectExpression(attr.expression)) {
38146
+ if (isClass && !_babel_types.isTemplateLiteral(attr.expression)) {
38147
+ runtimeImports?.add("rozieClass");
38148
+ return `rozieClass(${code})`;
38149
+ }
37243
38150
  runtimeImports?.add("rozieDisplay");
37244
38151
  return `rozieDisplay(${code})`;
37245
38152
  }
@@ -37330,7 +38237,7 @@ function emitAttributes$2(attrs, ctx) {
37330
38237
  if (synthetic) merged.push(synthetic);
37331
38238
  } else if (src.name === a.name && !isLiteralStyleObjectBinding(src)) merged.push(src);
37332
38239
  for (const x of merged) consumed.add(x);
37333
- const segments = merged.map((x) => attrToArraySegment$1(x, ctx.ir, ctx.runtimeImports));
38240
+ const segments = merged.map((x) => attrToArraySegment$1(x, ctx.ir, ctx.runtimeImports, a.name === "class"));
37334
38241
  if (hasOpaqueMerge) for (const readExpr of opaqueSpreadClassReads) segments.push(readExpr);
37335
38242
  if (hasOpaqueMerge) deferredMergedClassStyle.push(`${a.name}={[${segments.join(", ")}]}`);
37336
38243
  else out.push(`${a.name}={[${segments.join(", ")}]}`);
@@ -38822,7 +39729,8 @@ function composeSourceMap$2(ms, opts) {
38822
39729
  outputOffset: opts.scriptOutputOffset
38823
39730
  }] : [],
38824
39731
  fileExt: ".svelte",
38825
- userCodeLineOffset: opts.userCodeLineOffset
39732
+ userCodeLineOffset: opts.userCodeLineOffset,
39733
+ partialLineOffsets: opts.partialLineOffsets
38826
39734
  });
38827
39735
  }
38828
39736
  //#endregion
@@ -38926,7 +39834,8 @@ function emitSvelte(ir, opts = {}) {
38926
39834
  source: opts.source,
38927
39835
  scriptMap: shellScriptMap,
38928
39836
  scriptOutputOffset,
38929
- userCodeLineOffset
39837
+ userCodeLineOffset,
39838
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
38930
39839
  }) : null,
38931
39840
  diagnostics: [
38932
39841
  ...scriptDiags,
@@ -40197,7 +41106,15 @@ function buildSlotCtx(slot) {
40197
41106
  */
40198
41107
  function buildNgTemplateContextGuard(componentName, slots) {
40199
41108
  if (slots.length === 0) return null;
40200
- const unionType = slots.map((s) => slotCtxName(s.name)).join(" | ");
41109
+ const seenCtxNames = /* @__PURE__ */ new Set();
41110
+ const ctxNames = [];
41111
+ for (const s of slots) {
41112
+ const ctxName = slotCtxName(s.name);
41113
+ if (seenCtxNames.has(ctxName)) continue;
41114
+ seenCtxNames.add(ctxName);
41115
+ ctxNames.push(ctxName);
41116
+ }
41117
+ const unionType = ctxNames.join(" | ");
40201
41118
  return [
40202
41119
  `static ngTemplateContextGuard(`,
40203
41120
  ` _dir: ${componentName},`,
@@ -40989,7 +41906,10 @@ function emitScript$2(ir, opts = {}) {
40989
41906
  const interfaceDecls = [];
40990
41907
  for (const typeDecl of hoistedTypeDecls) interfaceDecls.push(genCode$3(typeDecl));
40991
41908
  const slotFieldDecls = [];
41909
+ const seenSlotNames = /* @__PURE__ */ new Set();
40992
41910
  for (const slot of ir.slots) {
41911
+ if (seenSlotNames.has(slot.name)) continue;
41912
+ seenSlotNames.add(slot.name);
40993
41913
  const ctx = buildSlotCtx(slot);
40994
41914
  interfaceDecls.push(ctx.interfaceDecl);
40995
41915
  slotFieldDecls.push(ctx.fieldDecl);
@@ -41952,6 +42872,7 @@ function shouldWrapAttrBinding$1(name, expr, ctx, elementTagName) {
41952
42872
  if (BOOLEAN_HTML_ATTRS$1.has(name.toLowerCase())) return false;
41953
42873
  if ((name === "value" || name === "checked") && FORM_INPUT_TAGS$2.has(elementTagName.toLowerCase())) return false;
41954
42874
  if (name === "style") return false;
42875
+ if (name === "class") return false;
41955
42876
  if (_babel_types.isObjectExpression(expr)) return false;
41956
42877
  return true;
41957
42878
  }
@@ -44720,7 +45641,8 @@ function composeSourceMap$1(ms, opts) {
44720
45641
  outputOffset: opts.scriptOutputOffset
44721
45642
  }] : [],
44722
45643
  fileExt: ".ts",
44723
- userCodeLineOffset: opts.userCodeLineOffset
45644
+ userCodeLineOffset: opts.userCodeLineOffset,
45645
+ partialLineOffsets: opts.partialLineOffsets
44724
45646
  });
44725
45647
  }
44726
45648
  //#endregion
@@ -45106,7 +46028,8 @@ function emitAngular(ir, opts = {}) {
45106
46028
  source: opts.source,
45107
46029
  scriptMap: shellScriptMap,
45108
46030
  scriptOutputOffset,
45109
- userCodeLineOffset
46031
+ userCodeLineOffset,
46032
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
45110
46033
  }) : null,
45111
46034
  diagnostics: [
45112
46035
  ...scriptResult.diagnostics,
@@ -46050,6 +46973,56 @@ function tryHoistArrowToFunction(stmt) {
46050
46973
  return _babel_types.inherits(fn, stmt);
46051
46974
  }
46052
46975
  /**
46976
+ * Phase 55-04 (literal byte-identity) — reproduce the inline-authored comment
46977
+ * doubling at a script-partial splice boundary.
46978
+ *
46979
+ * In an inline-authored `<script>`, a comment block BETWEEN two statements is
46980
+ * attached by `@babel/parser` to BOTH neighbours (the earlier statement's
46981
+ * `trailingComments` AND the later statement's `leadingComments`). The `.rzts`
46982
+ * script-partial splice instead attaches the boundary banner ONLY to the spliced
46983
+ * node's `leadingComments` — the preceding statement lives in a different source
46984
+ * file and so carries no matching trailing comment. Re-mirroring the spliced
46985
+ * node's leading comments back onto the preceding statement's trailing comments
46986
+ * restores the inline form byte-for-byte:
46987
+ * - whole-program generation (Solid here) prints the (deduped) banner once AND
46988
+ * gets the boundary blank line from `@babel/generator`'s `printJoin`
46989
+ * `_lastCommentLine` path — a trailing comment on the previous statement is
46990
+ * exactly what triggers the loc-delta blank-line insertion; and
46991
+ * - per-statement generation (Vue/Svelte) doubles the banner (one copy as the
46992
+ * previous statement's trailing, one as the next statement's leading).
46993
+ *
46994
+ * Fires ONLY at a genuine splice boundary: the current statement carries
46995
+ * `extra.__roziePartialOrigin` AND its leading comments are not ALREADY shared as
46996
+ * the previous statement's trailing comments (within-partial statement pairs
46997
+ * already share the same comment objects; host-only pairs are left exactly as
46998
+ * authored). MUST run before the arrow→function hoist — `t.inherits` does not
46999
+ * copy `extra`, so the spliced marker is only visible on the original nodes.
47000
+ */
47001
+ function mirrorSpliceBoundaryComments(stmts) {
47002
+ for (let i = 1; i < stmts.length; i++) {
47003
+ const cur = stmts[i];
47004
+ const prev = stmts[i - 1];
47005
+ const lead = cur.leadingComments;
47006
+ if (!lead || lead.length === 0) continue;
47007
+ const extra = cur.extra;
47008
+ const curSpliced = extra?.__roziePartialOrigin !== void 0;
47009
+ const prevSpliced = prev.extra?.__roziePartialOrigin !== void 0;
47010
+ if (!curSpliced) {
47011
+ if (prevSpliced && extra?.__rozieAfterGap !== void 0) {
47012
+ const prevTrail = prev.trailingComments;
47013
+ const lastLead = lead[lead.length - 1];
47014
+ if (prevTrail && prevTrail.length > 0 && prevTrail[prevTrail.length - 1] === lastLead) continue;
47015
+ prev.trailingComments = [...prevTrail ?? [], ...lead];
47016
+ }
47017
+ continue;
47018
+ }
47019
+ const prevTrail = prev.trailingComments;
47020
+ const lastLead = lead[lead.length - 1];
47021
+ if (prevTrail && prevTrail.length > 0 && prevTrail[prevTrail.length - 1] === lastLead) continue;
47022
+ prev.trailingComments = [...prevTrail ?? [], ...lead];
47023
+ }
47024
+ }
47025
+ /**
46053
47026
  * WR-01 ROOT CAUSE 2 — re-project an author function-type annotation written
46054
47027
  * on a `VariableDeclarator` `id` (`const f: (e: E) => R = …`) onto a rebuilt
46055
47028
  * `FunctionDeclaration` (which has no annotatable `id`). Each declarator-type
@@ -46175,7 +47148,7 @@ function emitScript$1(ir, collectors, _registry) {
46175
47148
  }
46176
47149
  }
46177
47150
  for (const ref of ir.refs) hookLines.push(`let ${ref.name}Ref: HTMLElement | null = null;`);
46178
- const filteredStmts = [];
47151
+ const residualStmts = [];
46179
47152
  for (const stmt of rewriteResult.rewrittenProgram.program.body) {
46180
47153
  if (_babel_types.isVariableDeclaration(stmt)) {
46181
47154
  if (stmt.declarations.every((d) => d.init && _babel_types.isCallExpression(d.init) && _babel_types.isIdentifier(d.init.callee) && d.init.callee.name === "$computed")) continue;
@@ -46185,8 +47158,10 @@ function emitScript$1(ir, collectors, _registry) {
46185
47158
  const callee = stmt.expression.callee;
46186
47159
  if (_babel_types.isIdentifier(callee) && (callee.name === "$onMount" || callee.name === "$onUnmount" || callee.name === "$onUpdate" || callee.name === "$watch" || callee.name === "$expose" || callee.name === "$provide")) continue;
46187
47160
  }
46188
- filteredStmts.push(tryHoistArrowToFunction(stmt) ?? stmt);
47161
+ residualStmts.push(stmt);
46189
47162
  }
47163
+ mirrorSpliceBoundaryComments(residualStmts);
47164
+ const filteredStmts = residualStmts.map((stmt) => tryHoistArrowToFunction(stmt) ?? stmt);
46190
47165
  let userArrowsSection = "";
46191
47166
  let scriptMap = null;
46192
47167
  if (filteredStmts.length > 0) {
@@ -46887,6 +47862,10 @@ function composeClassValue(attrs, ir, exprOpts, runtime) {
46887
47862
  if (attrs.length === 1 && attrs[0].kind === "binding") {
46888
47863
  const a = attrs[0];
46889
47864
  if (_babel_types.isObjectExpression(a.expression)) return renderExpr(a.expression, ir, exprOpts);
47865
+ if (a.wrapForDisplay && !_babel_types.isTemplateLiteral(a.expression)) {
47866
+ runtime?.add("rozieClass");
47867
+ return `rozieClass(${renderExpr(a.expression, ir, exprOpts)})`;
47868
+ }
46890
47869
  return renderExpr(a.expression, ir, exprOpts);
46891
47870
  }
46892
47871
  if (attrs.length === 1 && attrs[0].kind === "interpolated") {
@@ -46905,7 +47884,10 @@ function composeClassValue(attrs, ir, exprOpts, runtime) {
46905
47884
  const parts = [];
46906
47885
  for (const a of attrs) if (a.kind === "twoWayBinding") throw new Error(`Solid target: twoWayBinding not valid in class array context (Phase 07.3 Wave 3 Plan 07.3-07).`);
46907
47886
  else if (a.kind === "static") parts.push(renderStaticClassValue(a.value));
46908
- else if (a.kind === "binding") parts.push(`(${renderExpr(a.expression, ir, exprOpts)})`);
47887
+ else if (a.kind === "binding") if (a.wrapForDisplay && !_babel_types.isTemplateLiteral(a.expression)) {
47888
+ runtime?.add("rozieClass");
47889
+ parts.push(`rozieClass(${renderExpr(a.expression, ir, exprOpts)})`);
47890
+ } else parts.push(`(${renderExpr(a.expression, ir, exprOpts)})`);
46909
47891
  else if (a.kind === "spreadBinding") throw new Error(`Solid target: spreadBinding not valid in class array context (Phase 14).`);
46910
47892
  else {
46911
47893
  let lit = "";
@@ -49556,7 +50538,8 @@ function composeSourceMap(ms, opts) {
49556
50538
  outputOffset: opts.scriptOutputOffset
49557
50539
  }] : [],
49558
50540
  fileExt: ".tsx",
49559
- userCodeLineOffset: opts.userCodeLineOffset
50541
+ userCodeLineOffset: opts.userCodeLineOffset,
50542
+ partialLineOffsets: opts.partialLineOffsets
49560
50543
  });
49561
50544
  }
49562
50545
  //#endregion
@@ -49662,7 +50645,8 @@ function emitSolid(ir, opts = {}) {
49662
50645
  source: opts.source,
49663
50646
  scriptMap: shell.scriptMap,
49664
50647
  scriptOutputOffset: shell.scriptOutputOffset,
49665
- userCodeLineOffset: shell.userCodeLineOffset
50648
+ userCodeLineOffset: shell.userCodeLineOffset,
50649
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
49666
50650
  }) : null;
49667
50651
  const diagnostics = [
49668
50652
  ...scriptResult.diagnostics,
@@ -52534,6 +53518,10 @@ function emitAttribute(attr, ir, tagName, tagKind = "html", opts) {
52534
53518
  return `style=\${styleMap(${expr})}`;
52535
53519
  }
52536
53520
  const expr = rewriteTemplateExpression(attr.expression, ir);
53521
+ if (attr.name === "style") {
53522
+ opts?.runtime.add("rozieStyle");
53523
+ return `style=\${rozieStyle(${expr})}`;
53524
+ }
52537
53525
  if (tagKind === "component" || tagKind === "self") {
52538
53526
  const propName = attr.name.includes("-") ? attr.name.replace(/-([a-z])/g, (_, ch) => ch.toUpperCase()) : attr.name;
52539
53527
  if (opts?._state && (_babel_types.isArrayExpression(attr.expression) || _babel_types.isObjectExpression(attr.expression)) && isPureLiteral(attr.expression)) {
@@ -52953,9 +53941,12 @@ function emitElementOpenTag(node, ir, irName, opts) {
52953
53941
  const expr = rewriteTemplateExpression(bindingClass.expression, ir);
52954
53942
  const staticPart = staticClassValues.length > 0 ? `${staticClassValues.join(" ")} ` : "";
52955
53943
  let classExpr = expr;
52956
- if (bindingClass.wrapForDisplay) {
53944
+ if (bindingClass.wrapForDisplay) if (_babel_types.isTemplateLiteral(bindingClass.expression)) {
52957
53945
  opts.runtime.add("rozieDisplay");
52958
53946
  classExpr = `rozieDisplay(${expr})`;
53947
+ } else {
53948
+ opts.runtime.add("rozieClass");
53949
+ classExpr = `rozieClass(${expr})`;
52959
53950
  }
52960
53951
  parts.push(`class="${staticPart}\${(${classExpr})}"`);
52961
53952
  } else if (bindingClass.kind === "interpolated") {