@barefootjs/cli 0.31.4 → 0.31.6

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.
@@ -24,6 +24,8 @@ error[BF001]: 'use client' directive required for components with createSignal
24
24
 
25
25
  ## Directive Errors (BF001–BF003)
26
26
 
27
+ <a id="bf001"></a>
28
+
27
29
  ### BF001 — Missing `"use client"` Directive
28
30
 
29
31
  **Trigger:** Reactive APIs used without `"use client"`.
@@ -46,6 +48,8 @@ import { createSignal } from '@barefootjs/client'
46
48
  export function Counter() { ... }
47
49
  ```
48
50
 
51
+ <a id="bf003"></a>
52
+
49
53
  ### BF003 — Client Component Importing Server Component
50
54
 
51
55
  **Trigger:** Client component imports from a file without `"use client"`.
@@ -56,6 +60,8 @@ export function Counter() { ... }
56
60
 
57
61
  ## Signal Errors (BF011)
58
62
 
63
+ <a id="bf011"></a>
64
+
59
65
  ### BF011 — Module-Level Reactive Declaration
60
66
 
61
67
  **Trigger:** A `createSignal` or `createMemo` call at module scope without a leading `/* @client */` directive.
@@ -100,6 +106,8 @@ export function Counter() {
100
106
 
101
107
  ## JSX Errors (BF021–BF023)
102
108
 
109
+ <a id="bf021"></a>
110
+
103
111
  ### BF021 — Unsupported JSX Pattern
104
112
 
105
113
  **Trigger:** Array method chain before `.map()` cannot compile to SSR template.
@@ -201,6 +209,8 @@ that a component-body local (`const iso = createdAt.toISOString()`) is NOT a
201
209
  workaround: it lowers to a template variable whose value the template
202
210
  backend cannot compute, and dies at render time the same way.
203
211
 
212
+ <a id="bf023"></a>
213
+
204
214
  ### BF023 — Missing Key in List
205
215
 
206
216
  **Trigger:** `.map()` loop without `key` prop.
@@ -219,8 +229,56 @@ backend cannot compute, and dies at render time the same way.
219
229
 
220
230
  ---
221
231
 
232
+ ## Template Adapter Errors (BF101)
233
+
234
+ <a id="bf101"></a>
235
+
236
+ ### BF101 — No Template-Language Lowering
237
+
238
+ **Trigger:** An expression that a JS-runtime adapter (Hono, CSR) executes verbatim has no lowering on a non-JS template adapter (Go, Mojo, Xslate, Twig, ERB, Blade, Jinja, MiniJinja). Two shapes are permanent known limitations rather than subset widenings:
239
+
240
+ **A nested `.some()` / `.find()` inside a filter predicate** ([#2320](https://github.com/piconic-ai/barefootjs/issues/2320)) — `find`-family methods return an element, not a boolean, so degrading them to their receiver would silently change predicate semantics:
241
+
242
+ ```tsx
243
+ // ❌ BF101 on Go/Mojo/Xslate/Twig/ERB/Blade/Jinja/MiniJinja
244
+ {items().filter(t => picked().some(p => p.id === t.id)).map(t => <li key={t.id}>{t.name}</li>)}
245
+ ```
246
+
247
+ **A `.map()` loop array bound to a component-scope `const` with a computed initializer** ([#2321](https://github.com/piconic-ai/barefootjs/issues/2321)) — no template adapter binds an arbitrary computed local, only a prop/param it passes straight through:
248
+
249
+ ```tsx
250
+ // ❌ BF101 on Go/Mojo/Xslate/Twig/ERB/Blade/Jinja/MiniJinja
251
+ function ReactionBar(props: { reactions: Record<string, string[]> }) {
252
+ const entries = Object.entries(props.reactions).filter(([, users]) => users.length > 0)
253
+ return <div>{entries.map(([emoji, users]) => <span key={emoji}>{emoji}</span>)}</div>
254
+ }
255
+ ```
256
+
257
+ **Escapes** — each verified by a conformance twin that compiles clean on the refusing adapter, listed best-SSR-first:
258
+
259
+ - **Pass the computed result as a prop** (`prop-precompute`) — available for the loop-source shape, wherever the array is already computable server-side. **Full server render**: the rendered result is present in the server HTML.
260
+ - **`/* @client */`** (`client-directive`) — available for both shapes, and compiles clean on every adapter. **Client-render**: the region is *empty in server HTML until hydration*. That trade is the cost of the escape, not a bug — the twin fixtures pin the empty region in their own committed `expectedHtml`.
261
+
262
+ ```tsx
263
+ // ✅ Best for the loop-source shape: pass the computed array as a prop
264
+ function ReactionBar({ entries }: { entries: [string, string[]][] }) {
265
+ return <div>{entries.map(([emoji, users]) => <span key={emoji}>{emoji}</span>)}</div>
266
+ }
267
+
268
+ // ✅ Either shape: defer to the client
269
+ {/* @client */ items().filter(t => picked().some(p => p.id === t.id)).map(t => (
270
+ <li key={t.id}>{t.name}</li>
271
+ ))}
272
+ ```
273
+
274
+ See [JSX Compatibility](../rendering/jsx-compatibility.md) for the full worked examples.
275
+
276
+ ---
277
+
222
278
  ## Component Errors (BF043–BF044)
223
279
 
280
+ <a id="bf043"></a>
281
+
224
282
  ### BF043 — Props Destructuring (Warning)
225
283
 
226
284
  **Trigger:** Props destructured in function parameter.
@@ -257,6 +315,8 @@ function Child({ initialCount }: Props) {
257
315
  }
258
316
  ```
