cajoa 0.7.0 → 0.9.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 +99 -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
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or add Cajoa to an existing project:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install cajoa
|
|
19
31
|
```
|
|
20
32
|
|
|
21
|
-
|
|
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,54 @@ 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
|
+
## Refs and SVG
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
function SearchBox() {
|
|
126
|
+
let input: HTMLInputElement | undefined;
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div>
|
|
130
|
+
<input ref={(el: HTMLInputElement) => (input = el)} />
|
|
131
|
+
<button onClick={() => input?.focus()}>Focus</button>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
````
|
|
137
|
+
|
|
138
|
+
- `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`.
|
|
139
|
+
- `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.
|
|
140
|
+
- Events are native DOM events. `onChange` fires when a field loses focus, so use `onInput` for every keystroke. `onDoubleClick` listens to `dblclick`.
|
|
141
|
+
- `className` and `htmlFor` work. Inline string handlers like `onclick="..."` are ignored.
|
|
142
|
+
- `ref` is a function that receives the element once it is created. Its props and children are ready, but it is not in the page yet, so use `onMount` if you need to measure it right away. Signals read inside `ref` are not tracked.
|
|
143
|
+
- SVG works: `<svg>`, `<path>`, `<circle>`, gradients, filters and the other SVG tags are created in the SVG namespace. Write attributes as `stroke-width="2"` or `strokeWidth="2"`.
|
|
144
|
+
|
|
85
145
|
```tsx
|
|
86
146
|
import { render, Router, Route, Link, useParams } from "cajoa";
|
|
87
147
|
|
|
@@ -106,6 +166,15 @@ render(
|
|
|
106
166
|
|
|
107
167
|
Routes are matched in order, so put `path="*"` last.
|
|
108
168
|
|
|
169
|
+
## Packages
|
|
170
|
+
|
|
171
|
+
| Package | What it is |
|
|
172
|
+
| -------------- | ------------------------------------------------------------------------------------------ |
|
|
173
|
+
| `cajoa` | The package most apps install (core + DOM) |
|
|
174
|
+
| `@cajoa/core` | Renderer-agnostic core: signals, JSX runtime, `Show`, `For`, context, `Renderer` interface |
|
|
175
|
+
| `@cajoa/dom` | DOM renderer and router |
|
|
176
|
+
| `create-cajoa` | Project starter (`npm create cajoa@latest`) |
|
|
177
|
+
|
|
109
178
|
## API
|
|
110
179
|
|
|
111
180
|
### Reactivity
|
|
@@ -116,13 +185,15 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
116
185
|
| `createEffect(fn)` | Runs `fn` now and again whenever a signal it read changes. |
|
|
117
186
|
| `createMemo(fn)` | Cached computed value. Only notifies when the result changes. |
|
|
118
187
|
| `batch(fn)` | Groups several `set` calls into one update. |
|
|
188
|
+
| `untrack(fn)` | Runs `fn` without subscribing to signals. |
|
|
119
189
|
|
|
120
190
|
### Components
|
|
121
191
|
|
|
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
|
|
192
|
+
| Component | What it does |
|
|
193
|
+
| ---------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
194
|
+
| `<Show when fallback>` | Conditional rendering. `children` and `fallback` are functions. |
|
|
195
|
+
| `<For each key?>` | List rendering. Reuses DOM nodes for unchanged items. The child is a function that returns a single node. |
|
|
196
|
+
| `<>...</>` (Fragment) | Groups siblings without a wrapper element. |
|
|
126
197
|
|
|
127
198
|
### Rendering
|
|
128
199
|
|
|
@@ -157,7 +228,6 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
157
228
|
| `navigate(to, { replace? })` | Navigates programmatically. |
|
|
158
229
|
| `useParams()` | Reads the route params (`{ id: "7" }`). |
|
|
159
230
|
| `usePath()` | Returns a signal getter for the current pathname. |
|
|
160
|
-
| `untrack(fn)` | Runs `fn` without subscribing to signals. |
|
|
161
231
|
|
|
162
232
|
### Renderer
|
|
163
233
|
|
|
@@ -167,20 +237,12 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
167
237
|
| `getRenderer()` | Returns the current renderer. |
|
|
168
238
|
| `domRenderer` | The built-in DOM renderer. |
|
|
169
239
|
|
|
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
240
|
## Good to know
|
|
179
241
|
|
|
180
242
|
- `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
|
-
-
|
|
243
|
+
- Inside `<For>`, return a single element (not a fragment), because `For` reuses that node.
|
|
244
|
+
- `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
245
|
- 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
246
|
|
|
185
247
|
## Known limitations
|
|
186
248
|
|
|
@@ -188,26 +250,28 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
188
250
|
- Router: pathname only (no query/hash handling), no nested routes, no `base` path.
|
|
189
251
|
- `<Show>` and `<Router>` return a function, so they must sit inside an element, not be the root returned by `render`.
|
|
190
252
|
- Single-page apps need a server fallback to `index.html` for unknown paths.
|
|
253
|
+
- `<select value>` works only when the `<option>` elements are direct children of the `<select>`.
|
|
191
254
|
- `createMemo` can show a brief inconsistent state when memos depend on each other.
|
|
192
255
|
- `set` calls are not batched automatically inside event handlers. Wrap them in `batch()`.
|
|
193
|
-
-
|
|
256
|
+
- Types in the core use a loose `HostNode` type for now.
|
|
257
|
+
- Inside `<svg>`, the tags `<a>`, `<title>`, `<style>` and `<script>` are created as HTML elements (they share their names with HTML tags).
|
|
194
258
|
|
|
195
259
|
## Roadmap
|
|
196
260
|
|
|
197
|
-
- [x] Signals
|
|
198
|
-
- [x] DOM bindings
|
|
199
|
-
- [x]
|
|
200
|
-
- [x] Lifecycle and disposal
|
|
201
|
-
- [x] `createMemo`, `batch`
|
|
202
|
-
- [x] Context
|
|
261
|
+
- [x] Signals, effects, memo, batch
|
|
262
|
+
- [x] DOM bindings, `Show`, keyed `For`, Fragments
|
|
263
|
+
- [x] Lifecycle, context
|
|
203
264
|
- [x] JSX support
|
|
204
|
-
- [x] Fragments
|
|
205
265
|
- [x] Router
|
|
206
|
-
- [x] Renderer abstraction
|
|
207
|
-
- [
|
|
266
|
+
- [x] Renderer abstraction, split into packages
|
|
267
|
+
- [x] Automated releases
|
|
268
|
+
- [x] Form, style and event handling in the DOM renderer
|
|
269
|
+
- [ ] SVG and `ref` support
|
|
208
270
|
- [ ] Vite plugin (removes the two rules above)
|
|
209
|
-
- [ ]
|
|
271
|
+
- [ ] Better form and style handling in the DOM renderer
|
|
272
|
+
- [ ] Native renderers (mobile, desktop)
|
|
210
273
|
- [ ] SSR
|
|
274
|
+
- [ ] Docs site
|
|
211
275
|
|
|
212
276
|
## Development
|
|
213
277
|
|
|
@@ -215,9 +279,11 @@ Routes are matched in order, so put `path="*"` last.
|
|
|
215
279
|
npm install
|
|
216
280
|
npm test # run tests
|
|
217
281
|
npm run dev # open the playground
|
|
218
|
-
npm run
|
|
282
|
+
npm run verify # typecheck, tests, build, sync and package checks
|
|
219
283
|
```
|
|
220
284
|
|
|
285
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for the release flow.
|
|
286
|
+
|
|
221
287
|
## License
|
|
222
288
|
|
|
223
289
|
[Apache-2.0](./LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cajoa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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.9.0",
|
|
60
|
+
"@cajoa/dom": "^0.9.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|