@barefootjs/go-template 0.16.0 → 0.17.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.
Files changed (73) hide show
  1. package/dist/adapter/analysis/component-tree.d.ts +23 -0
  2. package/dist/adapter/analysis/component-tree.d.ts.map +1 -0
  3. package/dist/adapter/emit-context.d.ts +53 -0
  4. package/dist/adapter/emit-context.d.ts.map +1 -0
  5. package/dist/adapter/expr/helper-inline.d.ts +31 -0
  6. package/dist/adapter/expr/helper-inline.d.ts.map +1 -0
  7. package/dist/adapter/expr/url-builder.d.ts +23 -0
  8. package/dist/adapter/expr/url-builder.d.ts.map +1 -0
  9. package/dist/adapter/go-template-adapter.d.ts +302 -1071
  10. package/dist/adapter/go-template-adapter.d.ts.map +1 -1
  11. package/dist/adapter/index.js +3116 -2692
  12. package/dist/adapter/lib/compile-state.d.ts +142 -0
  13. package/dist/adapter/lib/compile-state.d.ts.map +1 -0
  14. package/dist/adapter/lib/constants.d.ts +11 -0
  15. package/dist/adapter/lib/constants.d.ts.map +1 -0
  16. package/dist/adapter/lib/go-emit.d.ts +126 -0
  17. package/dist/adapter/lib/go-emit.d.ts.map +1 -0
  18. package/dist/adapter/lib/go-naming.d.ts +39 -0
  19. package/dist/adapter/lib/go-naming.d.ts.map +1 -0
  20. package/dist/adapter/lib/ir-scope.d.ts +13 -0
  21. package/dist/adapter/lib/ir-scope.d.ts.map +1 -0
  22. package/dist/adapter/lib/types.d.ts +165 -0
  23. package/dist/adapter/lib/types.d.ts.map +1 -0
  24. package/dist/adapter/memo/ctor-lowering.d.ts +39 -0
  25. package/dist/adapter/memo/ctor-lowering.d.ts.map +1 -0
  26. package/dist/adapter/memo/memo-compute.d.ts +173 -0
  27. package/dist/adapter/memo/memo-compute.d.ts.map +1 -0
  28. package/dist/adapter/memo/memo-type.d.ts +48 -0
  29. package/dist/adapter/memo/memo-type.d.ts.map +1 -0
  30. package/dist/adapter/memo/memo-value.d.ts +64 -0
  31. package/dist/adapter/memo/memo-value.d.ts.map +1 -0
  32. package/dist/adapter/memo/template-interp.d.ts +43 -0
  33. package/dist/adapter/memo/template-interp.d.ts.map +1 -0
  34. package/dist/adapter/props/prop-types.d.ts +31 -0
  35. package/dist/adapter/props/prop-types.d.ts.map +1 -0
  36. package/dist/adapter/spread/spread-codegen.d.ts +40 -0
  37. package/dist/adapter/spread/spread-codegen.d.ts.map +1 -0
  38. package/dist/adapter/type/type-codegen.d.ts +25 -0
  39. package/dist/adapter/type/type-codegen.d.ts.map +1 -0
  40. package/dist/adapter/value/parsed-literal-to-go.d.ts +17 -0
  41. package/dist/adapter/value/parsed-literal-to-go.d.ts.map +1 -0
  42. package/dist/adapter/value/value-lowering.d.ts +46 -0
  43. package/dist/adapter/value/value-lowering.d.ts.map +1 -0
  44. package/dist/build.js +3114 -2690
  45. package/dist/index.js +3116 -2692
  46. package/dist/test-render.d.ts.map +1 -1
  47. package/package.json +3 -3
  48. package/src/__tests__/derived-state-memo.test.ts +155 -84
  49. package/src/__tests__/go-template-adapter.test.ts +398 -54
  50. package/src/__tests__/lowering-plugin.test.ts +109 -0
  51. package/src/__tests__/query-href.test.ts +179 -0
  52. package/src/adapter/analysis/component-tree.ts +174 -0
  53. package/src/adapter/emit-context.ts +59 -0
  54. package/src/adapter/expr/helper-inline.ts +274 -0
  55. package/src/adapter/expr/url-builder.ts +123 -0
  56. package/src/adapter/go-template-adapter.ts +2189 -5320
  57. package/src/adapter/lib/compile-state.ts +181 -0
  58. package/src/adapter/lib/constants.ts +24 -0
  59. package/src/adapter/lib/go-emit.ts +309 -0
  60. package/src/adapter/lib/go-naming.ts +84 -0
  61. package/src/adapter/lib/ir-scope.ts +31 -0
  62. package/src/adapter/lib/types.ts +182 -0
  63. package/src/adapter/memo/ctor-lowering.ts +267 -0
  64. package/src/adapter/memo/memo-compute.ts +661 -0
  65. package/src/adapter/memo/memo-type.ts +93 -0
  66. package/src/adapter/memo/memo-value.ts +197 -0
  67. package/src/adapter/memo/template-interp.ts +246 -0
  68. package/src/adapter/props/prop-types.ts +84 -0
  69. package/src/adapter/spread/spread-codegen.ts +458 -0
  70. package/src/adapter/type/type-codegen.ts +95 -0
  71. package/src/adapter/value/parsed-literal-to-go.ts +94 -0
  72. package/src/adapter/value/value-lowering.ts +162 -0
  73. package/src/test-render.ts +48 -22
