cajoa 0.2.0 → 0.3.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 CHANGED
@@ -1,13 +1,14 @@
1
1
  # Cajoa
2
2
 
3
- A small, beginner-friendly frontend framework built on **signals**.
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.2 (early stage).** The API may still change. Not production-ready yet.
6
+ > ⚠️ **v0.3 (early stage).** The API may still change. Not production-ready yet.
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
+ - **Plain JSX.** Write `<button onClick={...}>`, no special syntax to learn.
11
12
  - **Tiny and readable.** The whole core is small enough to read in one sitting.
12
13
  - **TypeScript-first**, ESM + CommonJS, zero runtime dependencies.
13
14
 
@@ -17,52 +18,66 @@ No virtual DOM, no hooks rules, no re-rendering components.
17
18
  npm install cajoa
18
19
  ```
19
20
 
21
+ ## Setup (JSX)
22
+
23
+ In your `tsconfig.json`:
24
+
25
+ ```json
26
+ {
27
+ "compilerOptions": {
28
+ "jsx": "react-jsx",
29
+ "jsxImportSource": "cajoa"
30
+ }
31
+ }
32
+ ```
33
+
34
+ If you use Vite and your JSX is not transformed, also set it in `vite.config.ts`
35
+ (`oxc: { jsx: { runtime: "automatic", importSource: "cajoa" } }` on newer Vite,
36
+ or `esbuild: { jsx: "automatic", jsxImportSource: "cajoa" }` on older ones).
37
+
20
38
  ## Quick start
21
39
 
22
- ```ts
23
- import { createSignal, h, render } from "cajoa";
40
+ ```tsx
41
+ import { createSignal, render } from "cajoa";
24
42
 
25
43
  function Counter() {
26
44
  const [count, setCount] = createSignal(0);
27
45
 
28
- return h(
29
- "button",
30
- { onClick: () => setCount((c) => c + 1) },
31
- "Clicked ",
32
- count,
33
- " times",
46
+ return (
47
+ <button onClick={() => setCount((c) => c + 1)}>
48
+ Clicked {count} times
49
+ </button>
34
50
  );
35
51
  }
36
52
 
37
53
  render(Counter, document.getElementById("app")!);
38
54
  ```
39
55
 
40
- Passing `count` (the function itself, not `count()`) is what makes that part of the DOM reactive.
56
+ ## Two rules to remember
57
+
58
+ Cajoa has no compiler, so JSX expressions run exactly once. That gives two rules:
59
+
60
+ 1. **Pass the signal, don't call it.**
61
+ `{count}` is reactive. `{count()}` is read once and never updates.
62
+ For computed values, pass a function: `{() => count() * 2}`.
63
+ 2. **`Show` children are a function.**
64
+ `<Show when={on}>{() => <p>Hello</p>}</Show>`
65
+ Otherwise the element is created even when it is hidden.
41
66
 
42
67
  ## Lists and conditions
43
68
 
44
- ```ts
45
- import { createSignal, h, Show, For } from "cajoa";
69
+ ```tsx
70
+ import { createSignal, Show, For } from "cajoa";
46
71
 
47
72
  const [todos, setTodos] = createSignal([{ id: 1, text: "Learn Cajoa" }]);
48
73
 
