@barefootjs/jsx 0.29.0 → 0.30.2
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/child-scope.d.ts +17 -0
- package/dist/adapters/child-scope.d.ts.map +1 -0
- package/dist/adapters/loop-bound-names.d.ts.map +1 -1
- package/dist/analyzer-context.d.ts.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +990 -367
- package/dist/ir-to-client-js/collect-elements.d.ts +5 -25
- package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts +66 -2
- package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/lazy-conditional.d.ts +92 -0
- package/dist/ir-to-client-js/control-flow/plan/lazy-conditional.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/plan/lazy-preamble.d.ts +134 -0
- package/dist/ir-to-client-js/control-flow/plan/lazy-preamble.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts +46 -11
- package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/reactive-effects.d.ts +7 -0
- package/dist/ir-to-client-js/control-flow/plan/reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts.map +1 -1
- package/dist/ir-to-client-js/csr-substitute.d.ts.map +1 -1
- package/dist/ir-to-client-js/html-template.d.ts +2 -3
- package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/declaration-emit.d.ts +6 -0
- package/dist/ir-to-client-js/plan/declaration-emit.d.ts.map +1 -1
- package/dist/ir-to-client-js/reactivity.d.ts +1 -10
- package/dist/ir-to-client-js/reactivity.d.ts.map +1 -1
- package/dist/ir-to-client-js/types.d.ts +12 -0
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/jsx-to-ir.d.ts.map +1 -1
- package/dist/prop-rewrite.d.ts +24 -7
- package/dist/prop-rewrite.d.ts.map +1 -1
- package/dist/rich-type-evidence.d.ts.map +1 -1
- package/dist/ssr-defaults.d.ts.map +1 -1
- package/dist/to-locale-date-lowering.d.ts.map +1 -1
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +6 -24
- package/src/__tests__/branch-loop-plain.test.ts +28 -45
- package/src/__tests__/csr-substitution-safety-divergence.test.ts +9 -3
- package/src/__tests__/csr-template-scope-soundness.test.ts +167 -0
- package/src/__tests__/early-return-branch-switch.test.ts +96 -0
- package/src/__tests__/form-control-value-ssr.test.ts +102 -0
- package/src/__tests__/lazy-conditional.test.ts +190 -0
- package/src/__tests__/lazy-preamble.test.ts +297 -0
- package/src/__tests__/lazy-row-eligibility.test.ts +97 -28
- package/src/__tests__/loop-item-root-scope.test.ts +213 -0
- package/src/__tests__/map-multi-return-body.test.ts +30 -3
- package/src/__tests__/nested-loop-conditional.test.ts +27 -6
- package/src/__tests__/preamble-attr-reactivity.test.ts +217 -0
- package/src/__tests__/preamble-declarations.test.ts +207 -0
- package/src/__tests__/ssr-defaults.test.ts +44 -0
- package/src/adapters/child-scope.ts +22 -0
- package/src/adapters/loop-bound-names.ts +6 -0
- package/src/analyzer-context.ts +43 -0
- package/src/analyzer.ts +33 -3
- package/src/index.ts +1 -0
- package/src/ir-to-client-js/collect-elements.ts +51 -10
- package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +1 -1
- package/src/ir-to-client-js/control-flow/plan/build-composite-loop.ts +2 -2
- package/src/ir-to-client-js/control-flow/plan/build-event-delegation.ts +3 -3
- package/src/ir-to-client-js/control-flow/plan/build-inner-loop.ts +2 -2
- package/src/ir-to-client-js/control-flow/plan/build-lazy-row.ts +147 -5
- package/src/ir-to-client-js/control-flow/plan/build-loop.ts +2 -2
- package/src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts +1 -0
- package/src/ir-to-client-js/control-flow/plan/lazy-conditional.ts +151 -0
- package/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts +287 -0
- package/src/ir-to-client-js/control-flow/plan/lazy-row-eligibility.ts +85 -22
- package/src/ir-to-client-js/control-flow/plan/reactive-effects.ts +7 -0
- package/src/ir-to-client-js/control-flow/stringify/lazy-row.ts +133 -35
- package/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts +24 -4
- package/src/ir-to-client-js/csr-substitute.ts +6 -1
- package/src/ir-to-client-js/emit-registration.ts +1 -1
- package/src/ir-to-client-js/html-template.ts +51 -40
- package/src/ir-to-client-js/index.ts +1 -1
- package/src/ir-to-client-js/plan/build-declaration-emit.ts +2 -0
- package/src/ir-to-client-js/plan/build-static-array-child-init.ts +1 -1
- package/src/ir-to-client-js/plan/declaration-emit.ts +6 -0
- package/src/ir-to-client-js/reactivity.ts +25 -0
- package/src/ir-to-client-js/stringify/declaration-emit.ts +9 -4
- package/src/ir-to-client-js/types.ts +12 -0
- package/src/jsx-to-ir.ts +409 -24
- package/src/prop-rewrite.ts +149 -19
- package/src/rich-type-evidence.ts +6 -7
- package/src/ssr-defaults.ts +7 -2
- package/src/to-locale-date-lowering.ts +8 -2
- package/src/types.ts +80 -0
|
@@ -30,7 +30,7 @@ import { irChildrenToJsExpr, renderPreamble, irToHtmlTemplate } from '../html-te
|
|
|
30
30
|
function staticPreludeStatements(preamble: MapCallbackPreamble | undefined): string[] {
|
|
31
31
|
return preamble
|
|
32
32
|
? [renderPreamble(preamble, {
|
|
33
|
-
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined
|
|
33
|
+
renderLeaf: (ir) => irToHtmlTemplate(ir, undefined, 1, undefined, undefined),
|
|
34
34
|
})]
|
|
35
35
|
: []
|
|
36
36
|
}
|
|
@@ -27,6 +27,12 @@ export interface SignalEmitPlan {
|
|
|
27
27
|
getter: string
|
|
28
28
|
/** Setter identifier — `null` for read-only signals. */
|
|
29
29
|
setter: string | null
|
|
30
|
+
/**
|
|
31
|
+
* Source used the getter-elided form (`const [, setX] = ...`). `getter`
|
|
32
|
+
* holds the synthesized internal name (see `SignalInfo.getterElided`);
|
|
33
|
+
* stringify reproduces the source's hole instead of emitting it.
|
|
34
|
+
*/
|
|
35
|
+
getterElided?: boolean
|
|
30
36
|
/**
|
|
31
37
|
* Fully-resolved initial value expression. Prop rewrites
|
|
32
38
|
* (`<propsName>.X` → `_p.X`) and `?? <default>` insertions for controlled
|
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
} from './types.ts'
|
|
17
17
|
import { attrValueToString, freeIdsFromRefs, tokenContainsIdent } from './utils.ts'
|
|
18
18
|
import { expandConstantForReactivity } from './prop-handling.ts'
|
|
19
|
+
import { extractFreeIdentifiersFromText } from './csr-substitute.ts'
|
|
19
20
|
import { walkIR, stopAt } from './walker.ts'
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -621,12 +622,20 @@ export function collectLoopChildReactiveTexts(
|
|
|
621
622
|
* same semantics. Pass true only when the caller also separately collects
|
|
622
623
|
* nested reactive conditionals and gives each its own `insert()`.
|
|
623
624
|
*/
|
|
625
|
+
/** Does any name in `names` appear in `set`? Iterates rather than
|
|
626
|
+
* spreading — this runs per attribute (Copilot review). */
|
|
627
|
+
function anyNameIn(names: Iterable<string>, set: ReadonlySet<string>): boolean {
|
|
628
|
+
for (const n of names) if (set.has(n)) return true
|
|
629
|
+
return false
|
|
630
|
+
}
|
|
631
|
+
|
|
624
632
|
export function collectLoopChildReactiveAttrs(
|
|
625
633
|
node: IRNode,
|
|
626
634
|
ctx: ClientJsContext,
|
|
627
635
|
loopParam?: string,
|
|
628
636
|
loopParamBindings?: readonly LoopParamBinding[],
|
|
629
637
|
stopAtReactiveConditionals = false,
|
|
638
|
+
preambleNames?: ReadonlySet<string>,
|
|
630
639
|
): LoopChildReactiveAttr[] {
|
|
631
640
|
const attrs: LoopChildReactiveAttr[] = []
|
|
632
641
|
traverseElements(node, (el) => {
|
|
@@ -664,8 +673,23 @@ export function collectLoopChildReactiveAttrs(
|
|
|
664
673
|
// / `hasFunctionCalls` fallback so the loop-child path matches the
|
|
665
674
|
// top-level one — a harmless over-wrap at worst (an effect that
|
|
666
675
|
// subscribes to nothing runs once).
|
|
676
|
+
// A `.map()` callback preamble local (#2447 follow-up). The
|
|
677
|
+
// classifier cannot see it: `const cls = row.done ? …` makes `cls`
|
|
678
|
+
// neither a signal read nor a loop-param read, so `class={cls}`
|
|
679
|
+
// scored 'none' and the value froze at its row-construction value —
|
|
680
|
+
// a same-key item update rewrote the sibling `{row.label}` text and
|
|
681
|
+
// left the class stale. Its reactivity is the PREAMBLE's: whatever
|
|
682
|
+
// the initializer read. Flag it and let the emitter re-run the
|
|
683
|
+
// preamble ahead of the write (`stringifyReactiveEffects`), which is
|
|
684
|
+
// the child-position `preambleRegions` treatment (#2389) applied to
|
|
685
|
+
// the attribute position.
|
|
686
|
+
const readsPreamble =
|
|
687
|
+
preambleNames !== undefined &&
|
|
688
|
+
preambleNames.size > 0 &&
|
|
689
|
+
anyNameIn(expanded.freeIds ?? extractFreeIdentifiersFromText(expanded.expr), preambleNames)
|
|
667
690
|
const reactive =
|
|
668
691
|
classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind !== 'none'
|
|
692
|
+
|| readsPreamble
|
|
669
693
|
|| attr.callsReactiveGetters
|
|
670
694
|
|| attr.hasFunctionCalls
|
|
671
695
|
if (!attr.clientOnly && !reactive) continue
|
|
@@ -675,6 +699,7 @@ export function collectLoopChildReactiveAttrs(
|
|
|
675
699
|
expression: expanded.expr,
|
|
676
700
|
...pickAttrMetaFromIR(attr),
|
|
677
701
|
...(expanded.freeIds !== undefined && { freeIdentifiers: expanded.freeIds }),
|
|
702
|
+
...(readsPreamble && { readsPreamble: true }),
|
|
678
703
|
})
|
|
679
704
|
}
|
|
680
705
|
}
|
|
@@ -65,12 +65,17 @@ function bfIdArg(bfId: string | undefined): string {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function emitSignal(lines: string[], plan: SignalEmitPlan): void {
|
|
68
|
+
// Getter-elided source form (`const [, setX] = createSignal(...)`):
|
|
69
|
+
// reproduce the hole instead of introducing the synthesized internal
|
|
70
|
+
// getter name into output (#2468 sweep — dropping the declaration
|
|
71
|
+
// entirely left the setter as a guaranteed ReferenceError).
|
|
72
|
+
const getterSlot = plan.getterElided ? '' : plan.getter
|
|
68
73
|
// Env signal (#2057): emit its own factory call verbatim (`createSearchParams()`)
|
|
69
74
|
// — a stable request-scoped view, so no baked initial value, profile id,
|
|
70
75
|
// controlled effect, or branch condition applies.
|
|
71
76
|
if (plan.initializerOverride) {
|
|
72
77
|
if (plan.setter) {
|
|
73
|
-
lines.push(` const [${
|
|
78
|
+
lines.push(` const [${getterSlot}, ${plan.setter}] = ${plan.initializerOverride}`)
|
|
74
79
|
} else {
|
|
75
80
|
lines.push(` const [${plan.getter}] = ${plan.initializerOverride}`)
|
|
76
81
|
}
|
|
@@ -85,9 +90,9 @@ function emitSignal(lines: string[], plan: SignalEmitPlan): void {
|
|
|
85
90
|
// runs when the branch is taken. Sibling branches with different
|
|
86
91
|
// signals coexist because each gets its own `let` + guarded assign.
|
|
87
92
|
if (plan.setter) {
|
|
88
|
-
lines.push(` let ${plan.getter}, ${plan.setter}`)
|
|
93
|
+
lines.push(plan.getterElided ? ` let ${plan.setter}` : ` let ${plan.getter}, ${plan.setter}`)
|
|
89
94
|
lines.push(` if (${plan.branchCondition}) {`)
|
|
90
|
-
lines.push(` ;[${
|
|
95
|
+
lines.push(` ;[${getterSlot}, ${plan.setter}] = createSignal(${plan.initialValueExpr}${id})`)
|
|
91
96
|
lines.push(` }`)
|
|
92
97
|
} else {
|
|
93
98
|
lines.push(` let ${plan.getter}`)
|
|
@@ -101,7 +106,7 @@ function emitSignal(lines: string[], plan: SignalEmitPlan): void {
|
|
|
101
106
|
return
|
|
102
107
|
}
|
|
103
108
|
if (plan.setter) {
|
|
104
|
-
lines.push(` const [${
|
|
109
|
+
lines.push(` const [${getterSlot}, ${plan.setter}] = createSignal(${plan.initialValueExpr}${id})`)
|
|
105
110
|
} else {
|
|
106
111
|
lines.push(` const [${plan.getter}] = createSignal(${plan.initialValueExpr}${id})`)
|
|
107
112
|
}
|
|
@@ -494,6 +494,18 @@ export interface LoopChildReactiveAttr extends AttrMeta {
|
|
|
494
494
|
childSlotId: string // bf slot ID of the element with reactive attr
|
|
495
495
|
attrName: string // 'className', 'disabled', etc.
|
|
496
496
|
expression: string // Expression that reads signals
|
|
497
|
+
/**
|
|
498
|
+
* The expression reads a `.map()` callback preamble local (#2447
|
|
499
|
+
* follow-up), so it is reactive by way of whatever the preamble read — the
|
|
500
|
+
* classifier cannot see through the declaration, and the local is not in
|
|
501
|
+
* scope in the update body until the preamble is re-run there.
|
|
502
|
+
*
|
|
503
|
+
* Two consequences, both handled by the emitter rather than here: the row
|
|
504
|
+
* effect runs `mapPreambleWrapped` BEFORE the attr writes, and the lazy row
|
|
505
|
+
* graph refuses the loop (`lazyRowEligibility`'s `readsPreamble` gate), so
|
|
506
|
+
* such a row keeps the eager per-row effect.
|
|
507
|
+
*/
|
|
508
|
+
readsPreamble?: boolean
|
|
497
509
|
}
|
|
498
510
|
|
|
499
511
|
export interface LoopChildReactiveText {
|