@barefootjs/cli 0.1.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/docs/core/README.md +84 -0
- package/dist/docs/core/adapters/adapter-architecture.md +262 -0
- package/dist/docs/core/adapters/csr.md +78 -0
- package/dist/docs/core/adapters/custom-adapter.md +357 -0
- package/dist/docs/core/adapters/go-template-adapter.md +269 -0
- package/dist/docs/core/adapters/hono-adapter.md +199 -0
- package/dist/docs/core/adapters.md +37 -0
- package/dist/docs/core/advanced/code-splitting.md +142 -0
- package/dist/docs/core/advanced/compiler-internals.md +331 -0
- package/dist/docs/core/advanced/error-codes.md +261 -0
- package/dist/docs/core/advanced/ir-schema.md +65 -0
- package/dist/docs/core/advanced/performance.md +115 -0
- package/dist/docs/core/advanced/xyflow-browser-bundle.md +69 -0
- package/dist/docs/core/advanced.md +11 -0
- package/dist/docs/core/components/children-slots.md +150 -0
- package/dist/docs/core/components/component-authoring.md +207 -0
- package/dist/docs/core/components/context-api.md +236 -0
- package/dist/docs/core/components/portals.md +192 -0
- package/dist/docs/core/components/props-type-safety.md +97 -0
- package/dist/docs/core/components/styling.md +37 -0
- package/dist/docs/core/components.md +19 -0
- package/dist/docs/core/core-concepts/ai-native.md +83 -0
- package/dist/docs/core/core-concepts/backend-freedom.md +31 -0
- package/dist/docs/core/core-concepts/how-it-works.md +147 -0
- package/dist/docs/core/core-concepts/mpa-style.md +36 -0
- package/dist/docs/core/core-concepts/reactivity.md +35 -0
- package/dist/docs/core/core-concepts.md +28 -0
- package/dist/docs/core/introduction.md +87 -0
- package/dist/docs/core/llms.txt +53 -0
- package/dist/docs/core/quick-start.mdx +160 -0
- package/dist/docs/core/reactivity/create-effect.md +125 -0
- package/dist/docs/core/reactivity/create-memo.md +91 -0
- package/dist/docs/core/reactivity/create-signal.md +128 -0
- package/dist/docs/core/reactivity/on-cleanup.md +67 -0
- package/dist/docs/core/reactivity/on-mount.md +70 -0
- package/dist/docs/core/reactivity/props-reactivity.md +91 -0
- package/dist/docs/core/reactivity/untrack.md +69 -0
- package/dist/docs/core/reactivity.md +28 -0
- package/dist/docs/core/rendering/client-directive.md +82 -0
- package/dist/docs/core/rendering/fragment.md +43 -0
- package/dist/docs/core/rendering/jsx-compatibility.md +163 -0
- package/dist/docs/core/rendering.md +14 -0
- package/dist/index.js +25490 -0
- package/dist/tokens.json +74 -0
- package/package.json +37 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: How It Works
|
|
3
|
+
description: Two-phase compilation and hydration markers
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How It Works
|
|
7
|
+
|
|
8
|
+
## Two-Phase Compilation
|
|
9
|
+
|
|
10
|
+
One JSX source file produces two outputs:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
JSX Source
|
|
14
|
+
↓
|
|
15
|
+
[Phase 1] Analyze + Transform → IR (Intermediate Representation)
|
|
16
|
+
↓
|
|
17
|
+
[Phase 2a] IR → Marked Template (server)
|
|
18
|
+
[Phase 2b] IR → Client JS (browser)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Phase 1** produces a JSON IR tree — component structure, reactive expressions, event handlers, type information. Backend-independent.
|
|
22
|
+
|
|
23
|
+
**Phase 2** generates:
|
|
24
|
+
|
|
25
|
+
- **Marked Template** — HTML with `bf-*` attributes marking interactive elements. The adapter determines the format.
|
|
26
|
+
- **Client JS** — Creates signals, wires effects, binds event handlers to marked elements.
|
|
27
|
+
|
|
28
|
+
### Counter Example
|
|
29
|
+
|
|
30
|
+
Source:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
"use client"
|
|
34
|
+
import { createSignal } from '@barefootjs/client'
|
|
35
|
+
|
|
36
|
+
export function Counter() {
|
|
37
|
+
const [count, setCount] = createSignal(0)
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<button onClick={() => setCount(n => n + 1)}>
|
|
41
|
+
Count: {count()}
|
|
42
|
+
</button>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The IR records:
|
|
48
|
+
- Signal `count` with initial value `0`
|
|
49
|
+
- Reactive text expression `count()` → slot `s0`
|
|
50
|
+
- Click handler on button → slot `s1`
|
|
51
|
+
|
|
52
|
+
Marked template (Phase 2a):
|
|
53
|
+
|
|
54
|
+
<!-- tabs:adapter -->
|
|
55
|
+
<!-- tab:Hono -->
|
|
56
|
+
```tsx
|
|
57
|
+
export function Counter({ __instanceId, ... }) {
|
|
58
|
+
const __scopeId = __instanceId || `Counter_${Math.random().toString(36).slice(2, 8)}`
|
|
59
|
+
const count = () => 0 // server-side stub
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<button bf-s={__scopeId} bf="s1">
|
|
63
|
+
Count: {bfText("s0")}{count()}{bfTextEnd()}
|
|
64
|
+
</button>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
<!-- tab:Go Template -->
|
|
69
|
+
```go-template
|
|
70
|
+
{{define "Counter"}}
|
|
71
|
+
<button bf-s="{{bfScopeAttr .}}" bf="s1">
|
|
72
|
+
Count: {{bfTextStart "s0"}}{{.Count}}{{bfTextEnd}}
|
|
73
|
+
</button>
|
|
74
|
+
{{end}}
|
|
75
|
+
```
|
|
76
|
+
<!-- /tabs -->
|
|
77
|
+
|
|
78
|
+
Client JS (Phase 2b):
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
import { $, $t, createEffect, createSignal, hydrate } from '@barefootjs/client'
|
|
82
|
+
|
|
83
|
+
export function initCounter(__scope, _p = {}) {
|
|
84
|
+
if (!__scope) return
|
|
85
|
+
|
|
86
|
+
const [count, setCount] = createSignal(0)
|
|
87
|
+
|
|
88
|
+
const [_s1] = $(__scope, 's1') // element lookup
|
|
89
|
+
const [_s0] = $t(__scope, 's0') // text node lookup
|
|
90
|
+
|
|
91
|
+
createEffect(() => {
|
|
92
|
+
const __val = count()
|
|
93
|
+
if (_s0) _s0.nodeValue = String(__val ?? '')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
if (_s1) _s1.addEventListener('click', () => { setCount(n => n + 1) })
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
hydrate('Counter', {
|
|
100
|
+
init: initCounter,
|
|
101
|
+
template: (_p) => `<button bf="s1"> Count: <!--bf:s0-->${(0)}<!--/--></button>`
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
See [Compiler Internals](../advanced/compiler-internals.md) and [IR Schema Reference](../advanced/ir-schema.md).
|
|
106
|
+
|
|
107
|
+
## Hydration
|
|
108
|
+
|
|
109
|
+
Marker-driven hydration attaches behavior to server-rendered HTML.
|
|
110
|
+
|
|
111
|
+
### Markers
|
|
112
|
+
|
|
113
|
+
`bf-*` attributes in the marked template tell client JS where to attach:
|
|
114
|
+
|
|
115
|
+
| Marker | Purpose | Example |
|
|
116
|
+
|--------|---------|---------|
|
|
117
|
+
| `bf-s` | Component scope boundary (`~` = child) | `<div bf-s="Counter_a1b2">` |
|
|
118
|
+
| `bf` | Interactive element (slot) | `<p bf="s0">` |
|
|
119
|
+
| `bf-p` | Serialized props JSON | `<div bf-p='{"initial":5}'>` |
|
|
120
|
+
| `bf-c` | Conditional block | `<div bf-c="s2">` |
|
|
121
|
+
| `bf-po` | Portal owner scope ID | `<div bf-po="Dialog_a1b2">` |
|
|
122
|
+
| `bf-pi` | Portal container ID | `<div bf-pi="bf-portal-1">` |
|
|
123
|
+
| `bf-pp` | Portal placeholder | `<template bf-pp="bf-portal-1">` |
|
|
124
|
+
| `bf-i` | List item marker | `<li bf-i>` |
|
|
125
|
+
|
|
126
|
+
### Flow
|
|
127
|
+
|
|
128
|
+
1. Server renders HTML with markers, embeds props in `bf-p`
|
|
129
|
+
2. Browser loads client JS
|
|
130
|
+
3. `hydrate()` finds uninitialized `bf-s` elements
|
|
131
|
+
4. Init function runs per scope — signals, effects, handlers
|
|
132
|
+
5. Runtime tracks scopes to prevent double initialization
|
|
133
|
+
|
|
134
|
+
### Scoped Queries
|
|
135
|
+
|
|
136
|
+
`$()` and `$t()` search within a scope, excluding child component scopes:
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<div bf-s="TodoApp_x1">
|
|
140
|
+
<h1 bf="s0">Todo</h1>
|
|
141
|
+
<div bf-s="~TodoItem_y1">
|
|
142
|
+
<span bf="s0">Buy milk</span>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`$(__scope, 's0')` in TodoApp finds `<h1>`, not the `<span>` inside TodoItem. The `~` prefix marks a child scope excluded from parent queries.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: MPA-style Development
|
|
3
|
+
description: Server-rendering by default, with client JavaScript only where you need it
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MPA-style Development
|
|
7
|
+
|
|
8
|
+
Add interactive components to server-rendered pages without changing your architecture.
|
|
9
|
+
|
|
10
|
+
Existing options require trade-offs:
|
|
11
|
+
|
|
12
|
+
- **jQuery / vanilla JS** — No component model. Hard to maintain at scale.
|
|
13
|
+
- **SPA framework (Next.js, Nuxt)** — Even with server components, you adopt the framework's build pipeline, routing, and deployment model.
|
|
14
|
+
- **Islands (Astro, Fresh)** — Server rendering with selective hydration, but your server must be Astro or Fresh.
|
|
15
|
+
|
|
16
|
+
BarefootJS outputs templates for your existing server. The compiler is a build step — your routing and deployment don't change. `"use client"` marks the components that need interactivity; only those ship JavaScript:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
// ProductPage.tsx — server component
|
|
20
|
+
import { AddToCart } from './AddToCart' // "use client"
|
|
21
|
+
import { ReviewStars } from './ReviewStars' // "use client"
|
|
22
|
+
|
|
23
|
+
export function ProductPage({ product }) {
|
|
24
|
+
return (
|
|
25
|
+
<div>
|
|
26
|
+
<h1>{product.name}</h1>
|
|
27
|
+
<p>{product.description}</p>
|
|
28
|
+
<img src={product.image} />
|
|
29
|
+
<ReviewStars rating={product.rating} />
|
|
30
|
+
<AddToCart productId={product.id} />
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`h1`, `p`, `img` produce zero JS. Only `ReviewStars` and `AddToCart` ship client JavaScript. Server-rendered HTML is visible immediately; interactive components become functional after hydration.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Fine-grained Reactivity
|
|
3
|
+
description: Signal-based reactivity — no virtual DOM, updates at the DOM node level
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fine-grained Reactivity
|
|
7
|
+
|
|
8
|
+
The compiler analyzes which DOM nodes depend on which signals and generates code that connects them at hydration. When state changes, only that DOM node updates — no virtual DOM, no component re-render.
|
|
9
|
+
|
|
10
|
+
Inspired by [SolidJS](https://www.solidjs.com/). The key difference from React: **components run once**, not on every state change.
|
|
11
|
+
|
|
12
|
+
## Signals, Effects, Memos
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
const [count, setCount] = createSignal(0) // reactive value
|
|
16
|
+
const doubled = createMemo(() => count() * 2) // cached derived value
|
|
17
|
+
|
|
18
|
+
createEffect(() => {
|
|
19
|
+
console.log('Count is:', count()) // re-runs when count changes
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
setCount(1) // triggers the effect, recomputes doubled
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The getter is a function call — `count()`, not `count`. The runtime tracks which signals each effect reads. No dependency arrays.
|
|
26
|
+
|
|
27
|
+
## How Updates Reach the DOM
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
setCount(1) → signal notifies subscribers → effect updates the DOM node
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The compiler analyzed which DOM node depends on `count` and generated an effect that updates it directly. No tree diffing at runtime.
|
|
34
|
+
|
|
35
|
+
For the full API, see [Reactivity](../reactivity.md).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Core Concepts
|
|
3
|
+
description: The four design principles and a technical overview of how BarefootJS works
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Core Concepts
|
|
7
|
+
|
|
8
|
+
BarefootJS compiles JSX into server templates and minimal client JS for any backend. No SPA framework, no Node.js lock-in.
|
|
9
|
+
|
|
10
|
+
## Backend Freedom
|
|
11
|
+
|
|
12
|
+
Server rendering with JSX usually means running Node.js. BarefootJS compiles JSX to native templates for Hono, Go `html/template`, or any custom adapter — no Node.js at serving time.
|
|
13
|
+
|
|
14
|
+
## MPA-style Development
|
|
15
|
+
|
|
16
|
+
Components render to static HTML by default. `"use client"` marks those that need interactivity — only they ship JavaScript. Your routing, data fetching, and templates don't change.
|
|
17
|
+
|
|
18
|
+
## Fine-grained Reactivity
|
|
19
|
+
|
|
20
|
+
The compiler analyzes which DOM nodes depend on which signals and generates code that connects them. When state changes, only the affected node updates — no virtual DOM, no component re-render.
|
|
21
|
+
|
|
22
|
+
## AI-native Development
|
|
23
|
+
|
|
24
|
+
`renderToTest()` verifies component structure, signals, and accessibility against the compiler's IR in milliseconds. E2E tests are still needed for real interactions, but structural issues are caught fast. The `bf` CLI provides discovery, scaffolding, and debugging for humans and AI agents.
|
|
25
|
+
|
|
26
|
+
## How It Works
|
|
27
|
+
|
|
28
|
+
Two-phase compilation and hydration markers — a technical deep-dive.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Introduction
|
|
3
|
+
description: What is BarefootJS — JSX to server template + client JS compiler
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Introduction
|
|
7
|
+
|
|
8
|
+
## What is BarefootJS?
|
|
9
|
+
|
|
10
|
+
BarefootJS is a compiler that transforms JSX components into server-rendered templates and minimal client-side JavaScript.
|
|
11
|
+
|
|
12
|
+
Write familiar JSX with fine-grained reactivity — the compiler splits it into a **marked template** for your backend and a **tiny hydration script** for the browser.
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
"use client"
|
|
16
|
+
import { createSignal } from '@barefootjs/client'
|
|
17
|
+
|
|
18
|
+
export function Counter() {
|
|
19
|
+
const [count, setCount] = createSignal(0)
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<button onClick={() => setCount(n => n + 1)}>
|
|
23
|
+
Count: {count()}
|
|
24
|
+
</button>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This single file compiles into two outputs:
|
|
30
|
+
|
|
31
|
+
<!-- tabs:adapter -->
|
|
32
|
+
<!-- tab:Hono -->
|
|
33
|
+
**Marked template** — Renders static HTML with hydration markers:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
export function Counter({ __instanceId, ... }) {
|
|
37
|
+
const __scopeId = __instanceId || `Counter_${Math.random().toString(36).slice(2, 8)}`
|
|
38
|
+
const count = () => 0
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<button bf-s={__scopeId} bf="s1">
|
|
42
|
+
Count: {bfText("s0")}{count()}{bfTextEnd()}
|
|
43
|
+
</button>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
<!-- tab:Go Template -->
|
|
49
|
+
**Marked template** — Go `html/template` with hydration markers:
|
|
50
|
+
|
|
51
|
+
```go-template
|
|
52
|
+
{{define "Counter"}}
|
|
53
|
+
<button bf-s="{{bfScopeAttr .}}" bf="s1">
|
|
54
|
+
Count: {{bfTextStart "s0"}}{{.Count}}{{bfTextEnd}}
|
|
55
|
+
</button>
|
|
56
|
+
{{end}}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
<!-- /tabs -->
|
|
60
|
+
|
|
61
|
+
**Client script** — Wires up only the interactive parts:
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
import { $, $t, createEffect, createSignal, hydrate } from '@barefootjs/client'
|
|
65
|
+
|
|
66
|
+
export function initCounter(__scope, _p = {}) {
|
|
67
|
+
if (!__scope) return
|
|
68
|
+
|
|
69
|
+
const [count, setCount] = createSignal(0)
|
|
70
|
+
|
|
71
|
+
const [_s1] = $(__scope, 's1') // find element with bf="s1"
|
|
72
|
+
const [_s0] = $t(__scope, 's0') // find text node at <!--bf:s0-->
|
|
73
|
+
|
|
74
|
+
createEffect(() => {
|
|
75
|
+
const __val = count()
|
|
76
|
+
if (_s0) _s0.nodeValue = String(__val ?? '')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
if (_s1) _s1.addEventListener('click', () => { setCount(n => n + 1) })
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
hydrate('Counter', {
|
|
83
|
+
init: initCounter,
|
|
84
|
+
template: (_p) => `<button bf="s1"> Count: <!--bf:s0-->${(0)}<!--/--></button>`
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# BarefootJS
|
|
2
|
+
|
|
3
|
+
> JSX -> Marked Template + client JS compiler. Signal-based reactivity for any backend.
|
|
4
|
+
|
|
5
|
+
## Core Concepts
|
|
6
|
+
|
|
7
|
+
- [AI-native Development](https://barefootjs.dev/docs/core-concepts/ai-native.md): Millisecond IR tests and the bf CLI for human + agent workflows
|
|
8
|
+
- [Backend Freedom](https://barefootjs.dev/docs/core-concepts/backend-freedom.md): How adapters let the same JSX run on any server — Hono, Go, and beyond
|
|
9
|
+
- [How It Works](https://barefootjs.dev/docs/core-concepts/how-it-works.md): Two-phase compilation and hydration markers
|
|
10
|
+
- [MPA-style Development](https://barefootjs.dev/docs/core-concepts/mpa-style.md): Server-rendering by default, with client JavaScript only where you need it
|
|
11
|
+
- [Fine-grained Reactivity](https://barefootjs.dev/docs/core-concepts/reactivity.md): Signal-based reactivity — no virtual DOM, updates at the DOM node level
|
|
12
|
+
|
|
13
|
+
## Reactivity
|
|
14
|
+
|
|
15
|
+
- [createEffect](https://barefootjs.dev/docs/reactivity/create-effect.md): Runs a function and re-runs it whenever its tracked signal dependencies change.
|
|
16
|
+
- [createMemo](https://barefootjs.dev/docs/reactivity/create-memo.md): Creates a cached derived value that recomputes only when its dependencies change.
|
|
17
|
+
- [createSignal](https://barefootjs.dev/docs/reactivity/create-signal.md): Creates a reactive getter/setter pair for managing state.
|
|
18
|
+
- [onCleanup](https://barefootjs.dev/docs/reactivity/on-cleanup.md): Registers a cleanup function that runs when the owning effect re-runs or the component is destroyed.
|
|
19
|
+
- [onMount](https://barefootjs.dev/docs/reactivity/on-mount.md): Runs a callback once when the component initializes, without tracking signal dependencies.
|
|
20
|
+
- [Props Reactivity](https://barefootjs.dev/docs/reactivity/props-reactivity.md): How prop access patterns determine whether reactive updates propagate in BarefootJS components.
|
|
21
|
+
- [untrack](https://barefootjs.dev/docs/reactivity/untrack.md): Executes a function without tracking signal dependencies in the current reactive context.
|
|
22
|
+
|
|
23
|
+
## Rendering
|
|
24
|
+
|
|
25
|
+
- [/* @client */ Directive](https://barefootjs.dev/docs/rendering/client-directive.md): Mark JSX expressions for client-only evaluation when the compiler cannot translate them to server templates.
|
|
26
|
+
- [Fragment](https://barefootjs.dev/docs/rendering/fragment.md): Using fragments to render children without a wrapper element, including transparent fragment behavior.
|
|
27
|
+
- [JSX Compatibility](https://barefootjs.dev/docs/rendering/jsx-compatibility.md): Standard JSX syntax support in BarefootJS, including control flow and common patterns from React and SolidJS.
|
|
28
|
+
|
|
29
|
+
## Components
|
|
30
|
+
|
|
31
|
+
- [Children & Slots](https://barefootjs.dev/docs/components/children-slots.md): Accept nested JSX content via the children prop and enable polymorphic rendering with the Slot component.
|
|
32
|
+
- [Component Authoring](https://barefootjs.dev/docs/components/component-authoring.md): Learn how to write server and client components in BarefootJS using JSX functions.
|
|
33
|
+
- [Context API](https://barefootjs.dev/docs/components/context-api.md): Share state with deeply nested children without prop drilling using createContext and useContext.
|
|
34
|
+
- [Portals](https://barefootjs.dev/docs/components/portals.md): Render elements outside their parent DOM hierarchy for overlays, modals, and tooltips.
|
|
35
|
+
- [Props & Type Safety](https://barefootjs.dev/docs/components/props-type-safety.md): Type component props with TypeScript interfaces and preserve type information through compilation.
|
|
36
|
+
- [Style Overrides](https://barefootjs.dev/docs/components/styling.md): How user-supplied classes override component base classes via CSS Cascade Layers
|
|
37
|
+
|
|
38
|
+
## Adapters
|
|
39
|
+
|
|
40
|
+
- [Adapter Architecture](https://barefootjs.dev/docs/adapters/adapter-architecture.md): How adapters convert the compiler's IR into server-renderable template formats.
|
|
41
|
+
- [CSR (Client-Side Rendering)](https://barefootjs.dev/docs/adapters/csr.md): Render BarefootJS components directly in the browser without a server-rendered template.
|
|
42
|
+
- [Writing a Custom Adapter](https://barefootjs.dev/docs/adapters/custom-adapter.md): Step-by-step guide to building a custom adapter using the TestAdapter as a reference.
|
|
43
|
+
- [Go Template Adapter](https://barefootjs.dev/docs/adapters/go-template-adapter.md): Generate Go html/template files and type definitions from the compiler's IR.
|
|
44
|
+
- [Hono Adapter](https://barefootjs.dev/docs/adapters/hono-adapter.md): Generate Hono JSX templates from the compiler's IR for Hono-based servers.
|
|
45
|
+
|
|
46
|
+
## Advanced
|
|
47
|
+
|
|
48
|
+
- [Vendor code-splitting](https://barefootjs.dev/docs/advanced/code-splitting.md): Split large vendor libraries into separately-cached browser chunks via barefoot.config.ts externals
|
|
49
|
+
- [Compiler Internals](https://barefootjs.dev/docs/advanced/compiler-internals.md): How the BarefootJS compiler transforms JSX into marked templates and client JavaScript.
|
|
50
|
+
- [Error Codes Reference](https://barefootjs.dev/docs/advanced/error-codes.md): BF-prefixed compiler error codes with explanations and fixes.
|
|
51
|
+
- [IR Schema Reference](https://barefootjs.dev/docs/advanced/ir-schema.md): JSON tree structure of the Intermediate Representation consumed by adapters and client-JS generation.
|
|
52
|
+
- [Performance Optimization](https://barefootjs.dev/docs/advanced/performance.md): Strategies to minimize bundle size, reduce hydration cost, and optimize runtime reactivity
|
|
53
|
+
- [xyflow browser bundle](https://barefootjs.dev/docs/advanced/xyflow-browser-bundle.md): How to serve @barefootjs/xyflow as a pre-built browser chunk via an importmap
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Quick Start
|
|
3
|
+
description: Scaffold a BarefootJS app in under five minutes — Counter component, dev server, deploy.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Quick Start
|
|
7
|
+
|
|
8
|
+
Scaffold a BarefootJS app, run it locally, and tour the generated project. About five minutes.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
- **Node.js 22+** (or Bun).
|
|
13
|
+
- The default scaffold targets [Cloudflare Workers](https://developers.cloudflare.com/workers/) via `wrangler dev` — runs locally, no account needed.
|
|
14
|
+
|
|
15
|
+
## 1. Scaffold the project
|
|
16
|
+
|
|
17
|
+
<PackageManagerTabs command="barefootjs@latest" mode="create" />
|
|
18
|
+
|
|
19
|
+
Press Enter at the prompts to accept the defaults (Hono on Cloudflare Workers, UnoCSS).
|
|
20
|
+
|
|
21
|
+
## 2. Install and run
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd my-app
|
|
25
|
+
npm install
|
|
26
|
+
npm run dev
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`npm run dev` runs three processes in parallel:
|
|
30
|
+
|
|
31
|
+
- `bf build --watch` — the BarefootJS compiler. Watches `components/` and emits marked templates plus client JS to `public/components/`.
|
|
32
|
+
- `unocss --watch` — scans your JSX for utility classes and writes `public/uno.css`.
|
|
33
|
+
- `wrangler dev --live-reload` — Cloudflare's local Workers runtime. Serves the app and reloads the browser on rebuilds.
|
|
34
|
+
|
|
35
|
+
Open the URL Wrangler prints. You should see a counter card with **+1**, **-1**, and **Reset** buttons.
|
|
36
|
+
|
|
37
|
+
## 3. Look at what was generated
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
my-app/
|
|
41
|
+
├── server.tsx # Hono routes — entry point
|
|
42
|
+
├── renderer.tsx # HTML shell (<head>, <body>, BfScripts)
|
|
43
|
+
├── components/
|
|
44
|
+
│ └── Counter.tsx # The starter component
|
|
45
|
+
├── components/ui/
|
|
46
|
+
│ └── button/ # Pulled from the BarefootJS UI registry
|
|
47
|
+
├── public/ # Static assets served by Workers
|
|
48
|
+
│ ├── tokens.css # CSS design tokens
|
|
49
|
+
│ ├── styles.css # Counter + page styles
|
|
50
|
+
│ └── components/ # Generated client JS (bf build writes here)
|
|
51
|
+
├── barefoot.config.ts # Compiler + paths config
|
|
52
|
+
├── wrangler.jsonc # Cloudflare Workers config
|
|
53
|
+
└── uno.config.ts # UnoCSS scan patterns
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Open `components/Counter.tsx`:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
'use client'
|
|
60
|
+
|
|
61
|
+
import { createSignal, createMemo } from '@barefootjs/client'
|
|
62
|
+
import { Button } from '@/components/ui/button'
|
|
63
|
+
|
|
64
|
+
interface CounterProps {
|
|
65
|
+
initial?: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function Counter(props: CounterProps) {
|
|
69
|
+
const [count, setCount] = createSignal(props.initial ?? 0)
|
|
70
|
+
const doubled = createMemo(() => count() * 2)
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div className="counter">
|
|
74
|
+
<p className="counter-value">count: {count()}</p>
|
|
75
|
+
<p className="counter-doubled">doubled: {doubled()}</p>
|
|
76
|
+
<div className="counter-buttons">
|
|
77
|
+
<Button onClick={() => setCount(n => n + 1)}>+1</Button>
|
|
78
|
+
<Button onClick={() => setCount(n => n - 1)} variant="secondary">-1</Button>
|
|
79
|
+
<Button onClick={() => setCount(0)} variant="ghost">Reset</Button>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
See [Client Directive](./rendering/client-directive.md), [`createSignal`](./reactivity/create-signal.md), and [`createMemo`](./reactivity/create-memo.md) for what each piece does. Props are read via `props.initial`, not destructured — destructuring captures the value once and breaks reactivity, so the compiler emits warning [`BF043`](./advanced/error-codes.md) when it sees that form on a `"use client"` component. [Props Reactivity](./reactivity/props-reactivity.md) covers the full rule.
|
|
87
|
+
|
|
88
|
+
The Counter is mounted in `server.tsx`:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { Hono } from 'hono'
|
|
92
|
+
import { renderer } from './renderer'
|
|
93
|
+
import { Counter } from '@/components/Counter'
|
|
94
|
+
|
|
95
|
+
const app = new Hono()
|
|
96
|
+
|
|
97
|
+
app.use('*', renderer)
|
|
98
|
+
|
|
99
|
+
app.get('/', (c) =>
|
|
100
|
+
c.render(
|
|
101
|
+
<main>
|
|
102
|
+
<Counter />
|
|
103
|
+
</main>,
|
|
104
|
+
{ title: 'BarefootJS app' },
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
export default app
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Same `Counter` import, two outputs: server renders HTML, client hydrates the buttons.
|
|
112
|
+
|
|
113
|
+
## 4. Make a change
|
|
114
|
+
|
|
115
|
+
With `npm run dev` still running, pass an initial value from the server. In `server.tsx`:
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
app.get('/', (c) =>
|
|
119
|
+
c.render(
|
|
120
|
+
<main>
|
|
121
|
+
<Counter initial={5} />
|
|
122
|
+
</main>,
|
|
123
|
+
{ title: 'BarefootJS app' },
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Save. The browser reloads and the counter starts at **5**. The server rendered `5` into the HTML, and hydration picked it up on the client — the same JSX, evaluated in both places.
|
|
129
|
+
|
|
130
|
+
Now add a new button to `Counter.tsx`:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
<Button onClick={() => setCount(n => n * 2)} variant="ghost">×2</Button>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Save and watch the browser update. No virtual DOM, no diff — the compiler generated an effect that updates only the `<p>` text node when `count` changes.
|
|
137
|
+
|
|
138
|
+
## 5. Deploy (optional)
|
|
139
|
+
|
|
140
|
+
When you're ready to ship:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm run deploy
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This runs `bf build`, generates the final `uno.css`, and calls `wrangler deploy`. The first deploy will prompt you to log into Cloudflare. After that, your app is live on `*.workers.dev`.
|
|
147
|
+
|
|
148
|
+
## Next steps
|
|
149
|
+
|
|
150
|
+
- **[Core Concepts](./core-concepts.md)** — the four design principles behind BarefootJS: backend freedom, MPA-style rendering, fine-grained reactivity, and AI-native workflows.
|
|
151
|
+
- **[`createSignal`](./reactivity/create-signal.md)** and **[`createMemo`](./reactivity/create-memo.md)** — the reactivity primitives you just used.
|
|
152
|
+
- **[Client Directive](./rendering/client-directive.md)** — exactly what `"use client"` does and when to reach for it.
|
|
153
|
+
- **[Hono Adapter](./adapters/hono-adapter.md)** — adapter-specific configuration and output details.
|
|
154
|
+
- Pick a different backend by passing `--adapter` to the scaffolder. Supported values today: `hono` (default — Cloudflare Workers), `hono-node`, `echo` (Go / Echo), `mojo` (Mojolicious / Perl), `csr` (no backend — pure client render). For example:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm create barefootjs@latest -- --adapter hono-node
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
See [Adapter Architecture](./adapters/adapter-architecture.md) for the architectural overview, and the per-adapter pages under [`docs/core/adapters/`](./adapters/) for output details. The `@barefootjs/go-template` package is a compile-time adapter API for generating Go `html/template` files — it does not have a `bf init` scaffold; see [Go Template Adapter](./adapters/go-template-adapter.md) for the programmatic usage.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: createEffect
|
|
3
|
+
description: Runs a function and re-runs it whenever its tracked signal dependencies change.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# createEffect
|
|
7
|
+
|
|
8
|
+
Runs a function immediately and re-runs it whenever any signal read inside it changes.
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { createEffect } from '@barefootjs/client'
|
|
12
|
+
|
|
13
|
+
createEffect(fn: () => void | (() => void)): void
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Basic Usage
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
const [count, setCount] = createSignal(0)
|
|
21
|
+
|
|
22
|
+
createEffect(() => {
|
|
23
|
+
document.title = `Count: ${count()}`
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
setCount(1) // Effect re-runs, title becomes "Count: 1"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Dependencies are tracked automatically. No dependency array is needed.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Conditional Dependencies
|
|
33
|
+
|
|
34
|
+
Dependencies change per run. If a branch skips a signal read, that signal is not tracked for that run:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
const [showName, setShowName] = createSignal(true)
|
|
38
|
+
const [name, setName] = createSignal('Alice')
|
|
39
|
+
const [count, setCount] = createSignal(0)
|
|
40
|
+
|
|
41
|
+
createEffect(() => {
|
|
42
|
+
if (showName()) {
|
|
43
|
+
console.log(name()) // name is tracked
|
|
44
|
+
} else {
|
|
45
|
+
console.log(count()) // count is tracked instead
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## Cleanup
|
|
52
|
+
|
|
53
|
+
Two ways to register cleanup for resources that need teardown before re-run.
|
|
54
|
+
|
|
55
|
+
### Return a function
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
createEffect(() => {
|
|
59
|
+
const timer = setInterval(() => console.log('tick'), 1000)
|
|
60
|
+
return () => clearInterval(timer)
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### `onCleanup`
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
createEffect(() => {
|
|
68
|
+
const timer = setInterval(() => console.log('tick'), 1000)
|
|
69
|
+
onCleanup(() => clearInterval(timer))
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`onCleanup` can be called multiple times. Cleanups run in reverse order (last registered, first called). See [`onCleanup`](./on-cleanup.md) for details.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## Common Patterns
|
|
77
|
+
|
|
78
|
+
### localStorage sync
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
const [theme, setTheme] = createSignal('light')
|
|
82
|
+
|
|
83
|
+
createEffect(() => {
|
|
84
|
+
localStorage.setItem('theme', theme())
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Data fetching
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
const [query, setQuery] = createSignal('')
|
|
92
|
+
|
|
93
|
+
createEffect(() => {
|
|
94
|
+
const q = query()
|
|
95
|
+
if (!q) return
|
|
96
|
+
|
|
97
|
+
const controller = new AbortController()
|
|
98
|
+
fetch(`/api/search?q=${q}`, { signal: controller.signal })
|
|
99
|
+
.then(r => r.json())
|
|
100
|
+
.then(setResults)
|
|
101
|
+
|
|
102
|
+
onCleanup(() => controller.abort())
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
When `query` changes, the previous fetch is aborted before the new one starts.
|
|
107
|
+
|
|
108
|
+
### Reactive attributes
|
|
109
|
+
|
|
110
|
+
The compiler generates effects for reactive attributes.
|
|
111
|
+
|
|
112
|
+
Source:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
<button disabled={loading()}>Submit</button>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Generated client JS:
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
const [_s0] = $(__scope, 's0')
|
|
122
|
+
createEffect(() => {
|
|
123
|
+
if (_s0) { _s0.disabled = !!(loading()) }
|
|
124
|
+
})
|
|
125
|
+
```
|