@barefootjs/jsx 0.26.2 → 0.26.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/interface.d.ts +29 -0
- package/dist/adapters/interface.d.ts.map +1 -1
- package/dist/adapters/jsx-adapter.d.ts +9 -0
- package/dist/adapters/jsx-adapter.d.ts.map +1 -1
- package/dist/adapters/loop-bound-names.d.ts.map +1 -1
- package/dist/adapters/parsed-expr-emitter.d.ts +0 -10
- package/dist/adapters/parsed-expr-emitter.d.ts.map +1 -1
- package/dist/adapters/test-adapter.d.ts.map +1 -1
- package/dist/analyzer-context.d.ts +11 -1
- package/dist/analyzer-context.d.ts.map +1 -1
- package/dist/analyzer.d.ts +40 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/expression-parser.d.ts.map +1 -1
- package/dist/index.js +1086 -269
- package/dist/ir-to-client-js/build-references.d.ts.map +1 -1
- package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts +14 -1
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-component-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-composite-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-event-delegation.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-inner-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts +16 -1
- package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +39 -0
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/shared.d.ts +12 -1
- package/dist/ir-to-client-js/control-flow/shared.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/event-delegation.d.ts +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/event-delegation.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts +19 -0
- package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow.d.ts.map +1 -1
- package/dist/ir-to-client-js/html-template.d.ts +67 -1
- package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
- package/dist/ir-to-client-js/imports.d.ts +2 -2
- package/dist/ir-to-client-js/imports.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/build-static-array-child-init.d.ts.map +1 -1
- package/dist/ir-to-client-js/reactivity.d.ts.map +1 -1
- package/dist/ir-to-client-js/types.d.ts +29 -4
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/jsx-to-ir.d.ts.map +1 -1
- package/dist/loop-destructure.d.ts.map +1 -1
- package/dist/strip-types.d.ts +18 -0
- package/dist/strip-types.d.ts.map +1 -1
- package/dist/types.d.ts +143 -32
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +277 -33
- package/src/__tests__/client-js-generation.test.ts +18 -5
- package/src/__tests__/compiler-runtime-contract.test.ts +4 -4
- package/src/__tests__/compiler-stress-1244.test.ts +12 -1
- package/src/__tests__/delegated-handler-preamble.test.ts +153 -0
- package/src/__tests__/event-delegation-scope-2367.test.ts +108 -0
- package/src/__tests__/flatmap-segments.test.ts +262 -0
- package/src/__tests__/map-arbitrary-body.test.ts +226 -0
- package/src/__tests__/map-body-no-silent-divergence.test.ts +405 -0
- package/src/__tests__/map-multi-return-body.test.ts +172 -0
- package/src/__tests__/preamble-region-patch.test.ts +150 -0
- package/src/__tests__/static-loop-csr-materialize.test.ts +3 -2
- package/src/__tests__/unsupported-expression.test.ts +20 -2
- package/src/adapters/interface.ts +40 -0
- package/src/adapters/jsx-adapter.ts +10 -0
- package/src/adapters/loop-bound-names.ts +6 -2
- package/src/adapters/parsed-expr-emitter.ts +5 -10
- package/src/adapters/test-adapter.ts +10 -0
- package/src/analyzer-context.ts +35 -1
- package/src/analyzer.ts +162 -24
- package/src/compiler.ts +2 -2
- package/src/expression-parser.ts +27 -19
- package/src/ir-to-client-js/build-references.ts +19 -2
- package/src/ir-to-client-js/collect-elements.ts +69 -13
- package/src/ir-to-client-js/control-flow/plan/branch-loop.ts +14 -1
- package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +18 -5
- package/src/ir-to-client-js/control-flow/plan/build-component-loop.ts +4 -0
- package/src/ir-to-client-js/control-flow/plan/build-composite-loop.ts +15 -2
- package/src/ir-to-client-js/control-flow/plan/build-event-delegation.ts +32 -3
- package/src/ir-to-client-js/control-flow/plan/build-inner-loop.ts +26 -3
- package/src/ir-to-client-js/control-flow/plan/build-loop.ts +53 -2
- package/src/ir-to-client-js/control-flow/plan/event-delegation.ts +16 -1
- package/src/ir-to-client-js/control-flow/plan/loop.ts +40 -0
- package/src/ir-to-client-js/control-flow/shared.ts +28 -2
- package/src/ir-to-client-js/control-flow/stringify/branch-loop.ts +25 -3
- package/src/ir-to-client-js/control-flow/stringify/event-delegation.ts +76 -21
- package/src/ir-to-client-js/control-flow/stringify/loop.ts +65 -1
- package/src/ir-to-client-js/control-flow.ts +11 -0
- package/src/ir-to-client-js/html-template.ts +244 -39
- package/src/ir-to-client-js/imports.ts +1 -1
- package/src/ir-to-client-js/plan/build-static-array-child-init.ts +22 -10
- package/src/ir-to-client-js/reactivity.ts +6 -0
- package/src/ir-to-client-js/types.ts +28 -3
- package/src/jsx-to-ir.ts +1059 -167
- package/src/loop-destructure.ts +9 -5
- package/src/rich-type-refusal.ts +6 -2
- package/src/strip-types.ts +47 -0
- package/src/types.ts +159 -35
package/src/loop-destructure.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IRLoop, IRNode, AttrValue, IRTemplatePart } from './types.ts'
|
|
2
|
+
import { preambleAnalysisText, preambleAnalysisTemplateText } from './types.ts'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* True when a loop's `.map()` destructure param is one of the shapes this
|
|
@@ -154,12 +155,15 @@ function restNamesMisused(loop: IRLoop, names: string[]): boolean {
|
|
|
154
155
|
check(l.array)
|
|
155
156
|
check(l.templateArray)
|
|
156
157
|
check(l.key)
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
if (l.preamble) {
|
|
159
|
+
check(preambleAnalysisText(l.preamble))
|
|
160
|
+
check(preambleAnalysisTemplateText(l.preamble))
|
|
161
|
+
}
|
|
159
162
|
if (l.flatMapCallback) {
|
|
160
|
-
check(l.flatMapCallback
|
|
161
|
-
|
|
162
|
-
|
|
163
|
+
check(preambleAnalysisText(l.flatMapCallback))
|
|
164
|
+
for (const seg of l.flatMapCallback.segments) {
|
|
165
|
+
if (seg.kind === 'jsx') visit(seg.ir)
|
|
166
|
+
}
|
|
163
167
|
}
|
|
164
168
|
l.children.forEach(visit)
|
|
165
169
|
}
|
package/src/rich-type-refusal.ts
CHANGED
|
@@ -299,8 +299,12 @@ function walkNode(
|
|
|
299
299
|
for (const nested of node.nestedComponents ?? []) {
|
|
300
300
|
for (const child of nested.children) walkNode(child, meta, loopBindings, matchers, errors, seen)
|
|
301
301
|
}
|
|
302
|
-
for (const
|
|
303
|
-
walkNode(
|
|
302
|
+
for (const seg of node.flatMapCallback?.segments ?? []) {
|
|
303
|
+
if (seg.kind === 'jsx') walkNode(seg.ir, meta, loopBindings, matchers, errors, seen)
|
|
304
|
+
}
|
|
305
|
+
// Preamble leaves (array-builder bodies) are nested IR the same way.
|
|
306
|
+
for (const seg of node.preamble?.segments ?? []) {
|
|
307
|
+
if (seg.kind === 'jsx') walkNode(seg.ir, meta, loopBindings, matchers, errors, seen)
|
|
304
308
|
}
|
|
305
309
|
break
|
|
306
310
|
}
|
package/src/strip-types.ts
CHANGED
|
@@ -71,6 +71,53 @@ export function reconstructWithoutTypes(
|
|
|
71
71
|
return result
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Split a node's text into JS/marker segments: type ranges are stripped from
|
|
76
|
+
* the JS text, and each `markers` span becomes its own segment boundary
|
|
77
|
+
* (emitting `{ marker: i }` for the i-th marker, in the order given). Used by
|
|
78
|
+
* the Stage-3 preamble collector to carry mixed content (JS text + JSX leaves)
|
|
79
|
+
* as structured segments instead of a sentinel-bearing string — the marker
|
|
80
|
+
* index maps to the compiled leaf IR. Markers are absolute-position,
|
|
81
|
+
* non-overlapping sub-spans of `node`; a type range inside a marker span is
|
|
82
|
+
* subsumed.
|
|
83
|
+
*/
|
|
84
|
+
export function reconstructAsSegments(
|
|
85
|
+
node: ts.Node,
|
|
86
|
+
sourceFile: ts.SourceFile,
|
|
87
|
+
ranges: ExcludeRange[],
|
|
88
|
+
markers: Array<{ start: number; end: number }>
|
|
89
|
+
): Array<{ js: string } | { marker: number }> {
|
|
90
|
+
const nodeStart = node.getStart(sourceFile)
|
|
91
|
+
const nodeEnd = node.getEnd()
|
|
92
|
+
const fullText = sourceFile.text
|
|
93
|
+
|
|
94
|
+
type Edit = { start: number; end: number; marker: number | null }
|
|
95
|
+
const edits: Edit[] = []
|
|
96
|
+
for (const r of ranges) {
|
|
97
|
+
if (r.end <= nodeStart || r.start >= nodeEnd) continue
|
|
98
|
+
edits.push({ start: r.start, end: r.end, marker: null })
|
|
99
|
+
}
|
|
100
|
+
markers.forEach((m, i) => edits.push({ start: m.start, end: m.end, marker: i }))
|
|
101
|
+
edits.sort((a, b) => a.start - b.start || b.end - a.end)
|
|
102
|
+
|
|
103
|
+
const segments: Array<{ js: string } | { marker: number }> = []
|
|
104
|
+
let jsBuf = ''
|
|
105
|
+
let pos = nodeStart
|
|
106
|
+
for (const edit of edits) {
|
|
107
|
+
if (edit.start < pos) continue // subsumed by an already-emitted span
|
|
108
|
+
jsBuf += fullText.slice(pos, edit.start)
|
|
109
|
+
if (edit.marker !== null) {
|
|
110
|
+
if (jsBuf) segments.push({ js: jsBuf })
|
|
111
|
+
jsBuf = ''
|
|
112
|
+
segments.push({ marker: edit.marker })
|
|
113
|
+
}
|
|
114
|
+
pos = edit.end
|
|
115
|
+
}
|
|
116
|
+
if (pos < nodeEnd) jsBuf += fullText.slice(pos, nodeEnd)
|
|
117
|
+
if (jsBuf) segments.push({ js: jsBuf })
|
|
118
|
+
return segments
|
|
119
|
+
}
|
|
120
|
+
|
|
74
121
|
function mergeRanges(ranges: ExcludeRange[]): ExcludeRange[] {
|
|
75
122
|
if (ranges.length === 0) return []
|
|
76
123
|
const merged: ExcludeRange[] = [ranges[0]]
|
package/src/types.ts
CHANGED
|
@@ -371,6 +371,24 @@ export interface IRExpression {
|
|
|
371
371
|
loc: SourceLocation
|
|
372
372
|
/** When true, expression should be evaluated on client side only */
|
|
373
373
|
clientOnly?: boolean
|
|
374
|
+
/**
|
|
375
|
+
* Stage 3 / D4 (spec/callback-fidelity.md) — this expression child references
|
|
376
|
+
* an array built by an arbitrary `.map()` preamble (`return <tr>{out}</tr>`
|
|
377
|
+
* where `out` is `push`-populated with element strings). Phase-2 string-
|
|
378
|
+
* template emission must join it (`Array.isArray(out) ? out.join('') : …`)
|
|
379
|
+
* rather than `String([])`-comma-collapse it. JSX SSR adapters ignore the
|
|
380
|
+
* flag (their JSX runtime renders an array child natively).
|
|
381
|
+
*/
|
|
382
|
+
joinArrayChild?: boolean
|
|
383
|
+
/**
|
|
384
|
+
* True when this expression was classified as a loop's preamble-patched
|
|
385
|
+
* region (#2389 — see `IRLoop.preambleRegions`): its free identifiers
|
|
386
|
+
* intersect the enclosing loop's `preamble.declaredNames`. Excludes the
|
|
387
|
+
* node from `collectLoopChildReactiveTexts` (which patches via
|
|
388
|
+
* `.textContent`, wrong for markup) regardless of `reactive` / `slotId` —
|
|
389
|
+
* the region-patch effect (`patchSlotRange`) is its only wiring.
|
|
390
|
+
*/
|
|
391
|
+
preambleRegion?: boolean
|
|
374
392
|
/** When true, expression calls signal getters or memos (has reactive `foo()` pattern). */
|
|
375
393
|
callsReactiveGetters?: boolean
|
|
376
394
|
/** When true, expression contains function call(s) — any `identifier()` pattern (computed from AST). */
|
|
@@ -678,21 +696,43 @@ export interface IRLoop {
|
|
|
678
696
|
*/
|
|
679
697
|
bodyIsItemConditional?: boolean
|
|
680
698
|
|
|
681
|
-
/**
|
|
682
|
-
* Raw JS of pre-return statements in block body .map() callback.
|
|
683
|
-
* Example: `items.map(item => { const label = item.name.toUpperCase(); return <li>{label}</li> })`
|
|
684
|
-
* stores "const label = item.name.toUpperCase();" as mapPreamble.
|
|
685
|
-
*/
|
|
686
|
-
mapPreamble?: string
|
|
687
|
-
/** Pre-transformed mapPreamble with destructured prop refs rewritten to _p.xxx. */
|
|
688
|
-
templateMapPreamble?: string
|
|
689
|
-
|
|
690
699
|
/** Type annotation for loop param (e.g., 'Desk'), preserved for .tsx output */
|
|
691
700
|
paramType?: string
|
|
692
701
|
/** Type annotation for loop index param (e.g., 'number'), preserved for .tsx output */
|
|
693
702
|
indexType?: string
|
|
694
|
-
/**
|
|
695
|
-
|
|
703
|
+
/**
|
|
704
|
+
* Structured pre-return statements of a block-body `.map()` callback
|
|
705
|
+
* (Stage 3 root cure, spec/callback-fidelity.md). Replaces the former
|
|
706
|
+
* `mapPreamble` / `templateMapPreamble` / `typedMapPreamble` string carriers
|
|
707
|
+
* and `preambleFragments`: mixed content (JS text + JSX leaves) is carried as
|
|
708
|
+
* a segment list, never as a sentinel-bearing string, so an emitter that
|
|
709
|
+
* lacks preamble support cannot accidentally interpolate it — rendering goes
|
|
710
|
+
* through `renderPreamble()` (html-template.ts) or not at all.
|
|
711
|
+
*/
|
|
712
|
+
preamble?: MapCallbackPreamble
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Expression children of the loop body whose free identifiers intersect
|
|
716
|
+
* `preamble.declaredNames` (#2389, patch-on-update follow-up to the Stage 3
|
|
717
|
+
* root cure) — e.g. `{cells}` in `arr.map(t => { const cells = []; ...;
|
|
718
|
+
* return <tr>{cells}<td>{t.name}</td></tr> })`. `mapArray` reuses the same
|
|
719
|
+
* DOM node on a same-key item update via per-item `setItem`, re-running
|
|
720
|
+
* only the wired text/attr slots — a preamble-derived child has NEITHER
|
|
721
|
+
* (it's a bare interpolation, `joinArrayChild` array-join or otherwise),
|
|
722
|
+
* so without this it freezes at its mount-time value forever. Each entry's
|
|
723
|
+
* `slotId` is emitted as the usual `<!--bf:sN-->...<!--/-->` marker pair
|
|
724
|
+
* (same door as a reactive text slot — `irToHtmlTemplate` / Hono's
|
|
725
|
+
* `renderExpression` key off `slotId` alone), but the CLIENT wiring is a
|
|
726
|
+
* distinct `patchSlotRange`-based region-patch effect (`preambleRegions`
|
|
727
|
+
* in the client-JS loop plan), NOT a `reactiveTexts` entry — patching via
|
|
728
|
+
* `.textContent` would escape markup that a `joinArrayChild` region must
|
|
729
|
+
* render raw. `collectLoopChildReactiveTexts` excludes any node collected
|
|
730
|
+
* here (`IRExpression.preambleRegion`) so a qualifying expression is never
|
|
731
|
+
* double-wired. Populated only when `preamble` is set and the loop is NOT
|
|
732
|
+
* `isStaticArray` — a static array's SSR-rendered items are never
|
|
733
|
+
* recreated via signals, so there is nothing to patch.
|
|
734
|
+
*/
|
|
735
|
+
preambleRegions?: PreambleRegionSource[]
|
|
696
736
|
|
|
697
737
|
/**
|
|
698
738
|
* When `.map(callback)` destructures its item parameter (array or object
|
|
@@ -720,9 +760,9 @@ export interface IRLoop {
|
|
|
720
760
|
/**
|
|
721
761
|
* For flatMap callbacks whose body can't be decomposed into simple
|
|
722
762
|
* `children` (block bodies with conditional returns, variable-assigned
|
|
723
|
-
* JSX, etc.). Carries the
|
|
724
|
-
*
|
|
725
|
-
*
|
|
763
|
+
* JSX, etc.). Carries the body as structured segments (JS text +
|
|
764
|
+
* compiled JSX-leaf IR) rendered through `renderPreamble()`, plus the
|
|
765
|
+
* raw TSX for JSX-runtime SSR adapters.
|
|
726
766
|
*
|
|
727
767
|
* When present, `children` is empty — emitters use this field instead.
|
|
728
768
|
*/
|
|
@@ -730,31 +770,115 @@ export interface IRLoop {
|
|
|
730
770
|
}
|
|
731
771
|
|
|
732
772
|
/**
|
|
733
|
-
* A compiled flatMap callback body
|
|
734
|
-
*
|
|
735
|
-
* `
|
|
736
|
-
*
|
|
737
|
-
* Each emitter (html-template, hono-adapter) renders the IR fragments
|
|
738
|
-
* in its own format and substitutes the placeholders accordingly.
|
|
773
|
+
* A compiled flatMap callback body, carried as structured segments (the same
|
|
774
|
+
* shape as {@link MapCallbackPreamble}, rendered through the same
|
|
775
|
+
* `renderPreamble()` door) — never as a sentinel-bearing string. JSX-runtime
|
|
776
|
+
* SSR adapters emit `rawBody` (real TSX, branded) instead.
|
|
739
777
|
*/
|
|
740
778
|
export interface FlatMapCallback {
|
|
741
779
|
/** Callback parameters text, e.g. `"(frame, i)"` */
|
|
742
780
|
params: string
|
|
743
|
-
/** Callback body text
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
781
|
+
/** Callback body as js-text / compiled-JSX-leaf segments. */
|
|
782
|
+
segments: PreambleSegment[]
|
|
783
|
+
/** Original callback body text (JSX intact) for JSX-runtime SSR adapters. */
|
|
784
|
+
rawBody: TsxSourceText
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Raw TSX source text (types + JSX intact), branded so it is only assignable
|
|
789
|
+
* where a JSX-runtime SSR adapter emits real TSX (Hono / TestAdapter). Plain
|
|
790
|
+
* strings destined for the client bundle cannot satisfy this type, and this
|
|
791
|
+
* type must never be spliced into plain-JS emission — the write-side twin of
|
|
792
|
+
* the "never parse JS with regex" rule: raw source crossing into an emitted
|
|
793
|
+
* artifact is tagged with its only legal destination.
|
|
794
|
+
*/
|
|
795
|
+
export type TsxSourceText = string & { readonly __tsxSourceBrand: unique symbol }
|
|
796
|
+
|
|
797
|
+
/** Constructor for {@link TsxSourceText} — the single place the brand is applied. */
|
|
798
|
+
export function tsxSourceText(raw: string): TsxSourceText {
|
|
799
|
+
return raw as TsxSourceText
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* One segment of a `.map()` callback preamble. Mixed content is structured,
|
|
804
|
+
* never a sentinel-bearing string: `js` segments carry JSX-free JS source text
|
|
805
|
+
* (types stripped; `templateText` set when the destructured-prop rewrite
|
|
806
|
+
* differs), `jsx` segments carry the compiled IR of one JSX leaf. Segments
|
|
807
|
+
* concatenate in source order with no separators (they are sub-statement
|
|
808
|
+
* spans).
|
|
809
|
+
*/
|
|
810
|
+
export type PreambleSegment =
|
|
811
|
+
| {
|
|
812
|
+
kind: 'js'
|
|
813
|
+
/** Type-stripped JS text for the client bundle. */
|
|
814
|
+
text: string
|
|
815
|
+
/** `text` with destructured prop refs rewritten to `_p.xxx`, when different. */
|
|
816
|
+
templateText?: string
|
|
817
|
+
}
|
|
818
|
+
| { kind: 'jsx'; ir: IRNode }
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* The structured pre-return statements of a block-body `.map()` callback
|
|
822
|
+
* (Stage 3 root cure, spec/callback-fidelity.md). Client emission renders
|
|
823
|
+
* `segments` via `renderPreamble()` (html-template.ts) — the only door; a
|
|
824
|
+
* consumer that can't call it has no way to splice the preamble, so a missing
|
|
825
|
+
* wire-up is a type error or an explicit refusal, never a silent leak.
|
|
826
|
+
* JSX-runtime SSR adapters emit `ssrText` (real TSX) instead.
|
|
827
|
+
*/
|
|
828
|
+
export interface MapCallbackPreamble {
|
|
829
|
+
segments: PreambleSegment[]
|
|
830
|
+
/** Raw preamble source (types + JSX intact) for JSX-runtime SSR adapters. */
|
|
831
|
+
ssrText: TsxSourceText
|
|
832
|
+
/** const/let/function names the preamble declares (D5 key-derivability guard). */
|
|
833
|
+
declaredNames: string[]
|
|
834
|
+
/**
|
|
835
|
+
* The subset of {@link declaredNames} that accumulate JSX leaves — the
|
|
836
|
+
* `push`/`unshift` targets of a `jsx` segment and the declaration targets of
|
|
837
|
+
* a leaf-bearing initializer (`const out = xs.map(x => <td/>)`). Only these
|
|
838
|
+
* get the `{out}` array-join child emission; a value-only local (`{label}`)
|
|
839
|
+
* keeps the plain interpolation it always had.
|
|
840
|
+
*/
|
|
841
|
+
builderNames: string[]
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* One loop-body expression child classified as a preamble-patched region
|
|
846
|
+
* (#2389 — see `IRLoop.preambleRegions`). `expr` is the raw (unwrapped)
|
|
847
|
+
* expression text, exactly as carried on the source `IRExpression` node;
|
|
848
|
+
* emitters wrap it with the loop-param accessor at codegen time, matching
|
|
849
|
+
* every other loop-body expression. `joinArrayChild` mirrors the
|
|
850
|
+
* `IRExpression` field of the same name — the client-JS plan builder uses it
|
|
851
|
+
* to decide between the array-join value expression and a plain (escaped)
|
|
852
|
+
* text value, keeping the region's re-render byte-identical to the row
|
|
853
|
+
* template's own `irToHtmlTemplate` rendering of the same node.
|
|
854
|
+
*/
|
|
855
|
+
export interface PreambleRegionSource {
|
|
856
|
+
slotId: string
|
|
857
|
+
expr: string
|
|
858
|
+
joinArrayChild?: boolean
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* The preamble's JS text (js segments only, concatenated), for read-side
|
|
863
|
+
* analysis — free-identifier scans, reachability edges, rest-misuse checks.
|
|
864
|
+
* JSX-leaf interiors are compiled IR and are analyzed through their own IR
|
|
865
|
+
* walks, exactly as the former placeholder carrier excluded them. NEVER use
|
|
866
|
+
* this for emission — emission goes through `renderPreamble()`.
|
|
867
|
+
*/
|
|
868
|
+
export function preambleAnalysisText(p: Pick<MapCallbackPreamble, 'segments'>): string {
|
|
869
|
+
let out = ''
|
|
870
|
+
for (const seg of p.segments) if (seg.kind === 'js') out += seg.text
|
|
871
|
+
return out
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Template-context variant of {@link preambleAnalysisText} (destructured prop
|
|
876
|
+
* refs rewritten). Read-side analysis only.
|
|
877
|
+
*/
|
|
878
|
+
export function preambleAnalysisTemplateText(p: Pick<MapCallbackPreamble, 'segments'>): string {
|
|
879
|
+
let out = ''
|
|
880
|
+
for (const seg of p.segments) if (seg.kind === 'js') out += seg.templateText ?? seg.text
|
|
881
|
+
return out
|
|
758
882
|
}
|
|
759
883
|
|
|
760
884
|
/**
|