259
317
 
318
+ <a id="bf044"></a>
319
+
260
320
  ### BF044 — Signal/Memo Getter Not Called
261
321
 
262
322
  **Trigger:** Signal/memo getter passed without calling it.
@@ -273,6 +333,8 @@ function Child({ initialCount }: Props) {
273
333
  <Child count={count()} />
274
334
  ```
275
335
 
336
+ <a id="bf054"></a>
337
+
276
338
  ### BF054 — Built-in `<Async>` / `<Region>` Used Without Import
277
339
 
278
340
  **Trigger:** A bare `<Async>` or `<Region>` tag is used without importing it
@@ -125,6 +125,7 @@ Some JavaScript expressions cannot be translated into marked template syntax. Wh
125
125
  | JSX-returning `.flatMap()` with a statement body (early `return`, `const` before the projection) | works (runs as JS) | **BF021** |
126
126
  | Nested `.filter()` / `.map()` in a filter predicate (`x => x.tags.filter(...).length > 0`) | works | works |
127
127
  | Nested `.some()` / `.find()` / `.reduce()` in a filter predicate | works | **BF101** |
128
+ | `.map()` loop array bound to a component-scope `const` with a computed initializer (e.g. `Object.entries(props.x).filter(...)`) | works | **BF101** |
128
129
  | Sort comparator that's a multi-statement block body or `localeCompare(b, locale, opts)` | works (runs as JS) | **BF021** |
129
130
  | Sort comparator that's a function reference to an imported/prop identifier, or an alias chain (`const c2 = c1`) | works (runs as JS) | **BF021** |
130
131
  | `typeof` in a filter predicate | works (runs as JS) | **BF021** |
@@ -182,6 +183,39 @@ A nested `.some()` / `.find()` / `.reduce()` still has no faithful Go/Mojo lower
182
183
  {items().filter(x => x.done)}
183
184
  ```
184
185
 
186
+ **Computed loop array (`const` with a runtime initializer):**
187
+
188
+ A `.map()` loop whose array is a bare identifier works when that identifier is a prop or a signal read, but not when it's a component-scope `const` computed from one at render time (`Object.entries(...)`, `.filter(...)`, …) — no template adapter has a binding for an arbitrary computed local, only for a prop/param it can pass straight through:
189
+
190
+ ```tsx
191
+ // ❌ BF101 on Go/Mojo/Xslate/Twig/ERB/Blade/Jinja/MiniJinja; works on Hono
192
+ type Props = { reactions: Record<string, string[]> }
193
+ function ReactionBar(props: Props) {
194
+ const entries = Object.entries(props.reactions).filter(([, users]) => users.length > 0)
195
+ return <div>{entries.map(([emoji, users]) => (
196
+ <span key={emoji}>{emoji}: {String(users.length)}</span>
197
+ ))}</div>
198
+ }
199
+
200
+ // ✅ Best: precompute the array in the parent/route handler and pass it as a prop
201
+ type Entry = [string, string[]]
202
+ function ReactionBarByProp({ entries }: { entries: Entry[] }) {
203
+ return <div>{entries.map(([emoji, users]) => (
204
+ <span key={emoji}>{emoji}: {String(users.length)}</span>
205
+ ))}</div>
206
+ }
207
+
208
+ // ✅ Or defer to the client with /* @client */
209
+ function ReactionBarClientOnly(props: Props) {
210
+ const entries = Object.entries(props.reactions).filter(([, users]) => users.length > 0)
211
+ return <div>{/* @client */ entries.map(([emoji, users]) => (
212
+ <span key={emoji}>{emoji}: {String(users.length)}</span>
213
+ ))}</div>
214
+ }
215
+ ```
216
+
217
+ The prop-passing form is the better fix — it keeps full SSR output, since the template adapters bind a plain prop array directly. `/* @client */` compiles clean too, but renders **nothing** for that region at SSR — no content until hydration/mount runs the loop client-side.
218
+
185
219
  ### Sort comparators that error on Go / Mojo
186
220
 
187
221
  **Unsupported sort comparators** (imperative block bodies, unresolved function references) — a JS-runtime adapter (Hono, CSR) runs any of these verbatim; only non-JS template backends refuse them: