@faber1999/axon.js 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/LICENSE +21 -0
- package/README.md +292 -0
- package/dist/chunk-HYNBHCGU.js +227 -0
- package/dist/index.d.ts +463 -0
- package/dist/index.js +515 -0
- package/dist/jsx-KOQXGMp1.d.ts +64 -0
- package/dist/jsx.d.ts +1 -0
- package/dist/jsx.js +8 -0
- package/package.json +29 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Fragment,
|
|
3
|
+
disposeOwner,
|
|
4
|
+
effect,
|
|
5
|
+
getCurrentEffect,
|
|
6
|
+
h,
|
|
7
|
+
onCleanup,
|
|
8
|
+
onMount,
|
|
9
|
+
runOwned,
|
|
10
|
+
runWithOwner,
|
|
11
|
+
untrack
|
|
12
|
+
} from "./chunk-HYNBHCGU.js";
|
|
13
|
+
|
|
14
|
+
// src/reactivity/signal.ts
|
|
15
|
+
var batchDepth = 0;
|
|
16
|
+
var pendingEffects = /* @__PURE__ */ new Set();
|
|
17
|
+
function flushEffects() {
|
|
18
|
+
pendingEffects.forEach((run) => {
|
|
19
|
+
if (!run._disposed) run();
|
|
20
|
+
});
|
|
21
|
+
pendingEffects.clear();
|
|
22
|
+
}
|
|
23
|
+
function scheduleEffect(run) {
|
|
24
|
+
if (batchDepth > 0) {
|
|
25
|
+
pendingEffects.add(run);
|
|
26
|
+
} else {
|
|
27
|
+
run();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function signal(initialValue) {
|
|
31
|
+
let value = initialValue;
|
|
32
|
+
const subscribers = /* @__PURE__ */ new Map();
|
|
33
|
+
const read = () => {
|
|
34
|
+
const currentEffect = getCurrentEffect();
|
|
35
|
+
if (currentEffect && !subscribers.has(currentEffect)) {
|
|
36
|
+
subscribers.set(currentEffect, () => {
|
|
37
|
+
subscribers.delete(currentEffect);
|
|
38
|
+
});
|
|
39
|
+
currentEffect._subscriptions.add(() => {
|
|
40
|
+
subscribers.delete(currentEffect);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
};
|
|
45
|
+
const write = (newValue) => {
|
|
46
|
+
const next = typeof newValue === "function" ? newValue(value) : newValue;
|
|
47
|
+
if (Object.is(next, value)) return;
|
|
48
|
+
value = next;
|
|
49
|
+
const subs = [...subscribers.keys()];
|
|
50
|
+
subs.forEach((run) => {
|
|
51
|
+
if (!run._disposed) scheduleEffect(run);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
return [read, write];
|
|
55
|
+
}
|
|
56
|
+
function batch(fn) {
|
|
57
|
+
batchDepth++;
|
|
58
|
+
try {
|
|
59
|
+
fn();
|
|
60
|
+
} finally {
|
|
61
|
+
batchDepth--;
|
|
62
|
+
if (batchDepth === 0) flushEffects();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/reactivity/computed.ts
|
|
67
|
+
function computed(fn) {
|
|
68
|
+
const [get, set] = signal(void 0);
|
|
69
|
+
effect(() => set(fn()));
|
|
70
|
+
return get;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/dom/render.ts
|
|
74
|
+
function mount(component, container, props = {}) {
|
|
75
|
+
container.innerHTML = "";
|
|
76
|
+
let nodes;
|
|
77
|
+
if (typeof component === "function") {
|
|
78
|
+
nodes = runWithOwner(component, props);
|
|
79
|
+
} else {
|
|
80
|
+
nodes = component;
|
|
81
|
+
}
|
|
82
|
+
const append = (node) => {
|
|
83
|
+
if (node == null) return;
|
|
84
|
+
if (Array.isArray(node)) node.forEach(append);
|
|
85
|
+
else container.appendChild(node);
|
|
86
|
+
};
|
|
87
|
+
append(nodes);
|
|
88
|
+
}
|
|
89
|
+
function createApp(RootComponent) {
|
|
90
|
+
return {
|
|
91
|
+
mount(selector) {
|
|
92
|
+
const container = typeof selector === "string" ? document.querySelector(selector) : selector;
|
|
93
|
+
if (!container) throw new Error(`[axon] mount target not found: ${selector}`);
|
|
94
|
+
mount(RootComponent, container);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/dom/helpers.ts
|
|
100
|
+
function Show({ when, fallback = null, children }) {
|
|
101
|
+
const start = document.createComment("Show");
|
|
102
|
+
const end = document.createComment("/Show");
|
|
103
|
+
const fragment = document.createDocumentFragment();
|
|
104
|
+
fragment.appendChild(start);
|
|
105
|
+
fragment.appendChild(end);
|
|
106
|
+
let currentOwner = null;
|
|
107
|
+
const condition = typeof when === "function" ? when : () => when;
|
|
108
|
+
const insert = (parent, content) => {
|
|
109
|
+
if (content == null) return;
|
|
110
|
+
if (Array.isArray(content)) {
|
|
111
|
+
content.forEach((c) => insert(parent, c));
|
|
112
|
+
} else {
|
|
113
|
+
parent.insertBefore(
|
|
114
|
+
content instanceof Node ? content : document.createTextNode(String(content)),
|
|
115
|
+
end
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
effect(() => {
|
|
120
|
+
const isTrue = Boolean(condition());
|
|
121
|
+
if (currentOwner) {
|
|
122
|
+
disposeOwner(currentOwner);
|
|
123
|
+
currentOwner = null;
|
|
124
|
+
}
|
|
125
|
+
let node = start.nextSibling;
|
|
126
|
+
while (node && node !== end) {
|
|
127
|
+
const next = node.nextSibling;
|
|
128
|
+
node.parentNode?.removeChild(node);
|
|
129
|
+
node = next;
|
|
130
|
+
}
|
|
131
|
+
const content = isTrue ? children : fallback;
|
|
132
|
+
if (content == null) return;
|
|
133
|
+
const parent = end.parentNode;
|
|
134
|
+
if (!parent) return;
|
|
135
|
+
if (typeof content === "function") {
|
|
136
|
+
currentOwner = { _onMount: [], _onCleanup: [], _children: [], _mounted: false };
|
|
137
|
+
insert(parent, content());
|
|
138
|
+
} else {
|
|
139
|
+
insert(parent, content);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return fragment;
|
|
143
|
+
}
|
|
144
|
+
function For({ each, children: renderItem }) {
|
|
145
|
+
const start = document.createComment("For");
|
|
146
|
+
const end = document.createComment("/For");
|
|
147
|
+
const fragment = document.createDocumentFragment();
|
|
148
|
+
fragment.appendChild(start);
|
|
149
|
+
fragment.appendChild(end);
|
|
150
|
+
const getList = typeof each === "function" ? each : () => each;
|
|
151
|
+
let renderedNodes = [];
|
|
152
|
+
effect(() => {
|
|
153
|
+
const list = getList() ?? [];
|
|
154
|
+
const parent = end.parentNode;
|
|
155
|
+
if (!parent) return;
|
|
156
|
+
renderedNodes.forEach((nodes) => {
|
|
157
|
+
nodes.forEach((n) => parent.removeChild(n));
|
|
158
|
+
});
|
|
159
|
+
renderedNodes = [];
|
|
160
|
+
list.forEach((item, index) => {
|
|
161
|
+
const result = renderItem(item, () => index);
|
|
162
|
+
const nodes = Array.isArray(result) ? result : [result];
|
|
163
|
+
nodes.forEach((n) => {
|
|
164
|
+
if (n != null) parent.insertBefore(n, end);
|
|
165
|
+
});
|
|
166
|
+
renderedNodes.push(nodes);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
return fragment;
|
|
170
|
+
}
|
|
171
|
+
function Dynamic({ component: getComponent, ...props }) {
|
|
172
|
+
const start = document.createComment("Dynamic");
|
|
173
|
+
const end = document.createComment("/Dynamic");
|
|
174
|
+
const fragment = document.createDocumentFragment();
|
|
175
|
+
fragment.appendChild(start);
|
|
176
|
+
fragment.appendChild(end);
|
|
177
|
+
const getter = typeof getComponent === "function" && getComponent.length === 0 ? getComponent : () => getComponent;
|
|
178
|
+
effect(() => {
|
|
179
|
+
const Component = getter();
|
|
180
|
+
const parent = end.parentNode;
|
|
181
|
+
if (!parent || !Component) return;
|
|
182
|
+
let node = start.nextSibling;
|
|
183
|
+
while (node && node !== end) {
|
|
184
|
+
const next = node.nextSibling;
|
|
185
|
+
parent.removeChild(node);
|
|
186
|
+
node = next;
|
|
187
|
+
}
|
|
188
|
+
const result = runWithOwner(Component, props);
|
|
189
|
+
const nodes = Array.isArray(result) ? result : [result];
|
|
190
|
+
nodes.forEach((n) => {
|
|
191
|
+
if (n != null) parent.insertBefore(n, end);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
return fragment;
|
|
195
|
+
}
|
|
196
|
+
function Portal({ mount: target, children }) {
|
|
197
|
+
const nodes = Array.isArray(children) ? children : [children];
|
|
198
|
+
nodes.forEach((n) => {
|
|
199
|
+
if (n != null) target.appendChild(n);
|
|
200
|
+
});
|
|
201
|
+
return document.createComment("Portal");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/dom/transitions.ts
|
|
205
|
+
function withViewTransition(fn) {
|
|
206
|
+
if ("startViewTransition" in document) {
|
|
207
|
+
document.startViewTransition(fn);
|
|
208
|
+
} else {
|
|
209
|
+
fn();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/component/context.ts
|
|
214
|
+
var contextMap = /* @__PURE__ */ new Map();
|
|
215
|
+
function createContext(defaultValue) {
|
|
216
|
+
const key = /* @__PURE__ */ Symbol("axon.context");
|
|
217
|
+
const Provider = ({
|
|
218
|
+
value,
|
|
219
|
+
children
|
|
220
|
+
}) => {
|
|
221
|
+
if (!contextMap.has(key)) contextMap.set(key, []);
|
|
222
|
+
contextMap.get(key).push(value);
|
|
223
|
+
queueMicrotask(() => {
|
|
224
|
+
const stack = contextMap.get(key);
|
|
225
|
+
if (stack) stack.pop();
|
|
226
|
+
});
|
|
227
|
+
if (children == null) return null;
|
|
228
|
+
return Array.isArray(children) ? children : [children];
|
|
229
|
+
};
|
|
230
|
+
const use = () => {
|
|
231
|
+
const stack = contextMap.get(key);
|
|
232
|
+
if (stack && stack.length > 0) return stack[stack.length - 1];
|
|
233
|
+
return defaultValue;
|
|
234
|
+
};
|
|
235
|
+
return { Provider, use };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/router/router.ts
|
|
239
|
+
function compilePath(pattern) {
|
|
240
|
+
const paramNames = [];
|
|
241
|
+
const regexStr = pattern.replace(/:([^/]+)/g, (_, name) => {
|
|
242
|
+
paramNames.push(name);
|
|
243
|
+
return "([^/]+)";
|
|
244
|
+
}).replace(/\*/g, ".*");
|
|
245
|
+
return {
|
|
246
|
+
regex: new RegExp(`^${regexStr}(?:/)?$`),
|
|
247
|
+
paramNames
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function matchPath(pattern, pathname) {
|
|
251
|
+
const { regex, paramNames } = compilePath(pattern);
|
|
252
|
+
const match = pathname.match(regex);
|
|
253
|
+
if (!match) return null;
|
|
254
|
+
const params = {};
|
|
255
|
+
paramNames.forEach((name, i) => {
|
|
256
|
+
params[name] = decodeURIComponent(match[i + 1]);
|
|
257
|
+
});
|
|
258
|
+
return params;
|
|
259
|
+
}
|
|
260
|
+
var _router = null;
|
|
261
|
+
function createRouter(routes, options = {}) {
|
|
262
|
+
if (options.viewTransitions) {
|
|
263
|
+
const style = document.createElement("style");
|
|
264
|
+
style.dataset.axon = "view-transitions";
|
|
265
|
+
style.textContent = [
|
|
266
|
+
"::view-transition-old(root),",
|
|
267
|
+
"::view-transition-new(root) { animation: none; }"
|
|
268
|
+
].join("\n");
|
|
269
|
+
document.head.appendChild(style);
|
|
270
|
+
}
|
|
271
|
+
const [pathname, setPathname] = signal(location.pathname);
|
|
272
|
+
const [search, setSearch] = signal(location.search);
|
|
273
|
+
const [params, setParams] = signal({});
|
|
274
|
+
const compiledRoutes = [];
|
|
275
|
+
for (const def of routes) {
|
|
276
|
+
if ("children" in def) {
|
|
277
|
+
for (const child of def.children) {
|
|
278
|
+
compiledRoutes.push({
|
|
279
|
+
...child,
|
|
280
|
+
...compilePath(child.path),
|
|
281
|
+
...def.layout !== void 0 && { layout: def.layout },
|
|
282
|
+
...def.guard !== void 0 && { guard: def.guard },
|
|
283
|
+
...def.fallbackPath !== void 0 && { fallbackPath: def.fallbackPath }
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
compiledRoutes.push({ ...def, ...compilePath(def.path) });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function syncLocation() {
|
|
291
|
+
batch(() => {
|
|
292
|
+
setPathname(location.pathname);
|
|
293
|
+
setSearch(location.search);
|
|
294
|
+
for (const route of compiledRoutes) {
|
|
295
|
+
const matched = matchPath(route.path, location.pathname);
|
|
296
|
+
if (matched) {
|
|
297
|
+
setParams(matched);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
setParams({});
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
const doSync = () => {
|
|
305
|
+
if (options.viewTransitions) {
|
|
306
|
+
withViewTransition(syncLocation);
|
|
307
|
+
} else {
|
|
308
|
+
syncLocation();
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
window.addEventListener("popstate", doSync);
|
|
312
|
+
const navigate = (to, { replace = false } = {}) => {
|
|
313
|
+
if (replace) {
|
|
314
|
+
history.replaceState(null, "", to);
|
|
315
|
+
} else {
|
|
316
|
+
history.pushState(null, "", to);
|
|
317
|
+
}
|
|
318
|
+
doSync();
|
|
319
|
+
};
|
|
320
|
+
const router = {
|
|
321
|
+
pathname,
|
|
322
|
+
search,
|
|
323
|
+
params,
|
|
324
|
+
navigate,
|
|
325
|
+
routes: compiledRoutes,
|
|
326
|
+
currentRoute() {
|
|
327
|
+
const path = pathname();
|
|
328
|
+
for (const route of compiledRoutes) {
|
|
329
|
+
if (matchPath(route.path, path)) return route;
|
|
330
|
+
}
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
_router = router;
|
|
335
|
+
return router;
|
|
336
|
+
}
|
|
337
|
+
function useRouter() {
|
|
338
|
+
if (!_router) throw new Error("[axon] No router found. Call createRouter() first.");
|
|
339
|
+
return _router;
|
|
340
|
+
}
|
|
341
|
+
function useParams() {
|
|
342
|
+
return useRouter().params();
|
|
343
|
+
}
|
|
344
|
+
function useNavigate() {
|
|
345
|
+
return useRouter().navigate;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/router/components.tsx
|
|
349
|
+
function RouterView() {
|
|
350
|
+
const router = useRouter();
|
|
351
|
+
const start = document.createComment("RouterView");
|
|
352
|
+
const end = document.createComment("/RouterView");
|
|
353
|
+
const fragment = document.createDocumentFragment();
|
|
354
|
+
fragment.appendChild(start);
|
|
355
|
+
fragment.appendChild(end);
|
|
356
|
+
let currentOwner = null;
|
|
357
|
+
let lastValidPath = null;
|
|
358
|
+
effect(() => {
|
|
359
|
+
const path = router.pathname();
|
|
360
|
+
const parent = end.parentNode;
|
|
361
|
+
if (!parent) return;
|
|
362
|
+
if (currentOwner) {
|
|
363
|
+
disposeOwner(currentOwner);
|
|
364
|
+
currentOwner = null;
|
|
365
|
+
}
|
|
366
|
+
let node = start.nextSibling;
|
|
367
|
+
while (node && node !== end) {
|
|
368
|
+
const next = node.nextSibling;
|
|
369
|
+
parent.removeChild(node);
|
|
370
|
+
node = next;
|
|
371
|
+
}
|
|
372
|
+
for (const route of router.routes) {
|
|
373
|
+
if (!route.regex.test(path)) continue;
|
|
374
|
+
if (route.guard) {
|
|
375
|
+
const access = route.guard();
|
|
376
|
+
if (access === false) {
|
|
377
|
+
const target = lastValidPath ?? route.fallbackPath ?? null;
|
|
378
|
+
if (target) queueMicrotask(() => router.navigate(target, { replace: true }));
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (typeof access === "string") {
|
|
382
|
+
queueMicrotask(() => router.navigate(access, { replace: true }));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
lastValidPath = path;
|
|
387
|
+
const params = router.params();
|
|
388
|
+
let result;
|
|
389
|
+
let owner;
|
|
390
|
+
if (route.layout) {
|
|
391
|
+
const Layout = route.layout;
|
|
392
|
+
const Page = route.component;
|
|
393
|
+
[result, owner] = runOwned(
|
|
394
|
+
() => Layout({ children: (() => runWithOwner(Page, { params })) })
|
|
395
|
+
);
|
|
396
|
+
} else {
|
|
397
|
+
[result, owner] = runOwned(() => route.component({ params }));
|
|
398
|
+
}
|
|
399
|
+
currentOwner = owner;
|
|
400
|
+
const nodes = Array.isArray(result) ? result.flat() : [result];
|
|
401
|
+
nodes.forEach((n) => {
|
|
402
|
+
if (n != null) parent.insertBefore(n, end);
|
|
403
|
+
});
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
return fragment;
|
|
408
|
+
}
|
|
409
|
+
function Link({ href, replace = false, class: cls, activeClass, children }) {
|
|
410
|
+
const router = useRouter();
|
|
411
|
+
const el = document.createElement("a");
|
|
412
|
+
el.href = href;
|
|
413
|
+
if (cls) el.className = cls;
|
|
414
|
+
if (activeClass) {
|
|
415
|
+
const classes = activeClass.split(/\s+/).filter(Boolean);
|
|
416
|
+
effect(() => {
|
|
417
|
+
if (router.pathname() === href) {
|
|
418
|
+
el.classList.add(...classes);
|
|
419
|
+
} else {
|
|
420
|
+
el.classList.remove(...classes);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
el.addEventListener("click", (e) => {
|
|
425
|
+
if (e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey) {
|
|
426
|
+
e.preventDefault();
|
|
427
|
+
router.navigate(href, { replace });
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
const appendChildren = (parent, child) => {
|
|
431
|
+
if (child == null) return;
|
|
432
|
+
if (Array.isArray(child)) child.forEach((c) => appendChildren(parent, c));
|
|
433
|
+
else if (child instanceof Node) parent.appendChild(child);
|
|
434
|
+
else parent.appendChild(document.createTextNode(String(child)));
|
|
435
|
+
};
|
|
436
|
+
appendChildren(el, children);
|
|
437
|
+
return el;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// src/store/store.ts
|
|
441
|
+
function createStore(initialState) {
|
|
442
|
+
const signals = {};
|
|
443
|
+
for (const key of Object.keys(initialState)) {
|
|
444
|
+
signals[key] = signal(initialState[key]);
|
|
445
|
+
}
|
|
446
|
+
const store = new Proxy({}, {
|
|
447
|
+
get(_, key) {
|
|
448
|
+
const sig = signals[key];
|
|
449
|
+
if (!sig) throw new Error(`[axon] store has no property "${key}"`);
|
|
450
|
+
return sig[0]();
|
|
451
|
+
},
|
|
452
|
+
set() {
|
|
453
|
+
throw new Error("[axon] Store is read-only. Use setStore() to update values.");
|
|
454
|
+
},
|
|
455
|
+
has(_, key) {
|
|
456
|
+
return key in signals;
|
|
457
|
+
},
|
|
458
|
+
ownKeys() {
|
|
459
|
+
return Object.keys(signals);
|
|
460
|
+
},
|
|
461
|
+
getOwnPropertyDescriptor(_, key) {
|
|
462
|
+
if (key in signals) return { configurable: true, enumerable: true, writable: false };
|
|
463
|
+
return void 0;
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
function setStore(keyOrPartial, valueOrUpdater) {
|
|
467
|
+
if (typeof keyOrPartial === "object" && keyOrPartial !== null) {
|
|
468
|
+
for (const [k, v] of Object.entries(keyOrPartial)) {
|
|
469
|
+
if (!(k in signals)) {
|
|
470
|
+
signals[k] = signal(v);
|
|
471
|
+
} else {
|
|
472
|
+
signals[k][1](v);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
} else {
|
|
476
|
+
const key = keyOrPartial;
|
|
477
|
+
if (!(key in signals)) {
|
|
478
|
+
signals[key] = signal(valueOrUpdater);
|
|
479
|
+
} else {
|
|
480
|
+
signals[key][1](valueOrUpdater);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return [store, setStore];
|
|
485
|
+
}
|
|
486
|
+
function select(store, selector) {
|
|
487
|
+
return computed(() => selector(store));
|
|
488
|
+
}
|
|
489
|
+
export {
|
|
490
|
+
Dynamic,
|
|
491
|
+
For,
|
|
492
|
+
Fragment,
|
|
493
|
+
Link,
|
|
494
|
+
Portal,
|
|
495
|
+
RouterView,
|
|
496
|
+
Show,
|
|
497
|
+
batch,
|
|
498
|
+
computed,
|
|
499
|
+
createApp,
|
|
500
|
+
createContext,
|
|
501
|
+
createRouter,
|
|
502
|
+
createStore,
|
|
503
|
+
effect,
|
|
504
|
+
h,
|
|
505
|
+
mount,
|
|
506
|
+
onCleanup,
|
|
507
|
+
onMount,
|
|
508
|
+
select,
|
|
509
|
+
signal,
|
|
510
|
+
untrack,
|
|
511
|
+
useNavigate,
|
|
512
|
+
useParams,
|
|
513
|
+
useRouter,
|
|
514
|
+
withViewTransition
|
|
515
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Función que lee un valor reactivo y crea una suscripción si hay un efecto activo. */
|
|
2
|
+
type Getter<T> = () => T;
|
|
3
|
+
/** Función que escribe un signal. Acepta un valor directo o una función updater. */
|
|
4
|
+
type Setter<T> = (value: T | ((prev: T) => T)) => void;
|
|
5
|
+
/** Par [getter, setter] que representa un signal reactivo. */
|
|
6
|
+
type Signal<T> = [Getter<T>, Setter<T>];
|
|
7
|
+
/**
|
|
8
|
+
* Función componente de axon.js.
|
|
9
|
+
* Recibe props y devuelve uno o varios nodos DOM (o null).
|
|
10
|
+
* Se ejecuta exactamente una vez — no hay re-renders.
|
|
11
|
+
*/
|
|
12
|
+
type ComponentFn<P extends Record<string, unknown> = Record<string, unknown>> = (props: P) => Node | Node[] | null;
|
|
13
|
+
/**
|
|
14
|
+
* Tipos válidos como hijos en JSX.
|
|
15
|
+
* Las funciones se tratan como hijos reactivos (signal getters).
|
|
16
|
+
*/
|
|
17
|
+
type JSXChild = string | number | boolean | null | undefined | Node | JSXChild[] | (() => JSXChild);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* axon.js - JSX Runtime: h() and Fragment
|
|
21
|
+
*
|
|
22
|
+
* This is the JSX factory. Configure your build tool with:
|
|
23
|
+
* jsxFactory: 'h'
|
|
24
|
+
* jsxFragment: 'Fragment'
|
|
25
|
+
*
|
|
26
|
+
* Design rule:
|
|
27
|
+
* - Static values (string, number, Node) → applied once
|
|
28
|
+
* - Function values → wrapped in effect() for reactive updates
|
|
29
|
+
*
|
|
30
|
+
* This means signal getters passed as children or props are reactive:
|
|
31
|
+
* const [count] = signal(0)
|
|
32
|
+
* <p>{count}</p> ← count is a getter function → reactive text node
|
|
33
|
+
* <div class={cls}> ← cls is a getter function → reactive attribute
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
declare const Fragment: unique symbol;
|
|
37
|
+
declare global {
|
|
38
|
+
namespace JSX {
|
|
39
|
+
type Element = Node | Node[];
|
|
40
|
+
interface IntrinsicElements {
|
|
41
|
+
[tag: string]: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
interface ElementChildrenAttribute {
|
|
44
|
+
children: Record<string, never>;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
type Props = Record<string, unknown> | null;
|
|
49
|
+
/**
|
|
50
|
+
* JSX factory function. Called automatically by the JSX compiler.
|
|
51
|
+
*
|
|
52
|
+
* - `h(Fragment, ...)` → returns children as a flat array
|
|
53
|
+
* - `h(ComponentFn, props, ...children)` → runs the component with lifecycle
|
|
54
|
+
* - `h('div', props, ...children)` → creates a real DOM element
|
|
55
|
+
*
|
|
56
|
+
* Props that are functions (not event handlers) are reactive:
|
|
57
|
+
* `class={cls}` where `cls` is a signal getter → updates className on each change.
|
|
58
|
+
*
|
|
59
|
+
* Children that are functions are reactive:
|
|
60
|
+
* `{count}` where `count` is a signal getter → updates a text node on each change.
|
|
61
|
+
*/
|
|
62
|
+
declare function h(type: string | ComponentFn | typeof Fragment, props: Props, ...children: JSXChild[]): Node | Node[] | null;
|
|
63
|
+
|
|
64
|
+
export { type ComponentFn as C, Fragment as F, type Getter as G, type JSXChild as J, type Signal as S, type Setter as a, h };
|
package/dist/jsx.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { F as Fragment, h } from './jsx-KOQXGMp1.js';
|
package/dist/jsx.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@faber1999/axon.js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A fine-grained reactive frontend framework with JSX",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup",
|
|
12
|
+
"type-check": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts"
|
|
18
|
+
},
|
|
19
|
+
"./jsx": {
|
|
20
|
+
"import": "./dist/jsx.js",
|
|
21
|
+
"types": "./dist/jsx.d.ts"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"tsup": "^8.0.0",
|
|
26
|
+
"typescript": "^5.0.0",
|
|
27
|
+
"vite": "^5.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|