@@ -1 +1 @@
1
- {"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,iBAAiB,CAAA;AAsBnE,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAgCD,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,EAAE,eAAe,CAAA;IACxB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACzC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAyVvF"}
1
+ {"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,iBAAiB,CAAA;AAUnE,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAgCD,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,EAAE,eAAe,CAAA;IACxB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACzC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAyVvF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/go-template",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "description": "Go html/template adapter for BarefootJS - generates Go template files from IR",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,7 +49,7 @@
49
49
  "directory": "packages/adapter-go-template"
50
50
  },
51
51
  "dependencies": {
52
- "@barefootjs/shared": "0.16.0"
52
+ "@barefootjs/shared": "0.17.1"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@barefootjs/jsx": ">=0.2.0",
@@ -57,6 +57,6 @@
57
57
  },
58
58
  "devDependencies": {
59
59
  "@barefootjs/adapter-tests": "0.1.0",
60
- "@barefootjs/jsx": "0.16.0"
60
+ "@barefootjs/jsx": "0.17.1"
61
61
  }
62
62
  }
@@ -22,10 +22,11 @@ function generate(src: string) {
22
22
  describe('Capability A: object-returning searchParams memo → computed map field', () => {
23
23
  const SRC = `
24
24
  'use client'
25
- import { createMemo, searchParams } from '@barefootjs/client'
25
+ import { createMemo, createSearchParams } from '@barefootjs/client'
26
26
  const SORT_KEYS = ['date', 'title', 'tag']
27
27
  const asSortKey = (raw) => (SORT_KEYS.includes(raw) ? raw : 'date')
28
28
  export function P() {
29
+ const [searchParams] = createSearchParams()
29
30
  const params = createMemo(() => {
30
31
  const sp = searchParams()
31
32
  return { sort: asSortKey(sp.get('sort')), tag: sp.get('tag') ?? '' }
@@ -74,8 +75,9 @@ export function P() {
74
75
  test('string-valued ternary condition falls back to nil, never invalid Go', () => {
75
76
  const src = `
76
77
  'use client'
77
- import { createMemo, searchParams } from '@barefootjs/client'
78
+ import { createMemo, createSearchParams } from '@barefootjs/client'
78
79
  export function P() {
80
+ const [searchParams] = createSearchParams()
79
81
  const params = createMemo(() => {
80
82
  const sp = searchParams()
81
83
  return { label: sp.get('tag') ? 'has' : 'none' }
@@ -95,8 +97,9 @@ export function P() {
95
97
  test('block-body memo with control flow falls back to nil', () => {
96
98
  const src = `
97
99
  'use client'
98
- import { createMemo, searchParams } from '@barefootjs/client'
100
+ import { createMemo, createSearchParams } from '@barefootjs/client'
99
101
  export function P() {
102
+ const [searchParams] = createSearchParams()
100
103
  const params = createMemo(() => {
101
104
  const sp = searchParams()
102
105
  if (sp.get('x')) return { sort: 'early' }
@@ -115,10 +118,11 @@ describe('Capability B: inline local pure helper calls at attribute call sites',
115
118
  test('sortClass(k) inlines to a conditional, not a .SortClass method call', () => {
116
119
  const src = `
117
120
  'use client'
118
- import { createMemo, searchParams } from '@barefootjs/client'
121
+ import { createMemo, createSearchParams } from '@barefootjs/client'
119
122
  const SORT_KEYS = ['date', 'title', 'tag']
120
123
  const asSortKey = (raw) => (SORT_KEYS.includes(raw) ? raw : 'date')
121
124
  export function P() {
125
+ const [searchParams] = createSearchParams()
122
126
  const params = createMemo(() => {
123
127
  const sp = searchParams()
124
128
  return { sort: asSortKey(sp.get('sort')), tag: sp.get('tag') ?? '' }
@@ -139,8 +143,9 @@ export function P() {
139
143
  test('tagClass(t) inlines inside a loop, resolving the loop var and root memo', () => {
140
144
  const src = `
141
145
  'use client'
142
- import { createMemo, searchParams } from '@barefootjs/client'
146
+ import { createMemo, createSearchParams } from '@barefootjs/client'
143
147
  export function P(props: { tags: string[] }) {
148
+ const [searchParams] = createSearchParams()
144
149
  const params = createMemo(() => {
145
150
  const sp = searchParams()
146
151
  return { tag: sp.get('tag') ?? '' }
@@ -214,51 +219,22 @@ export function P(props: { xs: string[] }) {
214
219
  })
215
220
  })
216
221
 
217
- describe('Capability C2: URL-builder helpers bf_query + derived Root field', () => {
218
- const SRC = `
222
+ describe('Derived string-constcomputed Go struct field (#1945)', () => {
223
+ // The trailing-slash strip `(props.base ?? '').replace(/\/+$/, '')` lowers in
224
+ // the `NewXxxProps` constructor to `strings.TrimRight`, and `base || '/'`
225
+ // becomes a computed `Root` field referenced by the template. (The former
226
+ // `URLSearchParams` href-builder recognizer that shared this fixture was
227
+ // retired in #2042 once `queryHref` replaced it.)
228
+ test('derived `root` const (trailing-slash strip) becomes a computed Root field', () => {
229
+ const src = `
219
230
  'use client'
220
- import { createMemo, searchParams } from '@barefootjs/client'
221
231
  export function P(props: { base: string }) {
222
- const params = createMemo(() => {
223
- const sp = searchParams()
224
- return { sort: sp.get('sort') ?? '', tag: sp.get('tag') ?? '' }
225
- })
226
232
  const base = (props.base ?? '').replace(/\\/+$/, '')
227
233
  const root = base || '/'
228
- const hrefFor = (sort: string, tag: string) => {
229
- const u = new URLSearchParams()
230
- if (sort !== 'date') u.set('sort', sort)
231
- if (tag) u.set('tag', tag)
232
- const s = u.toString()
233
- return s ? \`\${root}?\${s}\` : root
234
- }
235
- const sortHref = (k) => hrefFor(k, params().tag)
236
- const tagHref = (t) => hrefFor(params().sort, t)
237
- return (
238
- <div>
239
- <a href={sortHref('title')}>s</a>
240
- {props.base ? <a href={tagHref('go')}>t</a> : null}
241
- </div>
242
- )
234
+ return <a href={root}>home</a>
243
235
  }
244
236
  `
245
-
246
- test('sortHref/tagHref lower to bf_query, not a .SortHref method call', () => {
247
- const { template } = generate(SRC)
248
- expect(template).not.toContain('.SortHref')
249
- expect(template).not.toContain('.TagHref')
250
- expect(template).toContain('bf_query .Root')
251
- })
252
-
253
- test('guarded set() calls become bool include triples', () => {
254
- const { template } = generate(SRC)
255
- // sort !== 'date' guard (runtime sort case via tagHref) + tag truthiness.
256
- expect(template).toContain('(ne (bf_string .Params.Sort) "date") "sort"')
257
- expect(template).toContain('(ne .Params.Tag "") "tag"')
258
- })
259
-
260
- test('derived `root` const becomes a computed Root field', () => {
261
- const { types } = generate(SRC)
237
+ const { types } = generate(src)
262
238
  expect(types).toContain('Root string `json:"-"`')
263
239
  expect(types).toContain(
264
240
  'Root: func() string { v := strings.TrimRight(in.Base, "/"); if v != "" { return v }; return "/" }(),',
@@ -266,45 +242,6 @@ export function P(props: { base: string }) {
266
242
  expect(types).toContain('"strings"')
267
243
  })
268
244
 
269
- // A `&&` guard is NOT a Go bool (Go's `and` returns an operand); it must be
270
- // truthiness-wrapped so `bf_query`'s `include` receives a real bool (#1945 review).
271
- test('a && guard is wrapped to a bool, not passed as bare `and`', () => {
272
- const src = `
273
- 'use client'
274
- import { createMemo, searchParams } from '@barefootjs/client'
275
- export function P(props: { base: string }) {
276
- const params = createMemo(() => { const sp = searchParams(); return { tag: sp.get('tag') ?? '' } })
277
- const root = (props.base ?? '') || '/'
278
- const hrefFor = (sort: string, tag: string) => {
279
- const u = new URLSearchParams()
280
- if (sort && tag) u.set('both', sort)
281
- return u.toString() ? \`\${root}?\${u}\` : root
282
- }
283
- const h = (k) => hrefFor(k, params().tag)
284
- return <a href={h('x')}>x</a>
285
- }
286
- `
287
- const { template } = generate(src)
288
- expect(template).toContain('(ne (and "x" .Params.Tag) "")')
289
- })
290
-
291
- // A URL-builder helper whose return isn't the conditional with-query/base
292
- // shape must not be lowered to bf_query (#1945 review) — it falls back to the
293
- // method-call form.
294
- test('a builder returning a non-conditional shape is not lowered to bf_query', () => {
295
- const src = `
296
- 'use client'
297
- import { searchParams } from '@barefootjs/client'
298
- export function P() {
299
- const bad = (sort: string) => { const u = new URLSearchParams(); u.set('s', sort); return u.toString() }
300
- return <a href={bad('x')}>x</a>
301
- }
302
- `
303
- const { template } = generate(src)
304
- expect(template).not.toContain('bf_query')
305
- expect(template).toContain('.Bad')
306
- })
307
-
308
245
  // A non-string derived const referenced by the template must not be emitted
309
246
  // as a `string` field with a non-string initializer (#1945 review).
310
247
  test('a numeric derived const is not emitted as a string field', () => {
@@ -338,9 +275,10 @@ describe('Capability D: array-memo .length → handler-filled loop slice count',
338
275
  test('visible().length lowers to len .<Slice>, not the nil memo field', () => {
339
276
  const src = `
340
277
  'use client'
341
- import { createMemo, searchParams } from '@barefootjs/client'
278
+ import { createMemo, createSearchParams } from '@barefootjs/client'
342
279
  import { Row } from './Row'
343
280
  export function P(props: { items: { id: string }[] }) {
281
+ const [searchParams] = createSearchParams()
344
282
  const params = createMemo(() => {
345
283
  const sp = searchParams()
346
284
  return { tag: sp.get('tag') ?? '' }
@@ -369,3 +307,136 @@ export function P(props: { items: { id: string }[] }) {
369
307
  expect(template).toContain('len .Items')
370
308
  })
371
309
  })
310
+
311
+ describe('typed memo bodies parse from the type-stripped computation (#1976 review)', () => {
312
+ const mk = (body: string) => `
313
+ "use client"
314
+ import { createSignal, createMemo } from '@barefootjs/client'
315
+ export function T() {
316
+ const [count, setCount] = createSignal(3)
317
+ const doubled = createMemo(() => ${body})
318
+ return <div>{doubled()}</div>
319
+ }`
320
+
321
+ // `MemoInfo.parsed` is parsed from the type-STRIPPED body (`ctx.getJS`), not
322
+ // the raw source — otherwise TypeScript-only syntax (`as T`, `!`, satisfies)
323
+ // would make `parseExpression` bail, `parsed` would be undefined, and the
324
+ // adapter would lose the arithmetic shape it used to match on the stripped
325
+ // `computation` (changing the SSR default).
326
+ test('a TS-annotated memo body yields the same SSR as the untyped form', () => {
327
+ const typed = generate(mk('(count() as number) * 2'))
328
+ const untyped = generate(mk('count() * 2'))
329
+ expect(typed.types).toContain('Doubled: 3 * 2,')
330
+ expect(typed.types).toBe(untyped.types)
331
+ })
332
+ })
333
+
334
+ // #2040 PR-B: guard-and-return-const block memos lower via the analyzer fold
335
+ // (`MemoInfo.parsed`), with a tolerant `parsedBlock` fallback for blocks the
336
+ // fold refuses (so the SSR const bake never regresses vs. the old statement
337
+ // walker).
338
+ describe('Capability C: guard-const block memo via fold (#2040 PR-B)', () => {
339
+ test('folded guard memo bakes the module-const array (not nil)', () => {
340
+ const { types } = generate(`
341
+ "use client"
342
+ import { createSignal, createMemo } from "@barefootjs/client"
343
+ const ALL = ['a', 'b', 'c']
344
+ export function Tags() {
345
+ const [sel, setSel] = createSignal<string | null>(null)
346
+ const visible = createMemo(() => {
347
+ const k = sel()
348
+ if (!k) return ALL
349
+ return ALL.filter(t => t === k)
350
+ })
351
+ return <ul>{visible().map(t => (<li key={t}>{t}</li>))}</ul>
352
+ }
353
+ `)
354
+ expect(types).toContain(`[]interface{}{"a", "b", "c"}`)
355
+ })
356
+
357
+ test('guard memo with an impure tail binding still bakes the const (fallback path)', () => {
358
+ // `const n = ext()` is impure (not a reactive getter) and used more than
359
+ // once in the tail, so `foldBlockToExpr` refuses → `parsed` is unset. The
360
+ // tolerant `parsedBlock` fallback must still bake `ALL`; otherwise the first
361
+ // server render is an empty list (regression vs. main). (PR #2053 review #1)
362
+ const { types } = generate(`
363
+ "use client"
364
+ import { createSignal, createMemo } from "@barefootjs/client"
365
+ const ALL = ['a', 'b', 'c']
366
+ function ext() { return 1 }
367
+ export function Tags() {
368
+ const [sel, setSel] = createSignal<string | null>(null)
369
+ const visible = createMemo(() => {
370
+ const k = sel()
371
+ const n = ext()
372
+ if (!k) return ALL
373
+ return ALL.filter(t => t.length === n).concat(ALL.slice(n))
374
+ })
375
+ return <ul>{visible().map(t => (<li key={t}>{t}</li>))}</ul>
376
+ }
377
+ `)
378
+ expect(types).toContain(`[]interface{}{"a", "b", "c"}`)
379
+ })
380
+
381
+ test('early-return string-const block memo bakes the initial branch (#2040 scope)', () => {
382
+ // `() => { if (vert()) return A; return B }` folds to `vert() ? A : B`; with
383
+ // `vert` starting false the SSR value is B. Now lowered via the general
384
+ // `memoInitialFromParsedBody` conditional path. (PR #2053 review #2)
385
+ const { types } = generate(`
386
+ "use client"
387
+ import { createSignal, createMemo } from "@barefootjs/client"
388
+ const A = 'flex-col'
389
+ const B = 'flex'
390
+ export function Box() {
391
+ const [vert, setVert] = createSignal(false)
392
+ const cls = createMemo(() => {
393
+ if (vert()) return A
394
+ return B
395
+ })
396
+ return <div className={cls()}>x</div>
397
+ }
398
+ `)
399
+ expect(types).toContain('"flex"')
400
+ })
401
+ })
402
+
403
+ // #2040 PR-C: the object-returning searchParams memo lowers via the general
404
+ // fold (`foldBlockToExpr` with `searchParams` treated as a pure idempotent
405
+ // read), not a bespoke statement walk.
406
+ describe('Capability D: searchParams object memo via fold (#2040 PR-C)', () => {
407
+ test('object memo using searchParams() directly (no const binding) lowers', () => {
408
+ // The old statement walk required a `const sp = searchParams()` binding to
409
+ // populate its env; the fold inlines any binding, so a direct
410
+ // `searchParams().get('k')` in the returned object now lowers too.
411
+ const { types } = generate(`
412
+ "use client"
413
+ import { createMemo, createSearchParams } from "@barefootjs/client"
414
+ export function PostList() {
415
+ const [searchParams] = createSearchParams()
416
+ const params = createMemo(() => {
417
+ return { tag: searchParams().get('tag') ?? '' }
418
+ })
419
+ return <div>{params().tag}</div>
420
+ }
421
+ `)
422
+ expect(types).toContain('map[string]interface{}{')
423
+ expect(types).toContain('"Tag":')
424
+ expect(types).toContain('in.SearchParams.Get("tag")')
425
+ })
426
+
427
+ test('aliased env-signal getter still lowers (local-name oracle)', () => {
428
+ const { types } = generate(`
429
+ "use client"
430
+ import { createMemo, createSearchParams } from "@barefootjs/client"
431
+ export function PostList() {
432
+ const [sp] = createSearchParams()
433
+ const params = createMemo(() => {
434
+ const q = sp()
435
+ return { tag: q.get('tag') ?? '' }
436
+ })
437
+ return <div>{params().tag}</div>
438
+ }
439
+ `)
440
+ expect(types).toContain('in.SearchParams.Get("tag")')
441
+ })
442
+ })