cajoa 0.7.0 → 0.8.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/README.md +83 -33
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,22 +3,34 @@
|
|
|
3
3
|
A small, beginner-friendly frontend framework built on **signals** and **JSX**.
|
|
4
4
|
No virtual DOM, no hooks rules, no re-rendering components.
|
|
5
5
|
|
|
6
|
-
> ⚠️ **
|
|
6
|
+
> ⚠️ **Early stage.** The API may still change before 1.0.
|
|
7
7
|
|
|
8
8
|
## Why Cajoa?
|
|
9
9
|
|
|
10
10
|
- **Components run once.** A component is just a function that builds DOM. Only the parts that depend on a signal update.
|
|
11
11
|
- **Plain JSX.** Write `<button onClick={...}>`, no special syntax to learn.
|
|
12
|
-
- **Tiny and readable.** The
|
|
12
|
+
- **Tiny and readable.** The core is small enough to read in one sitting.
|
|
13
|
+
- **Renderer-agnostic core.** `@cajoa/core` has no DOM dependency, so other targets (native, SSR) can plug in their own renderer.
|
|
13
14
|
- **TypeScript-first**, ESM + CommonJS, zero runtime dependencies.
|
|
14
15
|
|
|
15
|
-
##
|
|
16
|
+
## Get started
|
|
17
|
+
|
|
18
|
+
The fastest way is the starter:
|
|
16
19
|
|
|
17
20
|
```bash
|
|
18
21
|
npm create cajoa@latest my-app
|
|
22
|
+
cd my-app
|
|
23
|
+
npm install
|
|
24
|
+
npm run dev
|
|
19
25
|
```
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
Or add Cajoa to an existing project:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install cajoa
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Manual JSX setup
|
|
22
34
|
|
|
23
35
|
In your `tsconfig.json`:
|
|
24
36
|
|
|
@@ -31,7 +43,7 @@ In your `tsconfig.json`:
|
|
|
31
43
|
}
|
|
32
44
|
```
|
|
33
45
|
|
|
34
|
-
|
|
46
|
+
With Vite, also set it in `vite.config.ts`
|
|
35
47
|
(`oxc: { jsx: { runtime: "automatic", importSource: "cajoa" } }` on newer Vite,
|
|
36
48
|
or `esbuild: { jsx: "automatic", jsxImportSource: "cajoa" }` on older ones).
|
|
37
49
|
|
|
@@ -55,7 +67,7 @@ render(Counter, document.getElementById("app")!);
|
|
|
55
67
|
|
|
56
68
|
## Two rules to remember
|
|
57
69
|
|
|
58
|
-
Cajoa has no compiler, so JSX expressions run exactly once. That gives two rules:
|
|
70
|
+
Cajoa has no compiler yet, so JSX expressions run exactly once. That gives two rules:
|
|
59
71
|
|
|
60
72
|
1. **Pass the signal, don't call it.**
|
|
61
73
|
`{count}` is reactive. `{count()}` is read once and never updates.
|
|
@@ -82,6 +94,37 @@ const [todos, setTodos] = createSignal([{ id: 1, text: "Learn Cajoa" }]);
|
|
|
82
94
|
|
|
83
95
|
## Routing
|
|
84
96
|
|
|
97
|
+
## Forms, styles and events
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
const [name, setName] = createSignal("");
|
|
101
|
+
const [agree, setAgree] = createSignal(false);
|
|
102
|
+
|
|
103
|
+
<form>
|
|
104
|
+
<input
|
|
105
|
+
placeholder="Name"
|
|
106
|
+
value={name}
|
|
107
|
+
onInput={(e) => setName(e.target.value)}
|
|
108
|
+
/>
|
|
109
|
+
<label>
|
|
110
|
+
<input
|
|
111
|
+
type="checkbox"
|
|
112
|
+
checked={agree}
|
|
113
|
+
onChange={(e) => setAgree(e.target.checked)}
|
|
114
|
+
/>
|
|
115
|
+
I agree
|
|
116
|
+
</label>
|
|
117
|
+
<p style={() => ({ color: agree() ? "green" : "gray", fontSize: "18px" })}>
|
|
118
|
+
Hello, {() => name() || "stranger"}!
|
|
119
|
+
</p>
|
|
120
|
+
</form>;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- `value`, `checked` and `selected` are set as DOM properties, so they stay in sync even after the user typed. Write `value` after `type`, `min` and `max`.
|
|
124
|
+
- `style` takes a string or an object (camelCase keys, kebab-case keys, or custom properties like `--accent`). Numbers are not converted to `px`, so write `"12px"`. For a reactive style, pass a function: `style={() => ({ color: color() })}`. An object with signals inside, like `style={{ color: color() }}`, is read once.
|
|
125
|
+
- Events are native DOM events. `onChange` fires when a field loses focus, so use `onInput` for every keystroke. `onDoubleClick` listens to `dblclick`.
|
|
126
|
+
- `className` and `htmlFor` work. Inline string handlers like `onclick="..."` are ignored.
|
|
127
|
+
|
|
85
128
|
```tsx
|
|
86
129
|
import { render, Router, Route, Link, useParams } from "cajoa";
|
|
87
130
|
|
|
@@ -106,6 +149,15 @@ render(
|
|
|
106
149
|
|
|
107
150
|
Routes are matched in order, so put `path="*"` last.
|
|
108
151
|
|
|
152
|
+
## Packages
|
|
153
|
+
|
|
154
|
+
| Package | What it is |
|
|
155
|
+
| -------------- | ------------------------------------------------------------------------------------------ |
|
|
156
|
+
| `cajoa` | The package most apps install (core + DOM) |
|
|
157
|
+
| `@cajoa/core` | Renderer-agnostic core: signals, JSX runtime, `Show`, `For`, context, `Renderer` interface |
|
|
158
|
+
| `@cajoa/dom` | DOM renderer and router |
|
|
159
|
+
| `create-cajoa` | Project starter (`npm create cajoa@latest`) |
|
|
160
|
+
|
|
109
161
|
## API
|
|
110
162
|
|
|
111
163
|
### Reactivity
|
|
@@ -116,13 +168,15 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
116
168
|
| `createEffect(fn)` | Runs `fn` now and again whenever a signal it read changes. |
|
|
117
169
|
| `createMemo(fn)` | Cached computed value. Only notifies when the result changes. |
|
|
118
170
|
| `batch(fn)` | Groups several `set` calls into one update. |
|
|
171
|
+
| `untrack(fn)` | Runs `fn` without subscribing to signals. |
|
|
119
172
|
|
|
120
173
|
### Components
|
|
121
174
|
|
|
122
|
-
| Component | What it does
|
|
123
|
-
| ---------------------- |
|
|
124
|
-
| `<Show when fallback>` | Conditional rendering. `children` and `fallback` are functions.
|
|
125
|
-
| `<For each key?>` | List rendering. Reuses DOM nodes for unchanged items. The child is a function that returns a
|
|
175
|
+
| Component | What it does |
|
|
176
|
+
| ---------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
177
|
+
| `<Show when fallback>` | Conditional rendering. `children` and `fallback` are functions. |
|
|
178
|
+
| `<For each key?>` | List rendering. Reuses DOM nodes for unchanged items. The child is a function that returns a single node. |
|
|
179
|
+
| `<>...</>` (Fragment) | Groups siblings without a wrapper element. |
|
|
126
180
|
|
|
127
181
|
### Rendering
|
|
128
182
|
|
|
@@ -157,7 +211,6 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
157
211
|
| `navigate(to, { replace? })` | Navigates programmatically. |
|
|
158
212
|
| `useParams()` | Reads the route params (`{ id: "7" }`). |
|
|
159
213
|
| `usePath()` | Returns a signal getter for the current pathname. |
|
|
160
|
-
| `untrack(fn)` | Runs `fn` without subscribing to signals. |
|
|
161
214
|
|
|
162
215
|
### Renderer
|
|
163
216
|
|
|
@@ -167,20 +220,12 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
167
220
|
| `getRenderer()` | Returns the current renderer. |
|
|
168
221
|
| `domRenderer` | The built-in DOM renderer. |
|
|
169
222
|
|
|
170
|
-
## Packages
|
|
171
|
-
|
|
172
|
-
| Package | What it is |
|
|
173
|
-
| ------------- | -------------------------------------------------------------------- |
|
|
174
|
-
| `cajoa` | The package most apps install (core + DOM) |
|
|
175
|
-
| `@cajoa/core` | Renderer-agnostic core: signals, JSX runtime, `Show`, `For`, context |
|
|
176
|
-
| `@cajoa/dom` | DOM renderer and router |
|
|
177
|
-
|
|
178
223
|
## Good to know
|
|
179
224
|
|
|
180
225
|
- `For` uses the item itself as the key by default. If you pass `key`, items with the same key are **not** re-rendered when their data changes, so keys must be unique and the item should hold its own signals if it needs to update.
|
|
181
|
-
-
|
|
226
|
+
- Inside `<For>`, return a single element (not a fragment), because `For` reuses that node.
|
|
227
|
+
- `useContext` and `useParams` only work while a component is being created (or inside `onMount` / effects). Read them at the top of your component and use the value later in handlers.
|
|
182
228
|
- Updating an object in place will not trigger effects. Set a new object instead.
|
|
183
|
-
- Fragments (`<>...</>`) work anywhere, but inside `<For>` the child must be a single element, because `For` reuses that node.
|
|
184
229
|
|
|
185
230
|
## Known limitations
|
|
186
231
|
|
|
@@ -188,26 +233,29 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
188
233
|
- Router: pathname only (no query/hash handling), no nested routes, no `base` path.
|
|
189
234
|
- `<Show>` and `<Router>` return a function, so they must sit inside an element, not be the root returned by `render`.
|
|
190
235
|
- Single-page apps need a server fallback to `index.html` for unknown paths.
|
|
236
|
+
- SVG is not supported yet: `<svg>` is created as an HTML element, so it does not render.
|
|
237
|
+
- No `ref` prop yet.
|
|
238
|
+
- `<select value>` works only when the `<option>` elements are direct children of the `<select>`.
|
|
191
239
|
- `createMemo` can show a brief inconsistent state when memos depend on each other.
|
|
192
240
|
- `set` calls are not batched automatically inside event handlers. Wrap them in `batch()`.
|
|
193
|
-
-
|
|
241
|
+
- Types in the core use a loose `HostNode` type for now.
|
|
194
242
|
|
|
195
243
|
## Roadmap
|
|
196
244
|
|
|
197
|
-
- [x] Signals
|
|
198
|
-
- [x] DOM bindings
|
|
199
|
-
- [x]
|
|
200
|
-
- [x] Lifecycle and disposal
|
|
201
|
-
- [x] `createMemo`, `batch`
|
|
202
|
-
- [x] Context
|
|
245
|
+
- [x] Signals, effects, memo, batch
|
|
246
|
+
- [x] DOM bindings, `Show`, keyed `For`, Fragments
|
|
247
|
+
- [x] Lifecycle, context
|
|
203
248
|
- [x] JSX support
|
|
204
|
-
- [x] Fragments
|
|
205
249
|
- [x] Router
|
|
206
|
-
- [x] Renderer abstraction
|
|
207
|
-
- [
|
|
250
|
+
- [x] Renderer abstraction, split into packages
|
|
251
|
+
- [x] Automated releases
|
|
252
|
+
- [x] Form, style and event handling in the DOM renderer
|
|
253
|
+
- [ ] SVG and `ref` support
|
|
208
254
|
- [ ] Vite plugin (removes the two rules above)
|
|
209
|
-
- [ ]
|
|
255
|
+
- [ ] Better form and style handling in the DOM renderer
|
|
256
|
+
- [ ] Native renderers (mobile, desktop)
|
|
210
257
|
- [ ] SSR
|
|
258
|
+
- [ ] Docs site
|
|
211
259
|
|
|
212
260
|
## Development
|
|
213
261
|
|
|
@@ -215,9 +263,11 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
215
263
|
npm install
|
|
216
264
|
npm test # run tests
|
|
217
265
|
npm run dev # open the playground
|
|
218
|
-
npm run
|
|
266
|
+
npm run verify # typecheck, tests, build, sync and package checks
|
|
219
267
|
```
|
|
220
268
|
|
|
269
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for the release flow.
|
|
270
|
+
|
|
221
271
|
## License
|
|
222
272
|
|
|
223
273
|
[Apache-2.0](./LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cajoa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A small, beginner-friendly frontend framework built on signals and JSX.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
],
|
|
57
57
|
"sideEffects": true,
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@cajoa/core": "^0.
|
|
60
|
-
"@cajoa/dom": "^0.
|
|
59
|
+
"@cajoa/core": "^0.8.0",
|
|
60
|
+
"@cajoa/dom": "^0.8.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|