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.
Files changed (2) hide show
  1. package/README.md +99 -33
  2. 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
- > ⚠️ **v0.6 (early stage).** The API may still change. Not production-ready yet.
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 whole core is small enough to read in one sitting.
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
- ## Install
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
- ## Setup (JSX)
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
- If you use Vite and your JSX is not transformed, also set it in `vite.config.ts`
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 `Node`. |
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
- - `useContext` only works while a component is being created (or inside `onMount` / effects). Read it at the top of your component and use the value later in handlers.
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
- - The Router and event names (`onClick`) are still web-specific for now.
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 and effects
198
- - [x] DOM bindings and `render`
199
- - [x] `Show`, `For` (keyed)
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 (core no longer depends on DOM)
207
- - [ ] Split into packages ('@cajoa/core', '@cajoa/dom')
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
- - [ ] Native renderer (mobile, desktop)
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 build # build to dist/
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.7.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.7.0",
60
- "@cajoa/dom": "^0.7.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",