@barefootjs/jsx 0.16.0 → 0.17.0
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/env-signal.d.ts +38 -15
- package/dist/adapters/env-signal.d.ts.map +1 -1
- package/dist/adapters/jsx-adapter.d.ts.map +1 -1
- package/dist/adapters/parsed-expr-emitter.d.ts +7 -6
- package/dist/adapters/parsed-expr-emitter.d.ts.map +1 -1
- package/dist/analyzer-context.d.ts +29 -1
- package/dist/analyzer-context.d.ts.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/builtin-lowering-plugins.d.ts +34 -0
- package/dist/builtin-lowering-plugins.d.ts.map +1 -0
- package/dist/expression-parser.d.ts +219 -163
- package/dist/expression-parser.d.ts.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6754 -6129
- package/dist/ir-to-client-js/csr-substitute.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/build-declaration-emit.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/declaration-emit.d.ts +9 -0
- package/dist/ir-to-client-js/plan/declaration-emit.d.ts.map +1 -1
- package/dist/jsx-to-ir.d.ts.map +1 -1
- package/dist/lowering-registry.d.ts +122 -0
- package/dist/lowering-registry.d.ts.map +1 -0
- package/dist/query-href-lowering.d.ts +63 -0
- package/dist/query-href-lowering.d.ts.map +1 -0
- package/dist/ssr-defaults.d.ts.map +1 -1
- package/dist/types.d.ts +169 -11
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +68 -3
- package/src/__tests__/analyzer.test.ts +53 -0
- package/src/__tests__/expression-parser.test.ts +703 -391
- package/src/__tests__/ir-reduce-op.test.ts +18 -21
- package/src/__tests__/ir-sort-comparator.test.ts +19 -20
- package/src/__tests__/lowering-registry.test.ts +141 -0
- package/src/__tests__/primitive-resolver-alias.test.ts +23 -0
- package/src/__tests__/query-href-recognition.test.ts +58 -0
- package/src/__tests__/serialize-parsed-expr.test.ts +204 -0
- package/src/__tests__/unsupported-expression.test.ts +98 -4
- package/src/adapters/env-signal.ts +60 -21
- package/src/adapters/jsx-adapter.ts +17 -0
- package/src/adapters/parsed-expr-emitter.ts +39 -41
- package/src/analyzer-context.ts +72 -27
- package/src/analyzer.ts +226 -9
- package/src/builtin-lowering-plugins.ts +54 -0
- package/src/expression-parser.ts +1183 -927
- package/src/index.ts +26 -3
- package/src/ir-to-client-js/csr-substitute.ts +5 -0
- package/src/ir-to-client-js/plan/build-declaration-emit.ts +16 -0
- package/src/ir-to-client-js/plan/declaration-emit.ts +9 -0
- package/src/ir-to-client-js/stringify/declaration-emit.ts +11 -0
- package/src/jsx-to-ir.ts +182 -43
- package/src/lowering-registry.ts +160 -0
- package/src/query-href-lowering.ts +147 -0
- package/src/ssr-defaults.ts +5 -1
- package/src/types.ts +171 -12
- package/src/__tests__/flatmap-support.test.ts +0 -218
- package/src/__tests__/reduce-op.test.ts +0 -201
|
@@ -943,7 +943,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf:
|
|
|
943
943
|
export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
|
|
944
944
|
`;
|
|
945
945
|
|
|
946
|
-
exports[`docs/core/rendering/jsx-compatibility.md doc-examples
|
|
946
|
+
exports[`docs/core/rendering/jsx-compatibility.md doc-examples L167 — ✅ Value-producing block bodies normalize (let-inline) and lower everywhere 1`] = `
|
|
947
947
|
"import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
|
|
948
948
|
|
|
949
949
|
export function initTodoItem(__scope, _p = {}) {
|
|
@@ -997,14 +997,79 @@ export function initExample(__scope, _p = {}) {
|
|
|
997
997
|
|
|
998
998
|
const [_s1] = $(__scope, 's1')
|
|
999
999
|
|
|
1000
|
-
mapArray(() => items().
|
|
1000
|
+
mapArray(() => items().toSorted((a, b) => a.name > b.name ? 1 : -1), _s1, (item) => String(item.id), (item, __idx, __existing) => {
|
|
1001
1001
|
if (__existing) { initChild('Item__b3c36eee', __existing, { get item() { return item() } }); return __existing }
|
|
1002
1002
|
return createComponent('Item__b3c36eee', { get item() { return item() } }, item().id)
|
|
1003
1003
|
}, 'l0')
|
|
1004
1004
|
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
1007
|
-
hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).
|
|
1007
|
+
hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).toSorted((a, b) => a.name > b.name ? 1 : -1).map((item) => \`\${renderChild('Item__b3c36eee', {item: item}, item.id)}\`).join('')}<!--bf-/loop:l0--></div>\` })
|
|
1008
|
+
export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
|
|
1009
|
+
`;
|
|
1010
|
+
|
|
1011
|
+
exports[`docs/core/rendering/jsx-compatibility.md doc-examples L182 — ✅ Use /* @client */ 1`] = `
|
|
1012
|
+
"import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
|
|
1013
|
+
|
|
1014
|
+
export function initTodoItem(__scope, _p = {}) {
|
|
1015
|
+
if (!__scope) return
|
|
1016
|
+
const __scopeId = __scope.getAttribute('bf-s')
|
|
1017
|
+
|
|
1018
|
+
const [_s0] = $t(__scope, 's0')
|
|
1019
|
+
|
|
1020
|
+
let __anchor_s0 = _s0
|
|
1021
|
+
createEffect(() => {
|
|
1022
|
+
const __val = String(_p.todo)
|
|
1023
|
+
__anchor_s0 = __bfText(__anchor_s0, __val)
|
|
1024
|
+
})
|
|
1025
|
+
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
hydrate('TodoItem__b3c36eee', { init: initTodoItem, template: (_p) => \`<li bf="s1"><!--bf:s0-->\${escapeText(String(_p.todo))}<!--/--></li>\` })
|
|
1029
|
+
export function TodoItem(_p, __bfKey) { return createComponent('TodoItem__b3c36eee', _p, __bfKey) }
|
|
1030
|
+
export function initItem(__scope, _p = {}) {
|
|
1031
|
+
if (!__scope) return
|
|
1032
|
+
const __scopeId = __scope.getAttribute('bf-s')
|
|
1033
|
+
|
|
1034
|
+
const [_s0] = $t(__scope, 's0')
|
|
1035
|
+
|
|
1036
|
+
let __anchor_s0 = _s0
|
|
1037
|
+
createEffect(() => {
|
|
1038
|
+
const __val = String(_p.item)
|
|
1039
|
+
__anchor_s0 = __bfText(__anchor_s0, __val)
|
|
1040
|
+
})
|
|
1041
|
+
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
hydrate('Item__b3c36eee', { init: initItem, template: (_p) => \`<li bf="s1"><!--bf:s0-->\${escapeText(String(_p.item))}<!--/--></li>\` })
|
|
1045
|
+
export function Item(_p, __bfKey) { return createComponent('Item__b3c36eee', _p, __bfKey) }
|
|
1046
|
+
function initDashboard() {}
|
|
1047
|
+
|
|
1048
|
+
hydrate('Dashboard__b3c36eee', { init: initDashboard, template: (_p) => \`<div>D</div>\` })
|
|
1049
|
+
export function Dashboard(_p, __bfKey) { return createComponent('Dashboard__b3c36eee', _p, __bfKey) }
|
|
1050
|
+
export function initExample(__scope, _p = {}) {
|
|
1051
|
+
if (!__scope) return
|
|
1052
|
+
const __scopeId = __scope.getAttribute('bf-s')
|
|
1053
|
+
|
|
1054
|
+
const children = _p.children
|
|
1055
|
+
const [count, setCount] = createSignal(0)
|
|
1056
|
+
const [isLoggedIn] = createSignal(false)
|
|
1057
|
+
const [todos] = createSignal([])
|
|
1058
|
+
const [items] = createSignal([])
|
|
1059
|
+
const [filter] = createSignal('all')
|
|
1060
|
+
const [accepted] = createSignal(false)
|
|
1061
|
+
const [text, setText] = createSignal('')
|
|
1062
|
+
|
|
1063
|
+
const [_s1] = $(__scope, 's1')
|
|
1064
|
+
|
|
1065
|
+
mapArray(() => items().sort((a, b) => { let r = 0; r = a.name > b.name ? 1 : -1; return r }), _s1, (item) => String(item.id), (item, __idx, __existing) => {
|
|
1066
|
+
if (__existing) { initChild('Item__b3c36eee', __existing, { get item() { return item() } }); return __existing }
|
|
1067
|
+
return createComponent('Item__b3c36eee', { get item() { return item() } }, item().id)
|
|
1068
|
+
}, 'l0')
|
|
1069
|
+
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).sort((a, b) => { let r = 0; r = a.name > b.name ? 1 : -1; return r }).map((item) => \`\${renderChild('Item__b3c36eee', {item: item}, item.id)}\`).join('')}<!--bf-/loop:l0--></div>\` })
|
|
1008
1073
|
export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
|
|
1009
1074
|
`;
|
|
1010
1075
|
|
|
@@ -495,3 +495,56 @@ describe('analyzeComponent', () => {
|
|
|
495
495
|
})
|
|
496
496
|
})
|
|
497
497
|
})
|
|
498
|
+
|
|
499
|
+
// #2040: complete, value-producing block-bodied memos are folded to a single
|
|
500
|
+
// `parsed` expression so adapters lower them through the expression path
|
|
501
|
+
// (retiring the per-idiom block recognizers). Idempotent reactive getter reads
|
|
502
|
+
// count as pure, so a guard read on several branches still folds.
|
|
503
|
+
describe('block-bodied memo → parsed fold (#2040)', () => {
|
|
504
|
+
test('guard-and-return-const memo folds to a conditional `parsed`', () => {
|
|
505
|
+
const source = `
|
|
506
|
+
'use client'
|
|
507
|
+
import { createSignal, createMemo } from '@barefootjs/client'
|
|
508
|
+
const ALL = ['a', 'b']
|
|
509
|
+
export function Tags() {
|
|
510
|
+
const [sel, setSel] = createSignal<string | null>(null)
|
|
511
|
+
const visible = createMemo(() => {
|
|
512
|
+
const k = sel()
|
|
513
|
+
if (!k) return ALL
|
|
514
|
+
return ALL.filter(t => t === k)
|
|
515
|
+
})
|
|
516
|
+
return <ul>{visible().map(t => (<li key={t}>{t}</li>))}</ul>
|
|
517
|
+
}
|
|
518
|
+
`
|
|
519
|
+
const ctx = analyzeComponent(source, 'Tags.tsx')
|
|
520
|
+
const memo = ctx.memos.find(m => m.name === 'visible')
|
|
521
|
+
expect(memo?.parsedBlockComplete).toBe(true)
|
|
522
|
+
// `const k = sel(); if (!k) return ALL; return ALL.filter(...)` →
|
|
523
|
+
// `!sel() ? ALL : ALL.filter(...)`. `sel()` (a signal read) inlined twice.
|
|
524
|
+
expect(memo?.parsed?.kind).toBe('conditional')
|
|
525
|
+
const cond = memo?.parsed as { kind: 'conditional'; test: any; consequent: any }
|
|
526
|
+
expect(cond.test).toEqual({ kind: 'unary', op: '!', argument: { kind: 'call', callee: { kind: 'identifier', name: 'sel' }, args: [] } })
|
|
527
|
+
expect(cond.consequent).toEqual({ kind: 'identifier', name: 'ALL' })
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
test('imperative block-bodied memo leaves `parsed` unset', () => {
|
|
531
|
+
const source = `
|
|
532
|
+
'use client'
|
|
533
|
+
import { createSignal, createMemo } from '@barefootjs/client'
|
|
534
|
+
export function C() {
|
|
535
|
+
const [n, setN] = createSignal(0)
|
|
536
|
+
const total = createMemo(() => {
|
|
537
|
+
let s = 0
|
|
538
|
+
for (const x of [1, 2, 3]) s += x
|
|
539
|
+
return s + n()
|
|
540
|
+
})
|
|
541
|
+
return <div>{total()}</div>
|
|
542
|
+
}
|
|
543
|
+
`
|
|
544
|
+
const ctx = analyzeComponent(source, 'C.tsx')
|
|
545
|
+
const memo = ctx.memos.find(m => m.name === 'total')
|
|
546
|
+
// The `for` loop can't be represented → tolerant parser drops it →
|
|
547
|
+
// parsedBlockComplete is false → no fold → parsed stays undefined.
|
|
548
|
+
expect(memo?.parsed).toBeUndefined()
|
|
549
|
+
})
|
|
550
|
+
})
|