49
- h(
50
- "div",
51
- null,
52
- Show({
53
- when: () => todos().length > 0,
54
- fallback: () => h("p", null, "Nothing to do."),
55
- children: () =>
56
- h(
57
- "ul",
58
- null,
59
- For({
60
- each: todos,
61
- children: (todo) => h("li", null, todo.text),
62
- }),
63
- ),
64
- }),
65
- );
74
+ <Show when={() => todos().length > 0} fallback={() => <p>Nothing to do.</p>}>
75
+ {() => (
76
+ <ul>
77
+ <For each={todos}>{(todo) => <li>{todo.text}</li>}</For>
78
+ </ul>
79
+ )}
80
+ </Show>;
66
81
  ```
67
82
 
68
83
  ## API
@@ -76,14 +91,19 @@ h(
76
91
  | `createMemo(fn)` | Cached computed value. Only notifies when the result changes. |
77
92
  | `batch(fn)` | Groups several `set` calls into one update. |
78
93
 
79
- ### DOM
94
+ ### Components
95
+
96
+ | Component | What it does |
97
+ | ---------------------- | ---------------------------------------------------------------------------------------------------- |
98
+ | `<Show when fallback>` | Conditional rendering. `children` and `fallback` are functions. |
99
+ | `<For each key?>` | List rendering. Reuses DOM nodes for unchanged items. The child is a function that returns a `Node`. |
100
+
101
+ ### Rendering
80
102
 
81
- | Function | What it does |
82
- | ------------------------------------ | -------------------------------------------------------------------------------------- |
83
- | `h(tag, props, ...children)` | Creates a real DOM element. Function props/children are reactive. |
84
- | `render(component, container)` | Mounts a component. Returns a `dispose` function. |
85
- | `Show({ when, children, fallback })` | Conditional rendering. |
86
- | `For({ each, children, key? })` | List rendering. Reuses DOM nodes for unchanged items. `children` must return a `Node`. |
103
+ | Function | What it does |
104
+ | ------------------------------ | ------------------------------------------------- |
105
+ | `render(component, container)` | Mounts a component. Returns a `dispose` function. |
106
+ | `h(tag, props, ...children)` | Low-level element creator. JSX compiles to this. |
87
107
 
88
108
  ### Lifecycle
89
109
 
@@ -109,7 +129,7 @@ h(
109
129
 
110
130
  ## Known limitations
111
131
 
112
- - No JSX support yet (`h()` only).
132
+ - No Fragments (`<>...</>`) yet. Wrap siblings in an element.
113
133
  - No SSR.
114
134
  - `createMemo` can show a brief inconsistent state when memos depend on each other.
115
135
  - `set` calls are not batched automatically inside event handlers. Wrap them in `batch()`.
@@ -117,13 +137,14 @@ h(
117
137
  ## Roadmap
118
138
 
119
139
  - [x] Signals and effects
120
- - [x] DOM bindings (`h`, `render`)
121
- - [x] `Show`, `For`
122
- - [x] Lifecycle (`onMount`, `onCleanup`) and disposal
140
+ - [x] DOM bindings and `render`
141
+ - [x] `Show`, `For` (keyed)
142
+ - [x] Lifecycle and disposal
123
143
  - [x] `createMemo`, `batch`
124
- - [x] Keyed `For`
125
144
  - [x] Context
126
- - [ ] JSX support
145
+ - [x] JSX support
146
+ - [ ] Fragments
147
+ - [ ] Vite plugin (removes the two rules above)
127
148
  - [ ] Router
128
149
  - [ ] SSR
129
150
 
package/dist/index.cjs CHANGED
@@ -29,6 +29,9 @@ __export(index_exports, {
29
29
  createRoot: () => createRoot,
30
30
  createSignal: () => createSignal,
31
31
  h: () => h,
32
+ jsx: () => jsx,
33
+ jsxDEV: () => jsxDEV,
34
+ jsxs: () => jsxs,
32
35
  onCleanup: () => onCleanup,
33
36
  onMount: () => onMount,
34
37
  provide: () => provide,
@@ -293,6 +296,16 @@ function createMemo(fn) {
293
296
  createEffect(() => setValue(() => fn()));
294
297
  return value;
295
298
  }
299
+
300
+ // src/jsx-runtime.ts
301
+ function jsx(type, props) {
302
+ if (typeof type === "function") return type(props ?? {});
303
+ const { children, ...rest } = props ?? {};
304
+ const kids = children === void 0 ? [] : [children];
305
+ return h(type, rest, ...kids);
306
+ }
307
+ var jsxs = jsx;
308
+ var jsxDEV = jsx;
296
309
  // Annotate the CommonJS export names for ESM import in node:
297
310
  0 && (module.exports = {
298
311
  For,
@@ -304,6 +317,9 @@ function createMemo(fn) {
304
317
  createRoot,
305
318
  createSignal,
306
319
  h,
320
+ jsx,
321
+ jsxDEV,
322
+ jsxs,
307
323
  onCleanup,
308
324
  onMount,
309
325
  provide,
package/dist/index.d.cts CHANGED
@@ -32,5 +32,18 @@ declare function For<T>(props: {
32
32
 
33
33
  declare function createMemo<T>(fn: () => T): () => T;
34
34
 
35
- export { For, Show, batch, createContext, createEffect, createMemo, createRoot, createSignal, h, onCleanup, onMount, provide, render, useContext };
35
+ declare function jsx(type: any, props: Record<string, any> | null): any;
36
+ declare const jsxs: typeof jsx;
37
+ declare const jsxDEV: typeof jsx;
38
+ declare namespace JSX {
39
+ type Element = any;
40
+ interface IntrinsicElements {
41
+ [tag: string]: any;
42
+ }
43
+ interface ElementChildrenAttribute {
44
+ children: {};
45
+ }
46
+ }
47
+
48
+ export { For, JSX, Show, batch, createContext, createEffect, createMemo, createRoot, createSignal, h, jsx, jsxDEV, jsxs, onCleanup, onMount, provide, render, useContext };
36
49
  export type { Child, Context };
package/dist/index.d.ts CHANGED
@@ -32,5 +32,18 @@ declare function For<T>(props: {
32
32
 
33
33
  declare function createMemo<T>(fn: () => T): () => T;
34
34
 
35
- export { For, Show, batch, createContext, createEffect, createMemo, createRoot, createSignal, h, onCleanup, onMount, provide, render, useContext };
35
+ declare function jsx(type: any, props: Record<string, any> | null): any;
36
+ declare const jsxs: typeof jsx;
37
+ declare const jsxDEV: typeof jsx;
38
+ declare namespace JSX {
39
+ type Element = any;
40
+ interface IntrinsicElements {
41
+ [tag: string]: any;
42
+ }
43
+ interface ElementChildrenAttribute {
44
+ children: {};
45
+ }
46
+ }
47
+
48
+ export { For, JSX, Show, batch, createContext, createEffect, createMemo, createRoot, createSignal, h, jsx, jsxDEV, jsxs, onCleanup, onMount, provide, render, useContext };
36
49
  export type { Child, Context };
package/dist/index.js CHANGED
@@ -254,6 +254,16 @@ function createMemo(fn) {
254
254
  createEffect(() => setValue(() => fn()));
255
255
  return value;
256
256
  }
257
+
258
+ // src/jsx-runtime.ts
259
+ function jsx(type, props) {
260
+ if (typeof type === "function") return type(props ?? {});
261
+ const { children, ...rest } = props ?? {};
262
+ const kids = children === void 0 ? [] : [children];
263
+ return h(type, rest, ...kids);
264
+ }
265
+ var jsxs = jsx;
266
+ var jsxDEV = jsx;
257
267
  export {
258
268
  For,
259
269
  Show,
@@ -264,6 +274,9 @@ export {
264
274
  createRoot,
265
275
  createSignal,
266
276
  h,
277
+ jsx,
278
+ jsxDEV,
279
+ jsxs,
267
280
  onCleanup,
268
281
  onMount,
269
282
  provide,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cajoa",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A beginner-friendly, signals-based frontend framework.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -16,6 +16,26 @@
16
16
  "types": "./dist/index.d.cts",
17
17
  "default": "./dist/index.cjs"
18
18
  }
19
+ },
20
+ "./jsx-runtime": {
21
+ "import": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ },
25
+ "require": {
26
+ "types": "./dist/index.d.cts",
27
+ "default": "./dist/index.cjs"
28
+ }
29
+ },
30
+ "./jsx-dev-runtime": {
31
+ "import": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ },
35
+ "require": {
36
+ "types": "./dist/index.d.cts",
37
+ "default": "./dist/index.cjs"
38
+ }
19
39
  }
20
40
  },
21
41
  "files": [
@@ -31,7 +51,7 @@
31
51
  },
32
52
  "repository": {
33
53
  "type": "git",
34
- "url": "git+https://github.com/cjmgrc/Cajoa.git"
54
+ "url": "git+https://github.com/CajoaTS/Cajoa.git"
35
55
  },
36
56
  "keywords": [
37
57
  "frontend",
@@ -44,10 +64,11 @@
44
64
  "author": "cjmgrc",
45
65
  "license": "Apache-2.0",
46
66
  "bugs": {
47
- "url": "https://github.com/cjmgrc/Cajoa/issues"
67
+ "url": "https://github.com/CajoaTS/Cajoa/issues"
48
68
  },
49
- "homepage": "https://github.com/cjmgrc/Cajoa#readme",
69
+ "homepage": "https://github.com/CajoaTS/Cajoa#readme",
50
70
  "devDependencies": {
71
+ "@types/node": "^26.6.5",
51
72
  "jsdom": "^29.1.1",
52
73
  "tsup": "^9.0.0",
53
74
  "typescript": "^5.9.3",