@barefootjs/go-template 0.1.2 → 0.2.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/adapter/go-template-adapter.d.ts +16 -9
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +158 -90
- package/dist/build.js +158 -90
- package/dist/index.js +158 -90
- package/package.json +3 -3
- package/src/__tests__/go-template-adapter.test.ts +145 -10
- package/src/adapter/go-template-adapter.ts +191 -144
|
@@ -22,14 +22,6 @@ runAdapterConformanceTests({
|
|
|
22
22
|
// `<!--bf-cond-start:sN-->` / `<!--bf-cond-end:sN-->` comment pairs)
|
|
23
23
|
// is now collapsed by `normalizeHTML` in adapter-tests (#1266).
|
|
24
24
|
//
|
|
25
|
-
// `nullish-coalescing-jsx` / `return-nullish-coalescing` have a
|
|
26
|
-
// separate semantic divergence: the Go template's `{{if ne .Banner
|
|
27
|
-
// ""}}` condition treats an unset `Banner` (Go nil) as `!= ""` and
|
|
28
|
-
// takes the truthy branch with empty content, while Hono's JS
|
|
29
|
-
// `??` operator falls through to the JSX default. That's a Go-
|
|
30
|
-
// adapter branch-selection bug — fixing it is out of scope for
|
|
31
|
-
// #1266.
|
|
32
|
-
//
|
|
33
25
|
// `return-map` uses a `data-key` serialisation shape that differs
|
|
34
26
|
// between Hono (runtime helper) and Go (template variable) in a
|
|
35
27
|
// way that isn't structural — leaving it on `skipJsx` until a
|
|
@@ -43,8 +35,6 @@ runAdapterConformanceTests({
|
|
|
43
35
|
// `BF104` at build time instead of silently emitting invalid
|
|
44
36
|
// template syntax (#1266).
|
|
45
37
|
skipJsx: [
|
|
46
|
-
'nullish-coalescing-jsx',
|
|
47
|
-
'return-nullish-coalescing',
|
|
48
38
|
'return-map',
|
|
49
39
|
// #1297 fixed the harness-side IR emission gate (multi-component
|
|
50
40
|
// sources now emit one `ir` file per component, and the harness
|
|
@@ -1034,6 +1024,116 @@ export function ItemChecker() {
|
|
|
1034
1024
|
})
|
|
1035
1025
|
})
|
|
1036
1026
|
|
|
1027
|
+
describe('findLast/findLastIndex - adapter specific', () => {
|
|
1028
|
+
test('renders findLast() with equality predicate via bf_find_last', () => {
|
|
1029
|
+
const result = compileAndGenerate(`
|
|
1030
|
+
"use client"
|
|
1031
|
+
import { createSignal } from "@barefootjs/client"
|
|
1032
|
+
|
|
1033
|
+
type Item = { name: string; done: boolean }
|
|
1034
|
+
|
|
1035
|
+
export function ItemChecker() {
|
|
1036
|
+
const [items, setItems] = createSignal<Item[]>([])
|
|
1037
|
+
return <div>{items().findLast(t => t.done) ? 'Found' : 'Not found'}</div>
|
|
1038
|
+
}
|
|
1039
|
+
`)
|
|
1040
|
+
expect(result.template).toContain('bf_find_last .Items "Done" true')
|
|
1041
|
+
expect(result.template).toContain('Found')
|
|
1042
|
+
})
|
|
1043
|
+
|
|
1044
|
+
test('renders findLast() with complex predicate via range without break', () => {
|
|
1045
|
+
const result = compileAndGenerate(`
|
|
1046
|
+
"use client"
|
|
1047
|
+
import { createSignal } from "@barefootjs/client"
|
|
1048
|
+
|
|
1049
|
+
type Item = { price: number; category: string }
|
|
1050
|
+
|
|
1051
|
+
export function ItemFinder() {
|
|
1052
|
+
const [items, setItems] = createSignal<Item[]>([])
|
|
1053
|
+
const [type, setType] = createSignal('')
|
|
1054
|
+
return <div>{items().findLast(t => t.price > 100 && t.category === type())}</div>
|
|
1055
|
+
}
|
|
1056
|
+
`)
|
|
1057
|
+
expect(result.template).toContain('{{range')
|
|
1058
|
+
expect(result.template).toContain('$bf_r')
|
|
1059
|
+
expect(result.template).not.toContain('{{break}}')
|
|
1060
|
+
})
|
|
1061
|
+
|
|
1062
|
+
test('renders findLastIndex() with equality predicate via bf_find_last_index', () => {
|
|
1063
|
+
const result = compileAndGenerate(`
|
|
1064
|
+
"use client"
|
|
1065
|
+
import { createSignal } from "@barefootjs/client"
|
|
1066
|
+
|
|
1067
|
+
type Item = { name: string; done: boolean }
|
|
1068
|
+
|
|
1069
|
+
export function ItemChecker() {
|
|
1070
|
+
const [items, setItems] = createSignal<Item[]>([])
|
|
1071
|
+
return <div>idx: {items().findLastIndex(t => t.done)}</div>
|
|
1072
|
+
}
|
|
1073
|
+
`)
|
|
1074
|
+
expect(result.template).toContain('bf_find_last_index .Items "Done" true')
|
|
1075
|
+
})
|
|
1076
|
+
|
|
1077
|
+
test('renders findLastIndex() with complex predicate via range', () => {
|
|
1078
|
+
const result = compileAndGenerate(`
|
|
1079
|
+
"use client"
|
|
1080
|
+
import { createSignal } from "@barefootjs/client"
|
|
1081
|
+
|
|
1082
|
+
type Item = { price: number; active: boolean }
|
|
1083
|
+
|
|
1084
|
+
export function ItemFinder() {
|
|
1085
|
+
const [items, setItems] = createSignal<Item[]>([])
|
|
1086
|
+
return <div>{items().findLastIndex(t => t.price > 50 && t.active)}</div>
|
|
1087
|
+
}
|
|
1088
|
+
`)
|
|
1089
|
+
const varMatch = result.template.match(/(\$bf_r\d+) := -1/)
|
|
1090
|
+
expect(varMatch).not.toBeNull()
|
|
1091
|
+
expect(result.template).toContain(`${varMatch![1]} = $i`)
|
|
1092
|
+
expect(result.template).not.toContain('{{break}}')
|
|
1093
|
+
})
|
|
1094
|
+
|
|
1095
|
+
test('findLast() complex predicate in IR-level ternary works via preamble splitting', () => {
|
|
1096
|
+
const adapter = new GoTemplateAdapter()
|
|
1097
|
+
const ir = compileToIR(`
|
|
1098
|
+
"use client"
|
|
1099
|
+
import { createSignal } from "@barefootjs/client"
|
|
1100
|
+
|
|
1101
|
+
type Item = { price: number; category: string }
|
|
1102
|
+
|
|
1103
|
+
export function ItemFinder() {
|
|
1104
|
+
const [items, setItems] = createSignal<Item[]>([])
|
|
1105
|
+
const [type, setType] = createSignal('')
|
|
1106
|
+
return <div>{items().findLast(t => t.price > 100 && t.category === type()) ? 'yes' : 'no'}</div>
|
|
1107
|
+
}
|
|
1108
|
+
`, adapter)
|
|
1109
|
+
const output = adapter.generate(ir)
|
|
1110
|
+
expect(adapter.errors.filter(e => e.code === 'BF101')).toEqual([])
|
|
1111
|
+
expect(output.template).toMatch(/\$bf_r\d+ := ""/)
|
|
1112
|
+
expect(output.template).toContain('yes')
|
|
1113
|
+
})
|
|
1114
|
+
|
|
1115
|
+
test('findLast() complex predicate in binary expression compiles via preamble hoisting', () => {
|
|
1116
|
+
const adapter = new GoTemplateAdapter()
|
|
1117
|
+
const ir = compileToIR(`
|
|
1118
|
+
"use client"
|
|
1119
|
+
import { createSignal } from "@barefootjs/client"
|
|
1120
|
+
|
|
1121
|
+
type Item = { price: number; category: string }
|
|
1122
|
+
|
|
1123
|
+
export function ItemFinder() {
|
|
1124
|
+
const [items, setItems] = createSignal<Item[]>([])
|
|
1125
|
+
const [type, setType] = createSignal('')
|
|
1126
|
+
return <div class={items().findLast(t => t.price > 100 && t.category === type()) === 'special' ? 'highlight' : 'normal'}>test</div>
|
|
1127
|
+
}
|
|
1128
|
+
`, adapter)
|
|
1129
|
+
const output = adapter.generate(ir)
|
|
1130
|
+
expect(adapter.errors.filter(e => e.code === 'BF101')).toEqual([])
|
|
1131
|
+
expect(output.template).toMatch(/\$bf_r\d+ := ""/)
|
|
1132
|
+
expect(output.template).toContain('eq')
|
|
1133
|
+
expect(output.template).toContain('"special"')
|
|
1134
|
+
})
|
|
1135
|
+
})
|
|
1136
|
+
|
|
1037
1137
|
describe('component root scope comment propagation', () => {
|
|
1038
1138
|
test('component root in client component outputs bfScopeComment', () => {
|
|
1039
1139
|
const result = compileAndGenerate(`
|
|
@@ -1671,6 +1771,32 @@ export { A }`)
|
|
|
1671
1771
|
expect(result.template).toContain('bf_join (bf_concat .Left .Right) " "')
|
|
1672
1772
|
})
|
|
1673
1773
|
})
|
|
1774
|
+
|
|
1775
|
+
describe('.entries() / .keys() / .values() iteration shapes (#1448 Tier B)', () => {
|
|
1776
|
+
test('.entries().map(([i, v]) => ...) emits {{range $i, $v := .Items}}', () => {
|
|
1777
|
+
const result = compileAndGenerate(`function A({ items }: { items: string[] }) {
|
|
1778
|
+
return <ul>{items.entries().map(([i, v]) => <li key={i}>{i}: {v}</li>)}</ul>
|
|
1779
|
+
}
|
|
1780
|
+
export { A }`)
|
|
1781
|
+
expect(result.template).toContain('{{range $i, $v := .Items}}')
|
|
1782
|
+
})
|
|
1783
|
+
|
|
1784
|
+
test('.keys().map(k => ...) emits {{range $k, $_ := .Items}}', () => {
|
|
1785
|
+
const result = compileAndGenerate(`function A({ items }: { items: string[] }) {
|
|
1786
|
+
return <ul>{items.keys().map(k => <li key={k}>{k}</li>)}</ul>
|
|
1787
|
+
}
|
|
1788
|
+
export { A }`)
|
|
1789
|
+
expect(result.template).toContain('{{range $k, $_ := .Items}}')
|
|
1790
|
+
})
|
|
1791
|
+
|
|
1792
|
+
test('.values().map(v => ...) emits standard {{range $_, $v := .Items}}', () => {
|
|
1793
|
+
const result = compileAndGenerate(`function A({ items }: { items: string[] }) {
|
|
1794
|
+
return <ul>{items.values().map(v => <li key={v}>{v}</li>)}</ul>
|
|
1795
|
+
}
|
|
1796
|
+
export { A }`)
|
|
1797
|
+
expect(result.template).toContain('{{range $_, $v := .Items}}')
|
|
1798
|
+
})
|
|
1799
|
+
})
|
|
1674
1800
|
})
|
|
1675
1801
|
|
|
1676
1802
|
// =============================================================================
|
|
@@ -1708,6 +1834,10 @@ import { fixture as arraySortFieldDescFixture } from '../../../adapter-tests/fix
|
|
|
1708
1834
|
import { fixture as arraySortPrimitiveFixture } from '../../../adapter-tests/fixtures/methods/array-sort-primitive'
|
|
1709
1835
|
import { fixture as arraySortLocaleFixture } from '../../../adapter-tests/fixtures/methods/array-sort-locale'
|
|
1710
1836
|
import { fixture as arrayToSortedFixture } from '../../../adapter-tests/fixtures/methods/array-toSorted'
|
|
1837
|
+
// #1448 Tier B — .entries / .keys / .values iteration shapes.
|
|
1838
|
+
import { fixture as arrayEntriesFixture } from '../../../adapter-tests/fixtures/methods/array-entries'
|
|
1839
|
+
import { fixture as arrayKeysFixture } from '../../../adapter-tests/fixtures/methods/array-keys'
|
|
1840
|
+
import { fixture as arrayValuesFixture } from '../../../adapter-tests/fixtures/methods/array-values'
|
|
1711
1841
|
|
|
1712
1842
|
describe('GoTemplateAdapter - #1448 Tier A/B fixture-driven lowering pins', () => {
|
|
1713
1843
|
const cases = [
|
|
@@ -1738,6 +1868,11 @@ describe('GoTemplateAdapter - #1448 Tier A/B fixture-driven lowering pins', () =
|
|
|
1738
1868
|
{ fixture: arraySortPrimitiveFixture, expect: 'bf_sort .Nums "self" "" "numeric" "asc"' },
|
|
1739
1869
|
{ fixture: arraySortLocaleFixture, expect: 'bf_sort .Names "self" "" "string" "asc"' },
|
|
1740
1870
|
{ fixture: arrayToSortedFixture, expect: 'bf_sort .Nums "self" "" "numeric" "asc"' },
|
|
1871
|
+
// #1448 Tier B — iteration shapes. These are loop-level
|
|
1872
|
+
// patterns (range binding order), not helper function calls.
|
|
1873
|
+
{ fixture: arrayEntriesFixture, expect: '{{range $i, $v := .Items}}' },
|
|
1874
|
+
{ fixture: arrayKeysFixture, expect: '{{range $k, $_ := .Items}}' },
|
|
1875
|
+
{ fixture: arrayValuesFixture, expect: '{{range $_, $v := .Items}}' },
|
|
1741
1876
|
]
|
|
1742
1877
|
|
|
1743
1878
|
for (const { fixture, expect: expectedHelper } of cases) {
|