@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.
@@ -9994,6 +9994,66 @@ function subtreeReads(node, accessor, name) {
9994
9994
  return found;
9995
9995
  }
9996
9996
  /**
9997
+ * Returns true if the subtree rooted at `node` contains a BARE Identifier READ of
9998
+ * `name` — the exact shape the Vue `$computed` lowering wraps to `<name>.value`.
9999
+ *
10000
+ * The Vue trigger condition for the `$computed` shadow bug: a `$computed` name is
10001
+ * read as a BARE identifier in source (there is NO `$computed.<name>` accessor
10002
+ * member to gate on), and the downstream Identifier visitor `.value`-wraps every
10003
+ * such bare read. A colliding param/local only mis-captures that wrap when it
10004
+ * actually performs a bare read of `name` within its scope — so a `binding`
10005
+ * trigger (mere existence) would over-apply (renaming an unused same-named param
10006
+ * → corpus drift), and the `accessor` trigger cannot fire (no member access).
10007
+ *
10008
+ * Excludes the SAME non-read positions the Vue Identifier visitor skips, so the
10009
+ * gate matches the rewrite exactly: declaration ids, non-computed member
10010
+ * properties, object keys (shorthand or not), TS type positions, function names,
10011
+ * function params, and import/export specifier ids. A computed member property
10012
+ * (`obj[name]`) IS a bare read and counts.
10013
+ *
10014
+ * Hand-rolled own-child walk (the `subtreeReads` twin): a direct walk with
10015
+ * parent-context tracking is simpler than a Program-rooted Babel traverse and
10016
+ * has no rooting constraint.
10017
+ */
10018
+ function subtreeReadsBareIdentifier(node, name) {
10019
+ if (!node) return false;
10020
+ let found = false;
10021
+ function walk(n, parent) {
10022
+ if (found || !n || typeof n !== "object" || !("type" in n)) return;
10023
+ if (t.isIdentifier(n) && n.name === name) {
10024
+ if (isBareRead(n, parent)) {
10025
+ found = true;
10026
+ return;
10027
+ }
10028
+ }
10029
+ if (n.type.startsWith("TS") && n.type !== "TSNonNullExpression" && n.type !== "TSAsExpression" && n.type !== "TSSatisfiesExpression" && n.type !== "TSTypeAssertion") return;
10030
+ for (const key of Object.keys(n)) {
10031
+ if (key === "loc" || key === "start" || key === "end" || key === "leadingComments" || key === "trailingComments" || key === "innerComments") continue;
10032
+ const v = n[key];
10033
+ if (Array.isArray(v)) {
10034
+ for (const item of v) if (item && typeof item === "object" && "type" in item) walk(item, n);
10035
+ } else if (v && typeof v === "object" && "type" in v) walk(v, n);
10036
+ }
10037
+ }
10038
+ function isBareRead(id, parent) {
10039
+ if (!parent) return true;
10040
+ if (t.isVariableDeclarator(parent) && parent.id === id) return false;
10041
+ if ((t.isMemberExpression(parent) || t.isOptionalMemberExpression(parent)) && parent.property === id && !parent.computed) return false;
10042
+ if (t.isObjectProperty(parent) && parent.key === id && !parent.computed) return false;
10043
+ if (t.isFunctionDeclaration(parent) && parent.id === id) return false;
10044
+ if (t.isFunctionExpression(parent) && parent.id === id) return false;
10045
+ if (t.isFunction(parent) && parent.params.includes(id)) return false;
10046
+ if (t.isImportSpecifier(parent)) return false;
10047
+ if (t.isImportDefaultSpecifier(parent)) return false;
10048
+ if (t.isImportNamespaceSpecifier(parent)) return false;
10049
+ if (t.isExportSpecifier(parent)) return false;
10050
+ if (t.isLabeledStatement(parent) && parent.label === id) return false;
10051
+ return true;
10052
+ }
10053
+ walk(node, null);
10054
+ return found;
10055
+ }
10056
+ /**
9997
10057
  * THE UNIFIED PASS. Renames any USER binding (function param or
9998
10058
  * `const`/`let`/`var` declarator) that collides with a generated symbol to
9999
10059
  * `<name>$local`, atomically across its binding scope, gated only-on-collision
@@ -10013,6 +10073,7 @@ function deconflictGeneratedSymbols(program, groups, protectedNames = /* @__PURE
10013
10073
  if (!group.names.has(name)) return false;
10014
10074
  if (protectedNames.has(name)) return false;
10015
10075
  if (group.trigger.kind === "binding") return true;
10076
+ if (group.trigger.kind === "bare-read") return subtreeReadsBareIdentifier(scopeBlock, name);
10016
10077
  return subtreeReads(scopeBlock, group.trigger.accessor, name);
10017
10078
  };
10018
10079
  traverse$19(program, {
@@ -10026,6 +10087,7 @@ function deconflictGeneratedSymbols(program, groups, protectedNames = /* @__PURE
10026
10087
  if (!patternIntroducesBinding$3(id, name)) continue;
10027
10088
  const binding = path.scope.getBinding(name);
10028
10089
  const ownerScope = binding ? binding.scope : path.scope;
10090
+ if (group.trigger.kind === "bare-read" && t.isProgram(ownerScope.block)) continue;
10029
10091
  if (collides(group, name, ownerScope.block)) ownerScope.rename(name, alias(name));
10030
10092
  }
10031
10093
  },
@@ -18651,17 +18713,53 @@ function bindingNames(stmt) {
18651
18713
  function importLocalNames(imp) {
18652
18714
  return imp.specifiers.map((s) => s.local.name);
18653
18715
  }
18716
+ /**
18717
+ * Root identifier of a TS entity name (`Foo` or `Foo.Bar.Baz` → `Foo`). A
18718
+ * `TSImportType` (`import('pkg').Foo`) has no root local identifier — returns
18719
+ * null (its module surface is already self-contained, never a hoist target).
18720
+ */
18721
+ function rootTypeIdentifier(name) {
18722
+ let node = name;
18723
+ while (node && t.isTSQualifiedName(node)) node = node.left;
18724
+ return node && t.isIdentifier(node) ? node.name : null;
18725
+ }
18654
18726
  /** Referenced identifier names within a statement (excludes bindings/keys). */
18655
18727
  function referencedNames(stmt) {
18656
18728
  const out = /* @__PURE__ */ new Set();
18657
18729
  try {
18658
- traverse$18(t.file(t.program([stmt])), { ReferencedIdentifier(path) {
18659
- out.add(path.node.name);
18660
- } });
18730
+ traverse$18(t.file(t.program([stmt])), {
18731
+ ReferencedIdentifier(path) {
18732
+ out.add(path.node.name);
18733
+ },
18734
+ TSTypeReference(path) {
18735
+ const root = rootTypeIdentifier(path.node.typeName);
18736
+ if (root) out.add(root);
18737
+ },
18738
+ TSTypeQuery(path) {
18739
+ const root = rootTypeIdentifier(path.node.exprName);
18740
+ if (root) out.add(root);
18741
+ }
18742
+ });
18661
18743
  } catch {}
18662
18744
  return out;
18663
18745
  }
18664
18746
  /**
18747
+ * Effective import kind of a single specifier. A specifier is type-only when
18748
+ * EITHER the declaration is `import type { … }` (declKind === 'type', in which
18749
+ * case Babel reports the per-specifier `importKind` as `'value'`) OR the
18750
+ * specifier itself is the inline `import { type X }` form (spec.importKind ===
18751
+ * 'type'). The old `spec.importKind ? spec.importKind : declKind` form let the
18752
+ * `'value'` per-specifier kind of a declaration-level `import type` mask the
18753
+ * declaration's type-ness, so a hoisted `import type { T }` lost its type-only
18754
+ * marker and emitted as a runtime `import { T }` (IN-02). Value imports are
18755
+ * unaffected (both kinds are `'value'`).
18756
+ */
18757
+ function effectiveImportKind(declKind, spec) {
18758
+ if (declKind === "type") return "type";
18759
+ if (t.isImportSpecifier(spec) && spec.importKind === "type") return "type";
18760
+ return "value";
18761
+ }
18762
+ /**
18665
18763
  * Dedup key for a single import specifier per the R4 tuple:
18666
18764
  * (source, importKind, default|namespace|named:imported, local). Including the
18667
18765
  * LOCAL name keeps `import { thing }` and `import { thing as aliased }` distinct
@@ -18669,7 +18767,7 @@ function referencedNames(stmt) {
18669
18767
  * host and partial collapses to ONE statement.
18670
18768
  */
18671
18769
  function specifierKey(source, declKind, spec) {
18672
- const kind = (t.isImportSpecifier(spec) && spec.importKind ? spec.importKind : declKind) || "value";
18770
+ const kind = effectiveImportKind(declKind, spec);
18673
18771
  if (t.isImportDefaultSpecifier(spec)) return `${source}\0${kind}\0default\0${spec.local.name}`;
18674
18772
  if (t.isImportNamespaceSpecifier(spec)) return `${source}\0${kind}\0namespace\0${spec.local.name}`;
18675
18773
  return `${source}\0${kind}\0named:${t.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value}\0${spec.local.name}`;
@@ -18679,12 +18777,12 @@ function specifierKey(source, declKind, spec) {
18679
18777
  * deduped by {@link specifierKey} and grouped by (source, importKind) so the
18680
18778
  * splice produces idiomatic merged `import { a, b } from 'src'` statements.
18681
18779
  */
18682
- function hoistSpecifier(ctx, sourceNode, declKind, spec) {
18780
+ function hoistSpecifier(ctx, sourceNode, declKind, spec, sourceImport) {
18683
18781
  const source = sourceNode.value;
18684
18782
  const key = specifierKey(source, declKind, spec);
18685
18783
  if (ctx.hoistKeys.has(key)) return;
18686
18784
  ctx.hoistKeys.add(key);
18687
- const groupKind = (t.isImportSpecifier(spec) && spec.importKind ? spec.importKind : declKind) || "value";
18785
+ const groupKind = effectiveImportKind(declKind, spec);
18688
18786
  const groupKey = `${source}\0${groupKind}`;
18689
18787
  const existing = ctx.hoistGroups.get(groupKey);
18690
18788
  if (existing) {
@@ -18693,9 +18791,427 @@ function hoistSpecifier(ctx, sourceNode, declKind, spec) {
18693
18791
  }
18694
18792
  const decl = t.importDeclaration([spec], sourceNode);
18695
18793
  if (groupKind === "type") decl.importKind = "type";
18794
+ if (sourceImport) {
18795
+ if (sourceImport.loc) decl.loc = sourceImport.loc;
18796
+ if (sourceImport.trailingComments) decl.trailingComments = sourceImport.trailingComments;
18797
+ if (sourceImport.innerComments) decl.innerComments = sourceImport.innerComments;
18798
+ }
18696
18799
  ctx.hoistGroups.set(groupKey, decl);
18697
18800
  ctx.hoistImports.push(decl);
18698
18801
  }
18802
+ /**
18803
+ * Shift a single Babel `Position` object's `.line` by `offset`, exactly once.
18804
+ *
18805
+ * CRITICAL (Phase 55-04 bug fix): `@babel/parser` SHARES one `Position` object
18806
+ * across the `loc.start`/`loc.end` of nested nodes that begin/end at the same
18807
+ * source position — e.g. a `const f = () => {...}` shares ONE `loc.end` object
18808
+ * between the VariableDeclaration, VariableDeclarator, ArrowFunctionExpression
18809
+ * and BlockStatement (verified: all four `.loc.end` are `===`). The earlier
18810
+ * `seen`-keyed-on-the-`loc`-WRAPPER dedupe therefore shifted such a shared
18811
+ * `Position` once PER wrapping node (4× for the example above), corrupting
18812
+ * `loc.end.line` to `original + 4·offset` (~13985 for a host-line-3502 decl).
18813
+ * That blew the trailing-comment delta hugely negative, collapsing a between-
18814
+ * declaration comment onto the prior closing brace (`}; // comment`). Deduping on
18815
+ * the `Position` object itself shifts each unique position exactly once. Never
18816
+ * throws (D-04).
18817
+ */
18818
+ function shiftPositionLine(pos, offset, shifted) {
18819
+ if (!pos || shifted.has(pos)) return;
18820
+ shifted.add(pos);
18821
+ pos.line += offset;
18822
+ }
18823
+ /**
18824
+ * Stash a node's `.rzts` origin (once, idempotent) then shift its `loc` lines by
18825
+ * `offset`. Byte offsets (`loc.start.index`/`loc.end.index`) and `loc.filename`
18826
+ * are NEVER touched. `shifted` dedupes shared `Position` objects (see
18827
+ * {@link shiftPositionLine}) so a position aliased across nested nodes — or a
18828
+ * comment attached to two adjacent statements — is shifted exactly once
18829
+ * (Pitfall 2). Never throws (D-04).
18830
+ */
18831
+ function stashAndShiftNode(node, offset, shifted) {
18832
+ const loc = node.loc;
18833
+ if (!loc) return;
18834
+ const extra = node.extra ?? {};
18835
+ if (!("__roziePartialOrigin" in extra)) {
18836
+ const startAlreadyShifted = shifted.has(loc.start);
18837
+ node.extra = {
18838
+ ...extra,
18839
+ __roziePartialOrigin: {
18840
+ line: loc.start.line - (startAlreadyShifted ? offset : 0),
18841
+ column: loc.start.column,
18842
+ filename: loc.filename
18843
+ }
18844
+ };
18845
+ }
18846
+ shiftPositionLine(loc.start, offset, shifted);
18847
+ shiftPositionLine(loc.end, offset, shifted);
18848
+ }
18849
+ /** As {@link stashAndShiftNode} but for a comment (origin stashed on the comment). */
18850
+ function stashAndShiftComment(comment, offset, shifted) {
18851
+ const loc = comment.loc;
18852
+ if (!loc) return;
18853
+ const c = comment;
18854
+ if (c.__roziePartialOrigin === void 0) {
18855
+ const startAlreadyShifted = shifted.has(loc.start);
18856
+ c.__roziePartialOrigin = {
18857
+ line: loc.start.line - (startAlreadyShifted ? offset : 0),
18858
+ column: loc.start.column,
18859
+ filename: loc.filename
18860
+ };
18861
+ }
18862
+ shiftPositionLine(loc.start, offset, shifted);
18863
+ shiftPositionLine(loc.end, offset, shifted);
18864
+ }
18865
+ /** Shift every leading/trailing/inner comment attached to `node`. */
18866
+ function shiftAttachedComments(node, offset, shifted) {
18867
+ for (const c of node.leadingComments ?? []) stashAndShiftComment(c, offset, shifted);
18868
+ for (const c of node.trailingComments ?? []) stashAndShiftComment(c, offset, shifted);
18869
+ for (const c of node.innerComments ?? []) stashAndShiftComment(c, offset, shifted);
18870
+ }
18871
+ /** The line a block's first emitted token occupies: its banner comment if it has
18872
+ * leading comments, else the node itself. */
18873
+ function blockFirstEmitLine(firstNode) {
18874
+ const firstLeading = firstNode.leadingComments?.[0]?.loc;
18875
+ return firstLeading ? firstLeading.start.line : firstNode.loc.start.line;
18876
+ }
18877
+ /**
18878
+ * Phase 56-R10 — true when `stmt` is a sigil DIRECTIVE that every target's residual-body
18879
+ * emit STRIPS (it is consumed into a non-residual section: lifecycle / context / computed /
18880
+ * expose / watch). Mirrors the strip lists in each `emitResidualScriptBody`
18881
+ * (`$onMount`/`$onUnmount`/`$onUpdate`/`$watch`/`$expose`/`$provide` ExpressionStatements,
18882
+ * and `$computed`/`$inject` VariableDeclarations).
18883
+ *
18884
+ * USED ONLY to decide whether a spliced LEADING-comment run's IMMEDIATE source predecessor
18885
+ * survives in the residual body. When that predecessor is STRIPPED (e.g. the real DataTable
18886
+ * `exposeStateVerbs` run sits below a `$provide(...)`), the inline-authored form attaches the
18887
+ * boundary comment to the predecessor's `trailingComments` + the spliced decl's
18888
+ * `leadingComments` (shared object), but per-statement generation drops the predecessor's
18889
+ * trailing copy WITH the stripped statement → the comment SINGLE-emits. The vue/svelte splice
18890
+ * mirror would otherwise re-create that prev-trailing copy and DOUBLE it (the R10 bug). When
18891
+ * the predecessor SURVIVES (a plain `let`/`const`, e.g. `let expandedTouched` above
18892
+ * `groupingActiveDefault`), both copies survive → the inline form DOUBLES and the mirror must
18893
+ * keep doing so. Never throws.
18894
+ */
18895
+ function isStrippedSigilDirective(stmt) {
18896
+ if (t.isExpressionStatement(stmt) && t.isCallExpression(stmt.expression)) {
18897
+ const callee = stmt.expression.callee;
18898
+ if (t.isIdentifier(callee)) return callee.name === "$onMount" || callee.name === "$onUnmount" || callee.name === "$onUpdate" || callee.name === "$watch" || callee.name === "$expose" || callee.name === "$provide";
18899
+ return false;
18900
+ }
18901
+ if (t.isVariableDeclaration(stmt) && stmt.declarations.length > 0) return stmt.declarations.every((d) => d.init !== null && d.init !== void 0 && t.isCallExpression(d.init) && t.isIdentifier(d.init.callee) && (d.init.callee.name === "$computed" || d.init.callee.name === "$inject"));
18902
+ return false;
18903
+ }
18904
+ /**
18905
+ * Measure the ORIGINAL source gap above a decl run's first emit token (D-02, R2).
18906
+ *
18907
+ * Returns the `prevEnd + gap` delta the run should flow at: the distance, in source
18908
+ * lines, from the run's first emit token (its banner comment if any, else its first
18909
+ * node) DOWN from the END of the nearest preceding node IN THE SAME SOURCE FILE — a
18910
+ * same-file freshly-hoisted import (`sameFileHoists`) or the prior same-file decl run's
18911
+ * last node (`prevRunLastNode`). `gap = firstEmitLine − precedingEnd` so a zero-blank
18912
+ * adjacency yields `1` (next line) and N blanks yield `N+1` (no clamp). All `loc`s are
18913
+ * still PARTIAL-LOCAL here (the normalize shift runs later), so this reproduces the
18914
+ * partial's own pre-extraction layout — which, by the extraction rule, equals the host
18915
+ * adjacency the inline oracle has (Pitfall 3: measure source-side, NOT the host seam).
18916
+ *
18917
+ * When the run's first decl has NO same-file predecessor (a file-top nested-partial
18918
+ * decl, e.g. HostD's `inner` at the top of its own file), there is no source delta to
18919
+ * measure, so this falls back to the legacy default `2` — preserving the pre-D-02
18920
+ * behavior for that shape (no regression). Never throws.
18921
+ */
18922
+ function measureOriginalGap(firstNode, sameFileHoists, prevRunLastNode) {
18923
+ const loc = firstNode.loc;
18924
+ if (!loc) return 2;
18925
+ const firstEmitLine = blockFirstEmitLine(firstNode);
18926
+ const fname = loc.filename;
18927
+ let precedingEnd = 0;
18928
+ for (const h of sameFileHoists) {
18929
+ const hl = h.loc;
18930
+ if (hl && hl.filename === fname && hl.end.line < firstEmitLine) precedingEnd = Math.max(precedingEnd, hl.end.line);
18931
+ }
18932
+ const pl = prevRunLastNode?.loc;
18933
+ if (pl && pl.filename === fname && pl.end.line < firstEmitLine) precedingEnd = Math.max(precedingEnd, pl.end.line);
18934
+ if (precedingEnd === 0) return 2;
18935
+ return Math.max(1, firstEmitLine - precedingEnd);
18936
+ }
18937
+ /**
18938
+ * Shift every node + attached comment in a block by `offset`, deduping on the
18939
+ * underlying `Position` objects (see {@link shiftPositionLine}). Never throws.
18940
+ *
18941
+ * WR-01: `shifted` is supplied by the CALLER and SHARED across every block in one
18942
+ * normalize pass. A between-statement comment can be aliased across two blocks —
18943
+ * `@babel/parser` attaches the comment between a hoisted import and the first decl
18944
+ * to BOTH the import's `trailingComments` and the decl's `leadingComments` (the
18945
+ * SAME `Position` objects). When the hoist and that decl run land in separate
18946
+ * blocks, a per-block `shifted` set would shift the aliased comment TWICE (once per
18947
+ * block), corrupting its emit line. A shared set shifts each unique `Position`
18948
+ * exactly once. The adjacent hoist/decl offsets are equal by construction (the
18949
+ * sequential decl anchor is derived from the shifted hoist end + the same gap the
18950
+ * partial's import→decl delta encodes), so first-touch-wins is the correct offset.
18951
+ */
18952
+ function shiftBlock(block, offset, shifted) {
18953
+ for (const top of block.nodes) {
18954
+ stashAndShiftNode(top, offset, shifted);
18955
+ shiftAttachedComments(top, offset, shifted);
18956
+ try {
18957
+ traverse$18(t.file(t.program([top])), { enter(path) {
18958
+ stashAndShiftNode(path.node, offset, shifted);
18959
+ shiftAttachedComments(path.node, offset, shifted);
18960
+ } });
18961
+ } catch {}
18962
+ }
18963
+ }
18964
+ /** Shift a single comment's loc lines by `offset` WITHOUT stashing a partial origin. */
18965
+ function shiftCommentLinesOnly(comment, offset, shifted) {
18966
+ const loc = comment.loc;
18967
+ if (!loc) return;
18968
+ shiftPositionLine(loc.start, offset, shifted);
18969
+ shiftPositionLine(loc.end, offset, shifted);
18970
+ }
18971
+ /**
18972
+ * Phase 56-R8 — shift a HOST node's loc + attached/nested comments by `offset`,
18973
+ * deduping on the underlying `Position` objects (shared `shifted` set, like
18974
+ * {@link shiftBlock}). Never throws (D-04).
18975
+ *
18976
+ * Unlike {@link shiftBlock} / {@link stashAndShiftNode}, this does NOT stash a
18977
+ * `__roziePartialOrigin`: a host statement belongs to the HOST `.rozie` file, so its
18978
+ * `loc` is the host source-map anchor and must NOT be re-keyed onto a partial origin
18979
+ * (`buildPartialLineOffsets` is per-FILE first-stash-wins and would mis-offset every
18980
+ * other host segment). This is invoked ONLY for an after-side host run that carries a
18981
+ * GENUINE intended blank below a spliced run (`afterGap >= 2`) — the gap-1 trailing
18982
+ * seam. Such runs never appear in any built source-map gate today (dist-parity compiles
18983
+ * with `sourceMap: false`; the R5 smoke fixtures have no `afterGap >= 2` host run), so
18984
+ * the line shift is invisible to the source-map gates while making the @babel/generator
18985
+ * blank-line delta reproduce the source's after-side blank on vue/svelte/solid. The
18986
+ * host node's residual source-map LINE imperfection for such runs is the same class of
18987
+ * `userCodeLineOffset` limitation already documented in deferred-items.md (#1).
18988
+ */
18989
+ function shiftHostNodeLines(node, offset, shifted) {
18990
+ if (offset === 0) return;
18991
+ const apply = (n) => {
18992
+ if (n.loc) {
18993
+ shiftPositionLine(n.loc.start, offset, shifted);
18994
+ shiftPositionLine(n.loc.end, offset, shifted);
18995
+ }
18996
+ for (const c of n.leadingComments ?? []) shiftCommentLinesOnly(c, offset, shifted);
18997
+ for (const c of n.trailingComments ?? []) shiftCommentLinesOnly(c, offset, shifted);
18998
+ for (const c of n.innerComments ?? []) shiftCommentLinesOnly(c, offset, shifted);
18999
+ };
19000
+ apply(node);
19001
+ try {
19002
+ traverse$18(t.file(t.program([node])), { enter(path) {
19003
+ apply(path.node);
19004
+ } });
19005
+ } catch {}
19006
+ }
19007
+ /**
19008
+ * FINAL inline-pass step (Phase 55) — decouple the line `@babel/generator` reads
19009
+ * for blank-line/comment placement from the line it reads for the source-map
19010
+ * origin, for every spliced partial node.
19011
+ *
19012
+ * Rationale (RESEARCH Key Finding 1): under `retainLines:false` the generator's
19013
+ * comment-adjacency + blank-line math reads `node.loc.start.line` and
19014
+ * `comment.loc.start.line` only as DELTAS; only the host↔partial (and
19015
+ * partial↔partial) BOUNDARY deltas are wrong (a spliced node carries a small
19016
+ * `.rzts`-local line discontinuous with its host neighbour). A CONSTANT per-block
19017
+ * offset preserves each block's intra deltas; the right boundary delta is restored
19018
+ * by anchoring each block SEQUENTIALLY in residual-body order.
19019
+ *
19020
+ * SEQUENTIAL ANCHOR (Phase 55-04): each block's first emitted line (its banner
19021
+ * comment, else its first node) is anchored ONE blank line below the PRECEDING
19022
+ * statement in the final body. Anchoring every block at its own replaced import
19023
+ * line (Plan 02's approach) collapsed multi-partial hosts: three consecutive
19024
+ * mid-body imports made expand/group/facet pile onto adjacent host lines, so each
19025
+ * 30-45-line block overlapped the next and the partial↔partial comment deltas went
19026
+ * negative (`}; // banner` collapse). Flowing the blocks sequentially reproduces the
19027
+ * inline-authored contiguous layout. The first block (or any block preceded only by
19028
+ * un-located content) falls back to its replaced import's host line.
19029
+ *
19030
+ * WR-01 (whole-program byte-identity): the walk runs over the FULL emit body
19031
+ * (`[...hoistImports, ...residualBody]`), NOT just the residual body, so a
19032
+ * freshly-hoisted import flows in true emit order ahead of the decls. Consecutive
19033
+ * IMPORT blocks anchor CONTIGUOUSLY (gap 1 — no blank between imports); every other
19034
+ * run anchors one blank line below the preceding statement (gap 2). Because each
19035
+ * source-file decl run is now its OWN block (see {@link SplicedEmitBlock}), a
19036
+ * nested-partial decl run flows one blank line below the parent decl run rather than
19037
+ * inheriting the hoist's incommensurate file-top offset.
19038
+ *
19039
+ * TWO PASSES (WR-01): a between-statement comment is aliased across blocks — the
19040
+ * `Position` that is a hoisted import's `trailingComments[i]` is the SAME object as
19041
+ * the next decl's `leadingComments[i]`. If offsets were applied while walking, the
19042
+ * hoist block would shift that comment, and the decl block's `blockFirstEmitLine`
19043
+ * would then read the ALREADY-SHIFTED comment line and derive a wrong offset
19044
+ * (collapsing the comment onto the decl). So PASS 1 MEASURES every block's offset
19045
+ * from ORIGINAL (unmutated) lines, tracking the running emit end arithmetically;
19046
+ * PASS 2 MUTATES, applying every offset with ONE shared dedup set so each aliased
19047
+ * `Position` shifts exactly once (adjacent hoist/decl offsets are equal by
19048
+ * construction, so first-touch-wins is the correct line).
19049
+ *
19050
+ * Runs AFTER all diagnostics are collected (they captured true `.rzts` byte loc via
19051
+ * `nodeLoc`, which reads `node.start`/`node.end`, NOT `loc.{line,column}`), so the
19052
+ * R7 error-frame path is untouched (Pitfall 1). Never throws (D-04).
19053
+ */
19054
+ function normalizeSplicedEmitLines(body, blocks) {
19055
+ const nodeToBlock = /* @__PURE__ */ new Map();
19056
+ for (const b of blocks) for (const n of b.nodes) nodeToBlock.set(n, b);
19057
+ const measured = /* @__PURE__ */ new Set();
19058
+ const plan = [];
19059
+ let prevEnd = 0;
19060
+ let prevWasImport = false;
19061
+ let prevWasBlock = false;
19062
+ let prevBlockAnchorLine = 0;
19063
+ let hostRunOffset = 0;
19064
+ let seamAfterGap;
19065
+ let prevWasHostStmt = false;
19066
+ let prevHostOrigEnd = 0;
19067
+ let prevHostStmt = null;
19068
+ for (const stmt of body) {
19069
+ const block = nodeToBlock.get(stmt);
19070
+ if (block) {
19071
+ if (measured.has(block)) continue;
19072
+ measured.add(block);
19073
+ const firstNode = block.nodes.find((n) => n.loc);
19074
+ if (!firstNode?.loc) continue;
19075
+ const isImportBlock = t.isImportDeclaration(block.nodes[0]);
19076
+ let gap = isImportBlock && prevWasImport ? 1 : block.originalGap;
19077
+ let stampLeadingSeamStripped = false;
19078
+ if (!isImportBlock && prevWasHostStmt) {
19079
+ const beforeGap = block.anchorLine - prevHostOrigEnd;
19080
+ if (blockFirstEmitLine(firstNode) < firstNode.loc.start.line) {
19081
+ if (beforeGap >= 1 && beforeGap < gap) gap = beforeGap;
19082
+ if (prevHostStmt && isStrippedSigilDirective(prevHostStmt)) stampLeadingSeamStripped = true;
19083
+ }
19084
+ }
19085
+ const offset = (prevEnd > 0 ? prevEnd + gap : block.anchorLine) - blockFirstEmitLine(firstNode);
19086
+ let maxEnd = 0;
19087
+ for (const n of block.nodes) if (n.loc) maxEnd = Math.max(maxEnd, n.loc.end.line);
19088
+ prevEnd = maxEnd + offset;
19089
+ prevWasImport = isImportBlock;
19090
+ prevWasBlock = true;
19091
+ prevWasHostStmt = false;
19092
+ prevHostStmt = null;
19093
+ prevBlockAnchorLine = block.anchorLine;
19094
+ plan.push(stampLeadingSeamStripped ? {
19095
+ block,
19096
+ offset,
19097
+ leadingSeamPrevStripped: true
19098
+ } : {
19099
+ block,
19100
+ offset
19101
+ });
19102
+ continue;
19103
+ }
19104
+ if (stmt.loc) {
19105
+ const firstEmit = blockFirstEmitLine(stmt);
19106
+ let offset;
19107
+ if (prevWasBlock) {
19108
+ const afterGap = firstEmit - prevBlockAnchorLine;
19109
+ if (afterGap >= 2) {
19110
+ offset = prevEnd + afterGap - firstEmit;
19111
+ seamAfterGap = afterGap;
19112
+ } else offset = 0;
19113
+ hostRunOffset = offset;
19114
+ } else offset = hostRunOffset;
19115
+ if (offset !== 0) plan.push(seamAfterGap !== void 0 ? {
19116
+ hostNode: stmt,
19117
+ offset,
19118
+ afterGap: seamAfterGap
19119
+ } : {
19120
+ hostNode: stmt,
19121
+ offset
19122
+ });
19123
+ prevEnd = stmt.loc.end.line + offset;
19124
+ seamAfterGap = void 0;
19125
+ prevWasImport = t.isImportDeclaration(stmt);
19126
+ prevWasBlock = false;
19127
+ prevWasHostStmt = true;
19128
+ prevHostOrigEnd = stmt.loc.end.line;
19129
+ prevHostStmt = stmt;
19130
+ }
19131
+ }
19132
+ for (const block of blocks) {
19133
+ if (measured.has(block)) continue;
19134
+ measured.add(block);
19135
+ const firstNode = block.nodes.find((n) => n.loc);
19136
+ if (!firstNode?.loc) continue;
19137
+ plan.push({
19138
+ block,
19139
+ offset: block.anchorLine - blockFirstEmitLine(firstNode)
19140
+ });
19141
+ }
19142
+ const shifted = /* @__PURE__ */ new Set();
19143
+ for (const entry of plan) if ("block" in entry) {
19144
+ shiftBlock(entry.block, entry.offset, shifted);
19145
+ if (entry.leadingSeamPrevStripped) {
19146
+ const firstNode = entry.block.nodes.find((n) => n.loc);
19147
+ if (firstNode) firstNode.extra = {
19148
+ ...firstNode.extra ?? {},
19149
+ __rozieLeadingSeamPrevStripped: true
19150
+ };
19151
+ }
19152
+ } else {
19153
+ shiftHostNodeLines(entry.hostNode, entry.offset, shifted);
19154
+ if (entry.afterGap !== void 0) {
19155
+ const extra = entry.hostNode.extra ?? {};
19156
+ entry.hostNode.extra = {
19157
+ ...extra,
19158
+ __rozieAfterGap: entry.afterGap
19159
+ };
19160
+ }
19161
+ }
19162
+ }
19163
+ /**
19164
+ * Phase 56 (Shape-3, R4) — un-FLOAT a hoisted import's between-statement comment that
19165
+ * has separated from its owning declaration.
19166
+ *
19167
+ * `hoistSpecifier` copies a partial import's `trailingComments` onto the HOISTED import
19168
+ * node so a comment authored BETWEEN that import and the next surviving decl rides the
19169
+ * import to the host module-top (Phase 55 byte-identity). @babel/parser attaches such a
19170
+ * between-statement comment to BOTH neighbours: the import's `trailingComments` AND the
19171
+ * following decl's `leadingComments` (the SAME comment object). That copy is CORRECT
19172
+ * only when the import and its decl stay ADJACENT in the final body (e.g. HostC: the
19173
+ * hoisted `clamp` import is immediately followed by the `double` decl it shares the
19174
+ * comment with — the inline oracle ALSO has the comment on both neighbours, so
19175
+ * per-statement targets double it identically).
19176
+ *
19177
+ * When the spliced decl lands NON-adjacent to its hoisted import — a host statement
19178
+ * (e.g. a reassigned module-`let`) sits between them (HostH/the DataTable P15
19179
+ * `editTransition` after-`let` seam) — the import's copy FLOATS the comment to
19180
+ * module-top, away from the decl. The inline oracle keeps the comment ONLY on the decl
19181
+ * (its import is authored far above, never adjacent), so the float is a partial-vs-inline
19182
+ * divergence on ALL six targets (svelte/vue double it at the wrong place, react/angular/
19183
+ * lit drop it, solid dedups). This pass restores the inline placement by STRIPPING the
19184
+ * floated comment from the import's `trailingComments`; the decl keeps it on its
19185
+ * `leadingComments` (object identity preserved), and the per-target emitters then handle
19186
+ * the decl-attached comment exactly as they do for the inline oracle (svelte/vue's
19187
+ * `mirrorSpliceBoundaryComments` restores the doubling at the decl seam).
19188
+ *
19189
+ * Runs BEFORE `normalizeSplicedEmitLines` so the corrected attachment is in place when
19190
+ * the emit-line shift runs. A comment is treated as FLOATED iff it is shared (object
19191
+ * identity) with some body node's `leadingComments` whose owner is NOT the import's
19192
+ * immediate body-successor; a genuine import-only trailing comment (owned by no decl's
19193
+ * leadingComments) and the adjacent-decl case (HostC) are both left untouched. Never
19194
+ * throws (D-04).
19195
+ */
19196
+ function defloatHoistedImportComments(body, hoistImports) {
19197
+ if (hoistImports.length === 0) return;
19198
+ const hoistSet = new Set(hoistImports);
19199
+ const leadOwnerIndex = /* @__PURE__ */ new Map();
19200
+ for (let i = 0; i < body.length; i++) for (const c of body[i].leadingComments ?? []) if (!leadOwnerIndex.has(c)) leadOwnerIndex.set(c, i);
19201
+ for (let hi = 0; hi < body.length; hi++) {
19202
+ const node = body[hi];
19203
+ if (!hoistSet.has(node)) continue;
19204
+ const trailing = node.trailingComments;
19205
+ if (!trailing || trailing.length === 0) continue;
19206
+ const kept = trailing.filter((c) => {
19207
+ const owner = leadOwnerIndex.get(c);
19208
+ if (owner === void 0) return true;
19209
+ if (owner === hi + 1) return true;
19210
+ return false;
19211
+ });
19212
+ if (kept.length !== trailing.length) node.trailingComments = kept.length > 0 ? kept : null;
19213
+ }
19214
+ }
18699
19215
  /** Build the named imported-name list for a (host or nested) partial import. */
18700
19216
  function namedImports(imp) {
18701
19217
  const out = [];
@@ -18758,6 +19274,7 @@ function inlineResolvedPartial(absPath, importedNames, importStmt, importerFile,
18758
19274
  const nestedImports = [];
18759
19275
  /** local name -> nested partial resolved path + the imported name. */
18760
19276
  const nestedLocalMap = /* @__PURE__ */ new Map();
19277
+ const reExports = [];
18761
19278
  let order = 0;
18762
19279
  for (const stmt of partialBody) {
18763
19280
  if (t.isImportDeclaration(stmt)) {
@@ -18783,9 +19300,47 @@ function inlineResolvedPartial(absPath, importedNames, importStmt, importerFile,
18783
19300
  } else moduleImports.push(stmt);
18784
19301
  continue;
18785
19302
  }
19303
+ if (t.isExportNamedDeclaration(stmt) && !stmt.declaration && stmt.source) {
19304
+ const src = stmt.source;
19305
+ const declTypeOnly = stmt.exportKind === "type";
19306
+ for (const spec of stmt.specifiers) if (t.isExportSpecifier(spec)) {
19307
+ const exportedName = t.isIdentifier(spec.exported) ? spec.exported.name : spec.exported.value;
19308
+ const kind = declTypeOnly || spec.exportKind === "type" ? "type" : "value";
19309
+ const localId = spec.local;
19310
+ reExports.push({
19311
+ exportedName,
19312
+ source: src,
19313
+ kind,
19314
+ build: () => t.importSpecifier(t.identifier(exportedName), t.identifier(localId.name))
19315
+ });
19316
+ } else if (t.isExportNamespaceSpecifier(spec)) {
19317
+ const nsName = spec.exported.name;
19318
+ reExports.push({
19319
+ exportedName: nsName,
19320
+ source: src,
19321
+ kind: declTypeOnly ? "type" : "value",
19322
+ build: () => t.importNamespaceSpecifier(t.identifier(nsName))
19323
+ });
19324
+ }
19325
+ continue;
19326
+ }
19327
+ if (t.isExportAllDeclaration(stmt)) {
19328
+ ctx.diagnostics.push({
19329
+ code: RozieErrorCode.PARTIAL_UNSUPPORTED_IMPORT_FORM,
19330
+ severity: "error",
19331
+ 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}'\`).`,
19332
+ loc: nodeLoc(stmt),
19333
+ ...absPath ? { filename: absPath } : {},
19334
+ hint: `Replace the star re-export with explicit named re-exports: export { foo, bar } from '${stmt.source.value}'.`
19335
+ });
19336
+ continue;
19337
+ }
18786
19338
  let bare = null;
18787
- if (t.isExportNamedDeclaration(stmt) && stmt.declaration) bare = stmt.declaration;
18788
- else if (t.isVariableDeclaration(stmt) || t.isFunctionDeclaration(stmt) || t.isClassDeclaration(stmt) || t.isTSInterfaceDeclaration(stmt) || t.isTSTypeAliasDeclaration(stmt) || t.isTSEnumDeclaration(stmt) || t.isTSDeclareFunction(stmt)) bare = stmt;
19339
+ if (t.isExportNamedDeclaration(stmt) && stmt.declaration) {
19340
+ bare = stmt.declaration;
19341
+ const prevStmt = partialBody[partialBody.indexOf(stmt) - 1];
19342
+ if ((prevStmt && t.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;
19343
+ } else if (t.isVariableDeclaration(stmt) || t.isFunctionDeclaration(stmt) || t.isClassDeclaration(stmt) || t.isTSInterfaceDeclaration(stmt) || t.isTSTypeAliasDeclaration(stmt) || t.isTSEnumDeclaration(stmt) || t.isTSDeclareFunction(stmt)) bare = stmt;
18789
19344
  if (!bare) continue;
18790
19345
  const names = bindingNames(bare);
18791
19346
  if (names.length === 0) continue;
@@ -18838,8 +19393,9 @@ function inlineResolvedPartial(absPath, importedNames, importStmt, importerFile,
18838
19393
  }
18839
19394
  for (const imp of moduleImports) {
18840
19395
  const declKind = imp.importKind ?? "value";
18841
- for (const spec of imp.specifiers) if (referencedAll.has(spec.local.name)) hoistSpecifier(ctx, imp.source, declKind, spec);
19396
+ for (const spec of imp.specifiers) if (referencedAll.has(spec.local.name)) hoistSpecifier(ctx, imp.source, declKind, spec, imp);
18842
19397
  }
19398
+ for (const re of reExports) if (importedNames.includes(re.exportedName) || referencedAll.has(re.exportedName)) hoistSpecifier(ctx, re.source, re.kind, re.build());
18843
19399
  const out = [];
18844
19400
  for (const decl of includedSorted) {
18845
19401
  const collidingNames = [];
@@ -18929,6 +19485,7 @@ function inlineScriptPartials(file, opts = {}) {
18929
19485
  }
18930
19486
  }
18931
19487
  const splicedAbs = /* @__PURE__ */ new Set();
19488
+ const splicedBlocks = [];
18932
19489
  const newBody = [];
18933
19490
  for (const stmt of file.program.body) if (t.isImportDeclaration(stmt) && PARTIAL_EXT.test(stmt.source.value)) {
18934
19491
  const absPath = hostPartialAbs.get(stmt) ?? null;
@@ -18957,9 +19514,38 @@ function inlineScriptPartials(file, opts = {}) {
18957
19514
  if (splicedAbs.has(absPath)) continue;
18958
19515
  splicedAbs.add(absPath);
18959
19516
  const unionNames = hostPartialNames.get(absPath) ?? namedImports(stmt);
18960
- newBody.push(...inlineResolvedPartial(absPath, unionNames, stmt, fromFile, ctx));
19517
+ const hoistBefore = ctx.hoistImports.length;
19518
+ const spliced = inlineResolvedPartial(absPath, unionNames, stmt, fromFile, ctx);
19519
+ const newHoists = ctx.hoistImports.slice(hoistBefore);
19520
+ if (stmt.loc) {
19521
+ const anchorLine = stmt.loc.start.line;
19522
+ for (const hoist of newHoists) splicedBlocks.push({
19523
+ nodes: [hoist],
19524
+ anchorLine,
19525
+ originalGap: 1
19526
+ });
19527
+ let runStart = 0;
19528
+ let prevRunLastNode = null;
19529
+ while (runStart < spliced.length) {
19530
+ const fname = spliced[runStart].loc?.filename ?? null;
19531
+ let runEnd = runStart + 1;
19532
+ while (runEnd < spliced.length && (spliced[runEnd].loc?.filename ?? null) === fname) runEnd++;
19533
+ const nodes = spliced.slice(runStart, runEnd);
19534
+ const originalGap = measureOriginalGap(nodes.find((n) => n.loc) ?? nodes[0], newHoists, prevRunLastNode);
19535
+ splicedBlocks.push({
19536
+ nodes,
19537
+ anchorLine,
19538
+ originalGap
19539
+ });
19540
+ prevRunLastNode = nodes[nodes.length - 1] ?? prevRunLastNode;
19541
+ runStart = runEnd;
19542
+ }
19543
+ }
19544
+ newBody.push(...spliced);
18961
19545
  } else newBody.push(stmt);
18962
19546
  file.program.body = [...ctx.hoistImports, ...newBody];
19547
+ defloatHoistedImportComments(file.program.body, ctx.hoistImports);
19548
+ normalizeSplicedEmitLines(file.program.body, splicedBlocks);
18963
19549
  return {
18964
19550
  ast: file,
18965
19551
  diagnostics
@@ -21266,6 +21852,27 @@ function rewriteRozieIdentifiers$4(program, ir, diagnostics) {
21266
21852
  loc: ref.sourceLoc
21267
21853
  });
21268
21854
  normalizeModelAccessor$4(program);
21855
+ const vueProtected = new Set((ir.expose ?? []).map((e) => e.name));
21856
+ deconflictGeneratedSymbols(program, [
21857
+ {
21858
+ names: modelProps,
21859
+ trigger: {
21860
+ kind: "accessor",
21861
+ accessor: "$props"
21862
+ }
21863
+ },
21864
+ {
21865
+ names: dataNames,
21866
+ trigger: {
21867
+ kind: "accessor",
21868
+ accessor: "$data"
21869
+ }
21870
+ },
21871
+ {
21872
+ names: computedNames,
21873
+ trigger: { kind: "bare-read" }
21874
+ }
21875
+ ], vueProtected);
21269
21876
  traverse$17(program, {
21270
21877
  MemberExpression(path) {
21271
21878
  /* v8 ignore next -- defensive: MemberExpression nodes do not occur in TS type position */
@@ -21786,6 +22393,80 @@ function arrowBody$4(body) {
21786
22393
  return genCode$9(t.arrowFunctionExpression([], body));
21787
22394
  }
21788
22395
  /**
22396
+ * Phase 55-04 (literal byte-identity) — reproduce the inline-authored comment
22397
+ * doubling at a script-partial splice boundary.
22398
+ *
22399
+ * In an inline-authored `<script>`, a comment block BETWEEN two statements is
22400
+ * attached by `@babel/parser` to BOTH neighbours (the earlier statement's
22401
+ * `trailingComments` AND the later statement's `leadingComments`). The `.rzts`
22402
+ * script-partial splice attaches the boundary banner ONLY to the spliced node's
22403
+ * `leadingComments` — the preceding statement lives in a different source file and
22404
+ * carries no matching trailing comment. Vue emits the residual body one statement
22405
+ * at a time (`stmts.map((s) => genCode(s)).join('\n')`), so each `genCode` call has
22406
+ * its own comment-dedup set: in the inline form the boundary banner therefore
22407
+ * prints TWICE (once as the previous statement's trailing, once as the next
22408
+ * statement's leading), with a blank line after the previous closing brace.
22409
+ * Re-mirroring the spliced node's leading comments back onto the preceding
22410
+ * statement's trailing comments restores that byte-for-byte.
22411
+ *
22412
+ * Fires at a genuine splice boundary in EITHER direction (Phase 56 R1 broadened
22413
+ * the trigger): the CURRENT statement is spliced (`cur.extra.__roziePartialOrigin`
22414
+ * — the Phase 55 leading seam) OR the PREVIOUS statement is spliced and CUR is an
22415
+ * inline host successor carrying the leading comment (the R1 TRAILING seam). In
22416
+ * both cases CUR's leading comments are mirrored onto PREV's trailing comments
22417
+ * UNLESS already shared (within-partial statement pairs share the same comment
22418
+ * objects; host-only pairs — neither node spliced — are left exactly as authored).
22419
+ * `normalizeSplicedEmitLines` (core) has already anchored the seam spacing, so the
22420
+ * mirrored trailing copy spaces correctly.
22421
+ */
22422
+ function mirrorSpliceBoundaryComments$2(stmts) {
22423
+ for (let i = 1; i < stmts.length; i++) {
22424
+ const cur = stmts[i];
22425
+ const prev = stmts[i - 1];
22426
+ const curExtra = cur.extra;
22427
+ const prevExtra = prev.extra;
22428
+ const curSpliced = curExtra?.__roziePartialOrigin !== void 0;
22429
+ const prevSpliced = prevExtra?.__roziePartialOrigin !== void 0;
22430
+ if (!curSpliced && !prevSpliced) continue;
22431
+ const lead = cur.leadingComments;
22432
+ const prevTrail = prev.trailingComments;
22433
+ if (curSpliced && !prevSpliced && (!lead || lead.length === 0) && prevTrail && prevTrail.length > 0) {
22434
+ cur.leadingComments = [...prevTrail];
22435
+ continue;
22436
+ }
22437
+ if (!lead || lead.length === 0) continue;
22438
+ if (curSpliced && curExtra?.__rozieLeadingSeamPrevStripped === true) continue;
22439
+ const lastLead = lead[lead.length - 1];
22440
+ if (prevTrail && prevTrail.length > 0 && prevTrail[prevTrail.length - 1] === lastLead) continue;
22441
+ let toAppend = lead;
22442
+ if (prevSpliced && !curSpliced) {
22443
+ const afterGap = curExtra?.__rozieAfterGap;
22444
+ const anchorLine = (prev.loc?.end.line ?? 0) + (typeof afterGap === "number" ? afterGap : 1);
22445
+ const baseLine = lead[0]?.loc?.start.line;
22446
+ toAppend = lead.map((c) => {
22447
+ if (!c.loc) return { ...c };
22448
+ const startLine = baseLine === void 0 ? anchorLine : anchorLine + (c.loc.start.line - baseLine);
22449
+ const endLine = baseLine === void 0 ? anchorLine : anchorLine + (c.loc.end.line - baseLine);
22450
+ return {
22451
+ ...c,
22452
+ loc: {
22453
+ ...c.loc,
22454
+ start: {
22455
+ ...c.loc.start,
22456
+ line: startLine
22457
+ },
22458
+ end: {
22459
+ ...c.loc.end,
22460
+ line: endLine
22461
+ }
22462
+ }
22463
+ };
22464
+ });
22465
+ }
22466
+ prev.trailingComments = [...prevTrail ?? [], ...toAppend];
22467
+ }
22468
+ }
22469
+ /**
21789
22470
  * Render a PropTypeAnnotation as a TypeScript type string.
21790
22471
  *
21791
22472
  * Reference examples produce these patterns:
@@ -22240,6 +22921,7 @@ function emitResidualScriptBody$1(clonedProgram, consumedLifecycleIndices) {
22240
22921
  }
22241
22922
  stmts.push(stmt);
22242
22923
  }
22924
+ mirrorSpliceBoundaryComments$2(stmts);
22243
22925
  return {
22244
22926
  code: stmts.map((s) => genCode$9(s)).join("\n"),
22245
22927
  stmts
@@ -24881,7 +25563,8 @@ function remapping$1(input, loader, options) {
24881
25563
  * (the "parent" map) via @ampproject/remapping. Result: a single Source Map
24882
25564
  * v3 that resolves emitted-output positions all the way back to .rozie.
24883
25565
  *
24884
- * Used by all 4 target compose.ts files. Replaces the per-target
25566
+ * Used by all 6 target compose.ts files (react/vue/svelte/solid/lit/angular).
25567
+ * Replaces the per-target
24885
25568
  * single-segment re-projection hack (Phase 3 WR-01 / Phase 4 Plan 04-05
24886
25569
  * Task 1) removed in P2 (D-109).
24887
25570
  *
@@ -24894,7 +25577,114 @@ function remapping$1(input, loader, options) {
24894
25577
  * @experimental — shape may change before v1.0
24895
25578
  */
24896
25579
  const remapping = typeof remapping$1 === "function" ? remapping$1 : remapping$1.default;
25580
+ /** Source-file extension test: a spliced script partial origin (Phase 54/55). */
25581
+ function isPartialSource(src) {
25582
+ return !!src && (src.endsWith(".rzts") || src.endsWith(".rzjs"));
25583
+ }
25584
+ /**
25585
+ * Recover a partial source's constant emit-line offset from the table. The table
25586
+ * is keyed by the absolute `loc.filename` stashed at splice time; a child map's
25587
+ * `sources` entry is the same absolute path, so an EXACT lookup is tried first.
25588
+ *
25589
+ * WR-03: the defensive fallback is restricted to a path-SEGMENT-BOUNDARY match
25590
+ * (`src.endsWith('/' + file) || file.endsWith('/' + src)`) so a partial whose path
25591
+ * is a bare substring of another's (e.g. `xa.rzts` vs `a.rzts`) never collides, and
25592
+ * — critically — if TWO OR MORE table entries match the boundary test (an ambiguous
25593
+ * bare basename shared across nested dirs) the lookup BAILS (returns `undefined`)
25594
+ * rather than mis-attributing one partial's offset to another. Returns `undefined`
25595
+ * when no unambiguous offset is known (mapping left as-is — D-04).
25596
+ */
25597
+ function lookupPartialOffset(src, offsets) {
25598
+ if (!src || !offsets || offsets.size === 0) return void 0;
25599
+ const exact = offsets.get(src);
25600
+ if (exact !== void 0) return exact;
25601
+ let match;
25602
+ let matchCount = 0;
25603
+ for (const [file, off] of offsets) if (src.endsWith(`/${file}`) || file.endsWith(`/${src}`)) {
25604
+ match = off;
25605
+ matchCount++;
25606
+ }
25607
+ return matchCount === 1 ? match : void 0;
25608
+ }
25609
+ /**
25610
+ * Phase 55 Plan 03 (SC-2) — build the per-partial-file constant emit-line offset
25611
+ * table from the lowered script AST.
25612
+ *
25613
+ * Plan 02 shifted each spliced node's `loc.start.line` to a host-contiguous emit
25614
+ * value while stashing the true `.rzts` origin on `extra.__roziePartialOrigin`.
25615
+ * The per-partial offset is therefore the (constant) delta
25616
+ * `loc.start.line − __roziePartialOrigin.line`; subtracting it from a mapping's
25617
+ * original line restores the `.rzts`-local line. The offset is constant per
25618
+ * spliced block (Plan 02 anchored it), so one entry per partial `source` file
25619
+ * suffices — the first stashed node per file wins.
25620
+ *
25621
+ * Walks `scriptAst.program.body` (the spliced statements sit at top level) and
25622
+ * their attached leading/trailing/inner comments (comments carry the stash
25623
+ * directly). Never throws (D-04): a missing/zero stash is skipped, a null AST
25624
+ * yields an empty table.
25625
+ *
25626
+ * IN-02: the `innerComments` loop mirrors `shiftAttachedComments`
25627
+ * (inlineScriptPartials.ts) which walks leading/trailing/inner. In practice the
25628
+ * offset is per-file and first-stash-wins, so a leading/trailing comment or the
25629
+ * decl itself almost always supplies it — the inner loop is additive and harmless,
25630
+ * kept only so the stash-channel scan is symmetric with where stashes are written.
25631
+ */
25632
+ function buildPartialLineOffsets(scriptAst) {
25633
+ const out = /* @__PURE__ */ new Map();
25634
+ const body = scriptAst?.program?.body;
25635
+ if (!body) return out;
25636
+ const record = (carrier, locLine) => {
25637
+ const origin = carrier?.__roziePartialOrigin;
25638
+ if (!origin || locLine === void 0) return;
25639
+ const key = origin.filename;
25640
+ if (!key || out.has(key)) return;
25641
+ out.set(key, locLine - origin.line);
25642
+ };
25643
+ for (const node of body) {
25644
+ const extra = node.extra;
25645
+ record(extra, node.loc?.start.line);
25646
+ for (const c of node.leadingComments ?? []) record(c, c.loc?.start.line);
25647
+ for (const c of node.trailingComments ?? []) record(c, c.loc?.start.line);
25648
+ for (const c of node.innerComments ?? []) record(c, c.loc?.start.line);
25649
+ }
25650
+ return out;
25651
+ }
24897
25652
  /**
25653
+ * Phase 55 Plan 03 — per-target script-map flow survey (Task 1, Assumption A3).
25654
+ *
25655
+ * SURVEY RESULT (confirmed on disk 2026-06-20):
25656
+ *
25657
+ * 1. CONVERGENCE — all SIX per-target `sourcemap/compose.ts` wrappers
25658
+ * (react/vue/svelte/solid/lit/angular) import THIS `composeMaps` and route
25659
+ * their @babel/generator <script> child map through it as `children[0]`. No
25660
+ * target hand-rolls a second map merge; `composeMaps` is the single
25661
+ * convergence point, so the spliced-line restore arithmetic lives here ONCE
25662
+ * (D-03/D-05 uniformity) — never in a per-target wrapper or `emitScript.ts`.
25663
+ *
25664
+ * 2. ACTIVE MAP PATH — the five map-EMITTING targets (react/vue/svelte/solid/
25665
+ * angular) all pass a defined `userCodeLineOffset`, so each takes the
25666
+ * `userCodeLineOffset` branch below (step 3). Empirically, that branch
25667
+ * discarded the child map's per-node `sources` (it hardcoded `[opts.filename]`),
25668
+ * collapsing a spliced node's `.rzts` origin to the host `.rozie` — which is
25669
+ * exactly why the SC-2 line-fidelity smoke test was red/skipped. The restore
25670
+ * therefore lives in step 3. The step-4 remapping path is NOT exercised by
25671
+ * partials (every map-emitting target passes `userCodeLineOffset`), so it is
25672
+ * deliberately left untouched rather than carrying speculative dead code.
25673
+ *
25674
+ * 3. TABLE BUILD SITE — each of those five `emitXxx.ts` holds the lowered `ir`
25675
+ * (whose `ir.setupBody.scriptProgram` script AST carries the spliced nodes'
25676
+ * `extra.__roziePartialOrigin` stashes from Plan 02). Each builds the
25677
+ * `partialLineOffsets` table via {@link buildPartialLineOffsets} and threads
25678
+ * it into the `ComposeOpts` it already constructs. The table derives from the
25679
+ * IR, NOT from generator output — so NO `emitScript.ts` (the @babel/generator
25680
+ * caller) is touched (D-03).
25681
+ *
25682
+ * 4. LIT EXCEPTION — `packages/targets/lit/src/emitLit.ts` returns `map: null`
25683
+ * in v1 and does NOT call `composeSourceMap` (the wrapper is implemented but
25684
+ * dead until Phase 7 wires it). Lit therefore has no build+set site; its
25685
+ * wrapper still receives the `partialLineOffsets` field for forward-compat so
25686
+ * the restore flows automatically once Lit's map path is connected.
25687
+ *
24898
25688
  * Merge the shell map + zero-or-more child maps into a single Source Map v3
24899
25689
  * anchored to .rozie. Defensively re-asserts sources/sourcesContent per
24900
25690
  * Pitfall 2 mitigation.
@@ -24913,6 +25703,27 @@ function composeMaps(opts) {
24913
25703
  }
24914
25704
  if (opts.userCodeLineOffset !== void 0) {
24915
25705
  const childMap = opts.children[0].map;
25706
+ const childSources = childMap.sources ?? [];
25707
+ if (childSources.some((s) => isPartialSource(s)) && !!opts.partialLineOffsets && opts.partialLineOffsets.size > 0) {
25708
+ const decoded = decode(childMap.mappings);
25709
+ for (const line of decoded) for (const seg of line) if (seg.length >= 4) {
25710
+ const full = seg;
25711
+ const src = childSources[full[1]];
25712
+ const off = lookupPartialOffset(src, opts.partialLineOffsets);
25713
+ if (off !== void 0) full[2] = Math.max(0, full[2] - off);
25714
+ }
25715
+ const restoredMappings = encode(decoded);
25716
+ const sources = childSources.slice();
25717
+ const sourcesContent = sources.map((s, i) => s === opts.filename ? opts.source : childMap.sourcesContent?.[i] ?? null);
25718
+ return {
25719
+ version: 3,
25720
+ file: `${opts.filename}${opts.fileExt}`,
25721
+ sources,
25722
+ sourcesContent,
25723
+ names: childMap.names ?? [],
25724
+ mappings: ";".repeat(opts.userCodeLineOffset) + restoredMappings
25725
+ };
25726
+ }
24916
25727
  return {
24917
25728
  version: 3,
24918
25729
  file: `${opts.filename}${opts.fileExt}`,
@@ -24946,7 +25757,8 @@ function composeSourceMap$4(ms, opts) {
24946
25757
  outputOffset: opts.scriptOutputOffset
24947
25758
  }] : [],
24948
25759
  fileExt: ".vue",
24949
- userCodeLineOffset: opts.userCodeLineOffset
25760
+ userCodeLineOffset: opts.userCodeLineOffset,
25761
+ partialLineOffsets: opts.partialLineOffsets
24950
25762
  });
24951
25763
  }
24952
25764
  //#endregion
@@ -25131,7 +25943,8 @@ function emitVue(ir, opts = {}) {
25131
25943
  source: opts.source,
25132
25944
  scriptMap: shellScriptMap,
25133
25945
  scriptOutputOffset,
25134
- userCodeLineOffset
25946
+ userCodeLineOffset,
25947
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
25135
25948
  }) : null,
25136
25949
  diagnostics: [
25137
25950
  ...scriptDiags,
@@ -29072,7 +29885,8 @@ function composeClassName(attrs, ctx) {
29072
29885
  });
29073
29886
  else segments.push({
29074
29887
  kind: "plainBinding",
29075
- expr: a.expression
29888
+ expr: a.expression,
29889
+ wrapForDisplay: a.wrapForDisplay
29076
29890
  });
29077
29891
  else if (a.kind === "spreadBinding") throw new Error(`React target: spreadBinding not valid in class array context (Phase 14).`);
29078
29892
  else segments.push({
@@ -29094,6 +29908,10 @@ function composeClassName(attrs, ctx) {
29094
29908
  const seg = segments[0];
29095
29909
  const staticSegs = decomposeStaticClassExpr(seg.expr);
29096
29910
  if (staticSegs) return renderInterpolatedClass(staticSegs, ctx);
29911
+ if (seg.wrapForDisplay) {
29912
+ ctx.collectors.runtime.add("clsx");
29913
+ return `clsx(${renderExpr$1(seg.expr, ir)})`;
29914
+ }
29097
29915
  return renderExpr$1(seg.expr, ir);
29098
29916
  }
29099
29917
  if (segments.length === 1 && segments[0].kind === "interpolated") {
@@ -34928,7 +35746,8 @@ function composeSourceMap$3(ms, opts) {
34928
35746
  outputOffset: opts.scriptOutputOffset
34929
35747
  }] : [],
34930
35748
  fileExt: ".tsx",
34931
- userCodeLineOffset: opts.userCodeLineOffset
35749
+ userCodeLineOffset: opts.userCodeLineOffset,
35750
+ partialLineOffsets: opts.partialLineOffsets
34932
35751
  });
34933
35752
  }
34934
35753
  //#endregion
@@ -35031,7 +35850,8 @@ function emitReact(ir, opts = {}) {
35031
35850
  source: opts.source,
35032
35851
  scriptMap: shellScriptMap,
35033
35852
  scriptOutputOffset,
35034
- userCodeLineOffset
35853
+ userCodeLineOffset,
35854
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
35035
35855
  }) : null,
35036
35856
  diagnostics: [
35037
35857
  ...scriptDiags,
@@ -36025,6 +36845,80 @@ function arrowBody$2(body) {
36025
36845
  return genCode$5(t.arrowFunctionExpression([], body));
36026
36846
  }
36027
36847
  /**
36848
+ * Phase 55-04 (literal byte-identity) — reproduce the inline-authored comment
36849
+ * doubling at a script-partial splice boundary.
36850
+ *
36851
+ * In an inline-authored `<script>`, a comment block BETWEEN two statements is
36852
+ * attached by `@babel/parser` to BOTH neighbours (the earlier statement's
36853
+ * `trailingComments` AND the later statement's `leadingComments`). The `.rzts`
36854
+ * script-partial splice attaches the boundary banner ONLY to the spliced node's
36855
+ * `leadingComments` — the preceding statement lives in a different source file and
36856
+ * carries no matching trailing comment. Svelte emits the residual body one
36857
+ * statement at a time (`stmts.map((s) => genCode(s)).join('\n')`), so each
36858
+ * `genCode` call has its own comment-dedup set: in the inline form the boundary
36859
+ * banner therefore prints TWICE (once as the previous statement's trailing, once
36860
+ * as the next statement's leading), with a blank line after the previous closing
36861
+ * brace. Re-mirroring the spliced node's leading comments back onto the preceding
36862
+ * statement's trailing comments restores that byte-for-byte.
36863
+ *
36864
+ * Fires at a genuine splice boundary in EITHER direction (Phase 56 R1 broadened
36865
+ * the trigger): the CURRENT statement is spliced (`cur.extra.__roziePartialOrigin`
36866
+ * — the Phase 55 leading seam) OR the PREVIOUS statement is spliced and CUR is an
36867
+ * inline host successor carrying the leading comment (the R1 TRAILING seam). In
36868
+ * both cases CUR's leading comments are mirrored onto PREV's trailing comments
36869
+ * UNLESS already shared (within-partial statement pairs share the same comment
36870
+ * objects; host-only pairs — neither node spliced — are left exactly as authored).
36871
+ * `normalizeSplicedEmitLines` (core) has already anchored the seam spacing, so the
36872
+ * mirrored trailing copy spaces correctly.
36873
+ */
36874
+ function mirrorSpliceBoundaryComments$1(stmts) {
36875
+ for (let i = 1; i < stmts.length; i++) {
36876
+ const cur = stmts[i];
36877
+ const prev = stmts[i - 1];
36878
+ const curExtra = cur.extra;
36879
+ const prevExtra = prev.extra;
36880
+ const curSpliced = curExtra?.__roziePartialOrigin !== void 0;
36881
+ const prevSpliced = prevExtra?.__roziePartialOrigin !== void 0;
36882
+ if (!curSpliced && !prevSpliced) continue;
36883
+ const lead = cur.leadingComments;
36884
+ const prevTrail = prev.trailingComments;
36885
+ if (curSpliced && !prevSpliced && (!lead || lead.length === 0) && prevTrail && prevTrail.length > 0) {
36886
+ cur.leadingComments = [...prevTrail];
36887
+ continue;
36888
+ }
36889
+ if (!lead || lead.length === 0) continue;
36890
+ if (curSpliced && curExtra?.__rozieLeadingSeamPrevStripped === true) continue;
36891
+ const lastLead = lead[lead.length - 1];
36892
+ if (prevTrail && prevTrail.length > 0 && prevTrail[prevTrail.length - 1] === lastLead) continue;
36893
+ let toAppend = lead;
36894
+ if (prevSpliced && !curSpliced) {
36895
+ const afterGap = curExtra?.__rozieAfterGap;
36896
+ const anchorLine = (prev.loc?.end.line ?? 0) + (typeof afterGap === "number" ? afterGap : 1);
36897
+ const baseLine = lead[0]?.loc?.start.line;
36898
+ toAppend = lead.map((c) => {
36899
+ if (!c.loc) return { ...c };
36900
+ const startLine = baseLine === void 0 ? anchorLine : anchorLine + (c.loc.start.line - baseLine);
36901
+ const endLine = baseLine === void 0 ? anchorLine : anchorLine + (c.loc.end.line - baseLine);
36902
+ return {
36903
+ ...c,
36904
+ loc: {
36905
+ ...c.loc,
36906
+ start: {
36907
+ ...c.loc.start,
36908
+ line: startLine
36909
+ },
36910
+ end: {
36911
+ ...c.loc.end,
36912
+ line: endLine
36913
+ }
36914
+ }
36915
+ };
36916
+ });
36917
+ }
36918
+ prev.trailingComments = [...prevTrail ?? [], ...toAppend];
36919
+ }
36920
+ }
36921
+ /**
36028
36922
  * Render a PropTypeAnnotation as a TypeScript type string. Mirrors the Vue
36029
36923
  * target's renderType helper.
36030
36924
  */
@@ -36463,6 +37357,7 @@ function emitResidualScriptBody(clonedProgram, consumedLifecycleIndices, exposeN
36463
37357
  }
36464
37358
  stmts.push(stmt);
36465
37359
  }
37360
+ mirrorSpliceBoundaryComments$1(stmts);
36466
37361
  return {
36467
37362
  code: stmts.map((s) => {
36468
37363
  if (exposeNames.size > 0 && isExposedTopLevelDecl(s, exposeNames)) {
@@ -37126,6 +38021,14 @@ function emitSingleAttr$1(attr, ctx) {
37126
38021
  if (styleObjectLowered !== null) return styleObjectLowered;
37127
38022
  const expr = rewriteTemplateExpression$3(attr.expression, ir);
37128
38023
  const outName = resolveAttrName(attr.name, ctx);
38024
+ if (attr.name === "style") {
38025
+ ctx.runtimeImports?.add("rozieStyle");
38026
+ return `${outName}={rozieStyle(${expr})}`;
38027
+ }
38028
+ if (attr.name === "class" && attr.wrapForDisplay && !t.isObjectExpression(attr.expression) && !t.isTemplateLiteral(attr.expression) && shouldWrapSvelteAttrBinding(attr.name, attr.expression, ctx)) {
38029
+ ctx.runtimeImports?.add("rozieClass");
38030
+ return `${outName}={rozieClass(${expr})}`;
38031
+ }
37129
38032
  if (attr.wrapForDisplay && shouldWrapSvelteAttrBinding(attr.name, attr.expression, ctx)) {
37130
38033
  ctx.runtimeImports?.add("rozieAttr");
37131
38034
  return `${outName}={rozieAttr(${expr})}`;
@@ -37153,12 +38056,16 @@ function emitSingleAttr$1(attr, ctx) {
37153
38056
  * Convert a single AttributeBinding into a JS expression string suitable for
37154
38057
  * inclusion in an array (used by class/style merge below).
37155
38058
  */
37156
- function attrToArraySegment$1(attr, ir, runtimeImports) {
38059
+ function attrToArraySegment$1(attr, ir, runtimeImports, isClass = false) {
37157
38060
  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).`);
37158
38061
  if (attr.kind === "static") return JSON.stringify(attr.value);
37159
38062
  if (attr.kind === "binding") {
37160
38063
  const code = rewriteTemplateExpression$3(attr.expression, ir);
37161
38064
  if (attr.wrapForDisplay && !t.isObjectExpression(attr.expression)) {
38065
+ if (isClass && !t.isTemplateLiteral(attr.expression)) {
38066
+ runtimeImports?.add("rozieClass");
38067
+ return `rozieClass(${code})`;
38068
+ }
37162
38069
  runtimeImports?.add("rozieDisplay");
37163
38070
  return `rozieDisplay(${code})`;
37164
38071
  }
@@ -37249,7 +38156,7 @@ function emitAttributes$2(attrs, ctx) {
37249
38156
  if (synthetic) merged.push(synthetic);
37250
38157
  } else if (src.name === a.name && !isLiteralStyleObjectBinding(src)) merged.push(src);
37251
38158
  for (const x of merged) consumed.add(x);
37252
- const segments = merged.map((x) => attrToArraySegment$1(x, ctx.ir, ctx.runtimeImports));
38159
+ const segments = merged.map((x) => attrToArraySegment$1(x, ctx.ir, ctx.runtimeImports, a.name === "class"));
37253
38160
  if (hasOpaqueMerge) for (const readExpr of opaqueSpreadClassReads) segments.push(readExpr);
37254
38161
  if (hasOpaqueMerge) deferredMergedClassStyle.push(`${a.name}={[${segments.join(", ")}]}`);
37255
38162
  else out.push(`${a.name}={[${segments.join(", ")}]}`);
@@ -38741,7 +39648,8 @@ function composeSourceMap$2(ms, opts) {
38741
39648
  outputOffset: opts.scriptOutputOffset
38742
39649
  }] : [],
38743
39650
  fileExt: ".svelte",
38744
- userCodeLineOffset: opts.userCodeLineOffset
39651
+ userCodeLineOffset: opts.userCodeLineOffset,
39652
+ partialLineOffsets: opts.partialLineOffsets
38745
39653
  });
38746
39654
  }
38747
39655
  //#endregion
@@ -38845,7 +39753,8 @@ function emitSvelte(ir, opts = {}) {
38845
39753
  source: opts.source,
38846
39754
  scriptMap: shellScriptMap,
38847
39755
  scriptOutputOffset,
38848
- userCodeLineOffset
39756
+ userCodeLineOffset,
39757
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
38849
39758
  }) : null,
38850
39759
  diagnostics: [
38851
39760
  ...scriptDiags,
@@ -40116,7 +41025,15 @@ function buildSlotCtx(slot) {
40116
41025
  */
40117
41026
  function buildNgTemplateContextGuard(componentName, slots) {
40118
41027
  if (slots.length === 0) return null;
40119
- const unionType = slots.map((s) => slotCtxName(s.name)).join(" | ");
41028
+ const seenCtxNames = /* @__PURE__ */ new Set();
41029
+ const ctxNames = [];
41030
+ for (const s of slots) {
41031
+ const ctxName = slotCtxName(s.name);
41032
+ if (seenCtxNames.has(ctxName)) continue;
41033
+ seenCtxNames.add(ctxName);
41034
+ ctxNames.push(ctxName);
41035
+ }
41036
+ const unionType = ctxNames.join(" | ");
40120
41037
  return [
40121
41038
  `static ngTemplateContextGuard(`,
40122
41039
  ` _dir: ${componentName},`,
@@ -40908,7 +41825,10 @@ function emitScript$2(ir, opts = {}) {
40908
41825
  const interfaceDecls = [];
40909
41826
  for (const typeDecl of hoistedTypeDecls) interfaceDecls.push(genCode$3(typeDecl));
40910
41827
  const slotFieldDecls = [];
41828
+ const seenSlotNames = /* @__PURE__ */ new Set();
40911
41829
  for (const slot of ir.slots) {
41830
+ if (seenSlotNames.has(slot.name)) continue;
41831
+ seenSlotNames.add(slot.name);
40912
41832
  const ctx = buildSlotCtx(slot);
40913
41833
  interfaceDecls.push(ctx.interfaceDecl);
40914
41834
  slotFieldDecls.push(ctx.fieldDecl);
@@ -41871,6 +42791,7 @@ function shouldWrapAttrBinding$1(name, expr, ctx, elementTagName) {
41871
42791
  if (BOOLEAN_HTML_ATTRS$1.has(name.toLowerCase())) return false;
41872
42792
  if ((name === "value" || name === "checked") && FORM_INPUT_TAGS$2.has(elementTagName.toLowerCase())) return false;
41873
42793
  if (name === "style") return false;
42794
+ if (name === "class") return false;
41874
42795
  if (t.isObjectExpression(expr)) return false;
41875
42796
  return true;
41876
42797
  }
@@ -44639,7 +45560,8 @@ function composeSourceMap$1(ms, opts) {
44639
45560
  outputOffset: opts.scriptOutputOffset
44640
45561
  }] : [],
44641
45562
  fileExt: ".ts",
44642
- userCodeLineOffset: opts.userCodeLineOffset
45563
+ userCodeLineOffset: opts.userCodeLineOffset,
45564
+ partialLineOffsets: opts.partialLineOffsets
44643
45565
  });
44644
45566
  }
44645
45567
  //#endregion
@@ -45025,7 +45947,8 @@ function emitAngular(ir, opts = {}) {
45025
45947
  source: opts.source,
45026
45948
  scriptMap: shellScriptMap,
45027
45949
  scriptOutputOffset,
45028
- userCodeLineOffset
45950
+ userCodeLineOffset,
45951
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
45029
45952
  }) : null,
45030
45953
  diagnostics: [
45031
45954
  ...scriptResult.diagnostics,
@@ -45969,6 +46892,56 @@ function tryHoistArrowToFunction(stmt) {
45969
46892
  return t.inherits(fn, stmt);
45970
46893
  }
45971
46894
  /**
46895
+ * Phase 55-04 (literal byte-identity) — reproduce the inline-authored comment
46896
+ * doubling at a script-partial splice boundary.
46897
+ *
46898
+ * In an inline-authored `<script>`, a comment block BETWEEN two statements is
46899
+ * attached by `@babel/parser` to BOTH neighbours (the earlier statement's
46900
+ * `trailingComments` AND the later statement's `leadingComments`). The `.rzts`
46901
+ * script-partial splice instead attaches the boundary banner ONLY to the spliced
46902
+ * node's `leadingComments` — the preceding statement lives in a different source
46903
+ * file and so carries no matching trailing comment. Re-mirroring the spliced
46904
+ * node's leading comments back onto the preceding statement's trailing comments
46905
+ * restores the inline form byte-for-byte:
46906
+ * - whole-program generation (Solid here) prints the (deduped) banner once AND
46907
+ * gets the boundary blank line from `@babel/generator`'s `printJoin`
46908
+ * `_lastCommentLine` path — a trailing comment on the previous statement is
46909
+ * exactly what triggers the loc-delta blank-line insertion; and
46910
+ * - per-statement generation (Vue/Svelte) doubles the banner (one copy as the
46911
+ * previous statement's trailing, one as the next statement's leading).
46912
+ *
46913
+ * Fires ONLY at a genuine splice boundary: the current statement carries
46914
+ * `extra.__roziePartialOrigin` AND its leading comments are not ALREADY shared as
46915
+ * the previous statement's trailing comments (within-partial statement pairs
46916
+ * already share the same comment objects; host-only pairs are left exactly as
46917
+ * authored). MUST run before the arrow→function hoist — `t.inherits` does not
46918
+ * copy `extra`, so the spliced marker is only visible on the original nodes.
46919
+ */
46920
+ function mirrorSpliceBoundaryComments(stmts) {
46921
+ for (let i = 1; i < stmts.length; i++) {
46922
+ const cur = stmts[i];
46923
+ const prev = stmts[i - 1];
46924
+ const lead = cur.leadingComments;
46925
+ if (!lead || lead.length === 0) continue;
46926
+ const extra = cur.extra;
46927
+ const curSpliced = extra?.__roziePartialOrigin !== void 0;
46928
+ const prevSpliced = prev.extra?.__roziePartialOrigin !== void 0;
46929
+ if (!curSpliced) {
46930
+ if (prevSpliced && extra?.__rozieAfterGap !== void 0) {
46931
+ const prevTrail = prev.trailingComments;
46932
+ const lastLead = lead[lead.length - 1];
46933
+ if (prevTrail && prevTrail.length > 0 && prevTrail[prevTrail.length - 1] === lastLead) continue;
46934
+ prev.trailingComments = [...prevTrail ?? [], ...lead];
46935
+ }
46936
+ continue;
46937
+ }
46938
+ const prevTrail = prev.trailingComments;
46939
+ const lastLead = lead[lead.length - 1];
46940
+ if (prevTrail && prevTrail.length > 0 && prevTrail[prevTrail.length - 1] === lastLead) continue;
46941
+ prev.trailingComments = [...prevTrail ?? [], ...lead];
46942
+ }
46943
+ }
46944
+ /**
45972
46945
  * WR-01 ROOT CAUSE 2 — re-project an author function-type annotation written
45973
46946
  * on a `VariableDeclarator` `id` (`const f: (e: E) => R = …`) onto a rebuilt
45974
46947
  * `FunctionDeclaration` (which has no annotatable `id`). Each declarator-type
@@ -46094,7 +47067,7 @@ function emitScript$1(ir, collectors, _registry) {
46094
47067
  }
46095
47068
  }
46096
47069
  for (const ref of ir.refs) hookLines.push(`let ${ref.name}Ref: HTMLElement | null = null;`);
46097
- const filteredStmts = [];
47070
+ const residualStmts = [];
46098
47071
  for (const stmt of rewriteResult.rewrittenProgram.program.body) {
46099
47072
  if (t.isVariableDeclaration(stmt)) {
46100
47073
  if (stmt.declarations.every((d) => d.init && t.isCallExpression(d.init) && t.isIdentifier(d.init.callee) && d.init.callee.name === "$computed")) continue;
@@ -46104,8 +47077,10 @@ function emitScript$1(ir, collectors, _registry) {
46104
47077
  const callee = stmt.expression.callee;
46105
47078
  if (t.isIdentifier(callee) && (callee.name === "$onMount" || callee.name === "$onUnmount" || callee.name === "$onUpdate" || callee.name === "$watch" || callee.name === "$expose" || callee.name === "$provide")) continue;
46106
47079
  }
46107
- filteredStmts.push(tryHoistArrowToFunction(stmt) ?? stmt);
47080
+ residualStmts.push(stmt);
46108
47081
  }
47082
+ mirrorSpliceBoundaryComments(residualStmts);
47083
+ const filteredStmts = residualStmts.map((stmt) => tryHoistArrowToFunction(stmt) ?? stmt);
46109
47084
  let userArrowsSection = "";
46110
47085
  let scriptMap = null;
46111
47086
  if (filteredStmts.length > 0) {
@@ -46806,6 +47781,10 @@ function composeClassValue(attrs, ir, exprOpts, runtime) {
46806
47781
  if (attrs.length === 1 && attrs[0].kind === "binding") {
46807
47782
  const a = attrs[0];
46808
47783
  if (t.isObjectExpression(a.expression)) return renderExpr(a.expression, ir, exprOpts);
47784
+ if (a.wrapForDisplay && !t.isTemplateLiteral(a.expression)) {
47785
+ runtime?.add("rozieClass");
47786
+ return `rozieClass(${renderExpr(a.expression, ir, exprOpts)})`;
47787
+ }
46809
47788
  return renderExpr(a.expression, ir, exprOpts);
46810
47789
  }
46811
47790
  if (attrs.length === 1 && attrs[0].kind === "interpolated") {
@@ -46824,7 +47803,10 @@ function composeClassValue(attrs, ir, exprOpts, runtime) {
46824
47803
  const parts = [];
46825
47804
  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).`);
46826
47805
  else if (a.kind === "static") parts.push(renderStaticClassValue(a.value));
46827
- else if (a.kind === "binding") parts.push(`(${renderExpr(a.expression, ir, exprOpts)})`);
47806
+ else if (a.kind === "binding") if (a.wrapForDisplay && !t.isTemplateLiteral(a.expression)) {
47807
+ runtime?.add("rozieClass");
47808
+ parts.push(`rozieClass(${renderExpr(a.expression, ir, exprOpts)})`);
47809
+ } else parts.push(`(${renderExpr(a.expression, ir, exprOpts)})`);
46828
47810
  else if (a.kind === "spreadBinding") throw new Error(`Solid target: spreadBinding not valid in class array context (Phase 14).`);
46829
47811
  else {
46830
47812
  let lit = "";
@@ -49475,7 +50457,8 @@ function composeSourceMap(ms, opts) {
49475
50457
  outputOffset: opts.scriptOutputOffset
49476
50458
  }] : [],
49477
50459
  fileExt: ".tsx",
49478
- userCodeLineOffset: opts.userCodeLineOffset
50460
+ userCodeLineOffset: opts.userCodeLineOffset,
50461
+ partialLineOffsets: opts.partialLineOffsets
49479
50462
  });
49480
50463
  }
49481
50464
  //#endregion
@@ -49581,7 +50564,8 @@ function emitSolid(ir, opts = {}) {
49581
50564
  source: opts.source,
49582
50565
  scriptMap: shell.scriptMap,
49583
50566
  scriptOutputOffset: shell.scriptOutputOffset,
49584
- userCodeLineOffset: shell.userCodeLineOffset
50567
+ userCodeLineOffset: shell.userCodeLineOffset,
50568
+ partialLineOffsets: buildPartialLineOffsets(ir.setupBody.scriptProgram)
49585
50569
  }) : null;
49586
50570
  const diagnostics = [
49587
50571
  ...scriptResult.diagnostics,
@@ -52453,6 +53437,10 @@ function emitAttribute(attr, ir, tagName, tagKind = "html", opts) {
52453
53437
  return `style=\${styleMap(${expr})}`;
52454
53438
  }
52455
53439
  const expr = rewriteTemplateExpression(attr.expression, ir);
53440
+ if (attr.name === "style") {
53441
+ opts?.runtime.add("rozieStyle");
53442
+ return `style=\${rozieStyle(${expr})}`;
53443
+ }
52456
53444
  if (tagKind === "component" || tagKind === "self") {
52457
53445
  const propName = attr.name.includes("-") ? attr.name.replace(/-([a-z])/g, (_, ch) => ch.toUpperCase()) : attr.name;
52458
53446
  if (opts?._state && (t.isArrayExpression(attr.expression) || t.isObjectExpression(attr.expression)) && isPureLiteral(attr.expression)) {
@@ -52872,9 +53860,12 @@ function emitElementOpenTag(node, ir, irName, opts) {
52872
53860
  const expr = rewriteTemplateExpression(bindingClass.expression, ir);
52873
53861
  const staticPart = staticClassValues.length > 0 ? `${staticClassValues.join(" ")} ` : "";
52874
53862
  let classExpr = expr;
52875
- if (bindingClass.wrapForDisplay) {
53863
+ if (bindingClass.wrapForDisplay) if (t.isTemplateLiteral(bindingClass.expression)) {
52876
53864
  opts.runtime.add("rozieDisplay");
52877
53865
  classExpr = `rozieDisplay(${expr})`;
53866
+ } else {
53867
+ opts.runtime.add("rozieClass");
53868
+ classExpr = `rozieClass(${expr})`;
52878
53869
  }
52879
53870
  parts.push(`class="${staticPart}\${(${classExpr})}"`);
52880
53871
  } else if (bindingClass.kind === "interpolated") {