@cajoa/core 0.12.1 → 0.14.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/dist/index.cjs +255 -1
- package/dist/index.d.cts +60 -8
- package/dist/index.d.ts +60 -8
- package/dist/index.js +248 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,15 +20,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
Dynamic: () => Dynamic,
|
|
23
24
|
ErrorBoundary: () => ErrorBoundary,
|
|
24
25
|
For: () => For,
|
|
25
26
|
Fragment: () => Fragment,
|
|
27
|
+
Match: () => Match,
|
|
28
|
+
Portal: () => Portal,
|
|
26
29
|
Show: () => Show,
|
|
30
|
+
Suspense: () => Suspense,
|
|
31
|
+
Switch: () => Switch,
|
|
27
32
|
batch: () => batch,
|
|
28
33
|
catchError: () => catchError,
|
|
29
34
|
createContext: () => createContext,
|
|
30
35
|
createEffect: () => createEffect,
|
|
31
36
|
createMemo: () => createMemo,
|
|
37
|
+
createResource: () => createResource,
|
|
32
38
|
createRoot: () => createRoot,
|
|
33
39
|
createSignal: () => createSignal,
|
|
34
40
|
getRenderer: () => getRenderer,
|
|
@@ -36,6 +42,7 @@ __export(index_exports, {
|
|
|
36
42
|
jsx: () => jsx,
|
|
37
43
|
jsxDEV: () => jsxDEV,
|
|
38
44
|
jsxs: () => jsxs,
|
|
45
|
+
lazy: () => lazy,
|
|
39
46
|
onCleanup: () => onCleanup,
|
|
40
47
|
onMount: () => onMount,
|
|
41
48
|
provide: () => provide,
|
|
@@ -47,12 +54,22 @@ __export(index_exports, {
|
|
|
47
54
|
module.exports = __toCommonJS(index_exports);
|
|
48
55
|
|
|
49
56
|
// src/dev.ts
|
|
57
|
+
var muted = 0;
|
|
50
58
|
function isDev() {
|
|
51
59
|
return globalThis.__CAJOA_DEV__ === true;
|
|
52
60
|
}
|
|
61
|
+
function quietly(fn) {
|
|
62
|
+
muted++;
|
|
63
|
+
try {
|
|
64
|
+
return fn();
|
|
65
|
+
} finally {
|
|
66
|
+
muted--;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
53
69
|
function warn(message) {
|
|
54
|
-
if (isDev() && typeof console !== "undefined")
|
|
70
|
+
if (muted === 0 && isDev() && typeof console !== "undefined") {
|
|
55
71
|
console.warn(`[cajoa] ${message}`);
|
|
72
|
+
}
|
|
56
73
|
}
|
|
57
74
|
function logError(message, error) {
|
|
58
75
|
if (isDev() && typeof console !== "undefined")
|
|
@@ -259,6 +276,100 @@ function createMemo(fn) {
|
|
|
259
276
|
return value;
|
|
260
277
|
}
|
|
261
278
|
|
|
279
|
+
// src/reactivity/suspense.ts
|
|
280
|
+
var SuspenseContext = createContext(null);
|
|
281
|
+
function trackPending() {
|
|
282
|
+
const state = quietly(() => useContext(SuspenseContext));
|
|
283
|
+
return state ? () => state.add() : void 0;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/reactivity/resource.ts
|
|
287
|
+
function isPromiseLike(value) {
|
|
288
|
+
return typeof value?.then === "function";
|
|
289
|
+
}
|
|
290
|
+
function createResource(a, b) {
|
|
291
|
+
const hasSource = typeof b === "function";
|
|
292
|
+
const source = hasSource ? a : () => true;
|
|
293
|
+
const fetcher = hasSource ? b : a;
|
|
294
|
+
const [data, setData] = createSignal(void 0);
|
|
295
|
+
const [failure, setFailure] = createSignal(null);
|
|
296
|
+
const [loading, setLoading] = createSignal(false);
|
|
297
|
+
const [version, setVersion] = createSignal(0);
|
|
298
|
+
let run = 0;
|
|
299
|
+
const track = trackPending();
|
|
300
|
+
let release;
|
|
301
|
+
let settled = false;
|
|
302
|
+
const finish = (id, outcome) => {
|
|
303
|
+
if (id !== run) return;
|
|
304
|
+
settled = true;
|
|
305
|
+
batch(() => {
|
|
306
|
+
setLoading(false);
|
|
307
|
+
if ("error" in outcome) {
|
|
308
|
+
setFailure({ error: outcome.error });
|
|
309
|
+
} else {
|
|
310
|
+
setFailure(null);
|
|
311
|
+
setData(() => outcome.value);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
release?.();
|
|
315
|
+
release = void 0;
|
|
316
|
+
};
|
|
317
|
+
createEffect(() => {
|
|
318
|
+
const s = source();
|
|
319
|
+
version();
|
|
320
|
+
run++;
|
|
321
|
+
const id = run;
|
|
322
|
+
onCleanup(() => {
|
|
323
|
+
run++;
|
|
324
|
+
release?.();
|
|
325
|
+
release = void 0;
|
|
326
|
+
});
|
|
327
|
+
if (s === false || s === null || s === void 0) {
|
|
328
|
+
batch(() => {
|
|
329
|
+
setLoading(false);
|
|
330
|
+
setFailure(null);
|
|
331
|
+
setData(() => void 0);
|
|
332
|
+
});
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (!settled && track && !release) release = track();
|
|
336
|
+
setLoading(true);
|
|
337
|
+
setFailure(null);
|
|
338
|
+
let result;
|
|
339
|
+
try {
|
|
340
|
+
result = untrack(() => fetcher(s));
|
|
341
|
+
} catch (error) {
|
|
342
|
+
finish(id, { error });
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (isPromiseLike(result)) {
|
|
346
|
+
result.then(
|
|
347
|
+
(value) => finish(id, { value }),
|
|
348
|
+
(error) => finish(id, { error })
|
|
349
|
+
);
|
|
350
|
+
} else {
|
|
351
|
+
finish(id, { value: result });
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
const resource = (() => {
|
|
355
|
+
const failed = failure();
|
|
356
|
+
if (failed) throw failed.error;
|
|
357
|
+
return data();
|
|
358
|
+
});
|
|
359
|
+
resource.loading = loading;
|
|
360
|
+
resource.error = () => failure()?.error;
|
|
361
|
+
return [
|
|
362
|
+
resource,
|
|
363
|
+
{
|
|
364
|
+
refetch: () => setVersion((v) => v + 1),
|
|
365
|
+
mutate: (value) => batch(() => {
|
|
366
|
+
setFailure(null);
|
|
367
|
+
setData(() => value);
|
|
368
|
+
})
|
|
369
|
+
}
|
|
370
|
+
];
|
|
371
|
+
}
|
|
372
|
+
|
|
262
373
|
// src/renderer/index.ts
|
|
263
374
|
var current = null;
|
|
264
375
|
function setRenderer(renderer) {
|
|
@@ -458,6 +569,32 @@ function ErrorBoundary(props) {
|
|
|
458
569
|
};
|
|
459
570
|
}
|
|
460
571
|
|
|
572
|
+
// src/components/switch.ts
|
|
573
|
+
function resolve3(value) {
|
|
574
|
+
return typeof value === "function" ? value() : value;
|
|
575
|
+
}
|
|
576
|
+
function Match(props) {
|
|
577
|
+
return { __match: true, when: props.when, children: props.children };
|
|
578
|
+
}
|
|
579
|
+
var isMatch = (value) => typeof value === "object" && value !== null && value.__match === true;
|
|
580
|
+
function Switch(props) {
|
|
581
|
+
const matches = [props.children].flat(Infinity).filter(isMatch);
|
|
582
|
+
const selected = createMemo(() => {
|
|
583
|
+
for (let i = 0; i < matches.length; i++) {
|
|
584
|
+
if (resolve3(matches[i].when)) return i;
|
|
585
|
+
}
|
|
586
|
+
return -1;
|
|
587
|
+
});
|
|
588
|
+
return () => {
|
|
589
|
+
const i = selected();
|
|
590
|
+
return untrack(
|
|
591
|
+
() => resolve3(
|
|
592
|
+
i === -1 ? props.fallback ?? null : matches[i].children ?? null
|
|
593
|
+
)
|
|
594
|
+
);
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
461
598
|
// src/jsx-runtime.ts
|
|
462
599
|
function jsx(type, props) {
|
|
463
600
|
if (typeof type === "function") return type(props ?? {});
|
|
@@ -472,17 +609,133 @@ function Fragment(props) {
|
|
|
472
609
|
appendChildren(frag, props.children === void 0 ? [] : [props.children]);
|
|
473
610
|
return frag;
|
|
474
611
|
}
|
|
612
|
+
|
|
613
|
+
// src/components/dynamic.ts
|
|
614
|
+
function Dynamic(props) {
|
|
615
|
+
const { is, ...rest } = props;
|
|
616
|
+
const read = typeof is === "function" ? is : () => is;
|
|
617
|
+
return () => {
|
|
618
|
+
const target = read();
|
|
619
|
+
if (target == null || target === false) return null;
|
|
620
|
+
if (typeof target !== "string" && typeof target !== "function") {
|
|
621
|
+
throw new Error(
|
|
622
|
+
`<Dynamic>: \`is\` must be a tag name, or a function that returns a tag name or a component (got ${typeof target}). To render a component directly, write is={() => MyComponent}.`
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
return untrack(() => jsx(target, rest));
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/components/portal.ts
|
|
630
|
+
function resolve4(value) {
|
|
631
|
+
return typeof value === "function" ? value() : value;
|
|
632
|
+
}
|
|
633
|
+
function Portal(props) {
|
|
634
|
+
const r = getRenderer();
|
|
635
|
+
const mount = props.mount ?? r.portalRoot?.();
|
|
636
|
+
if (!mount) {
|
|
637
|
+
throw new Error(
|
|
638
|
+
"<Portal>: nowhere to render into. Pass a `mount` element (this renderer has no default)."
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
const start = r.createMarker();
|
|
642
|
+
const end = r.createMarker();
|
|
643
|
+
r.insert(mount, start);
|
|
644
|
+
r.insert(
|
|
645
|
+
mount,
|
|
646
|
+
materialize(() => resolve4(props.children ?? null))
|
|
647
|
+
);
|
|
648
|
+
r.insert(mount, end);
|
|
649
|
+
onCleanup(() => {
|
|
650
|
+
let node = r.nextSibling(start);
|
|
651
|
+
while (node && node !== end) {
|
|
652
|
+
const next = r.nextSibling(node);
|
|
653
|
+
r.remove(node);
|
|
654
|
+
node = next;
|
|
655
|
+
}
|
|
656
|
+
r.remove(start);
|
|
657
|
+
r.remove(end);
|
|
658
|
+
});
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// src/components/suspense.ts
|
|
663
|
+
function resolve5(value) {
|
|
664
|
+
return typeof value === "function" ? value() : value;
|
|
665
|
+
}
|
|
666
|
+
function Suspense(props) {
|
|
667
|
+
const [pending2, setPending] = createSignal(0);
|
|
668
|
+
const [ready, setReady] = createSignal(false);
|
|
669
|
+
const state = {
|
|
670
|
+
add() {
|
|
671
|
+
setPending((n) => n + 1);
|
|
672
|
+
let released = false;
|
|
673
|
+
return () => {
|
|
674
|
+
if (released) return;
|
|
675
|
+
released = true;
|
|
676
|
+
setPending((n) => n - 1);
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
const content = untrack(
|
|
681
|
+
() => provide(
|
|
682
|
+
SuspenseContext,
|
|
683
|
+
state,
|
|
684
|
+
() => materialize(resolve5(props.children ?? null))
|
|
685
|
+
)
|
|
686
|
+
);
|
|
687
|
+
createEffect(() => {
|
|
688
|
+
if (pending2() === 0) setReady(true);
|
|
689
|
+
});
|
|
690
|
+
return () => ready() ? content : untrack(() => resolve5(props.fallback ?? null));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/components/lazy.ts
|
|
694
|
+
function lazy(loader) {
|
|
695
|
+
let promise;
|
|
696
|
+
let component;
|
|
697
|
+
let failure = null;
|
|
698
|
+
const load = () => promise ??= loader().then(
|
|
699
|
+
(module2) => {
|
|
700
|
+
component = typeof module2 === "function" ? module2 : module2.default;
|
|
701
|
+
},
|
|
702
|
+
(error) => {
|
|
703
|
+
failure = { error };
|
|
704
|
+
}
|
|
705
|
+
);
|
|
706
|
+
return function LazyComponent(props) {
|
|
707
|
+
if (component) return component(props);
|
|
708
|
+
if (failure) throw failure.error;
|
|
709
|
+
const release = trackPending()?.();
|
|
710
|
+
const [done, setDone] = createSignal(false);
|
|
711
|
+
load().then(() => {
|
|
712
|
+
setDone(true);
|
|
713
|
+
release?.();
|
|
714
|
+
});
|
|
715
|
+
return () => {
|
|
716
|
+
done();
|
|
717
|
+
if (failure) throw failure.error;
|
|
718
|
+
return component ? untrack(() => component(props)) : null;
|
|
719
|
+
};
|
|
720
|
+
};
|
|
721
|
+
}
|
|
475
722
|
// Annotate the CommonJS export names for ESM import in node:
|
|
476
723
|
0 && (module.exports = {
|
|
724
|
+
Dynamic,
|
|
477
725
|
ErrorBoundary,
|
|
478
726
|
For,
|
|
479
727
|
Fragment,
|
|
728
|
+
Match,
|
|
729
|
+
Portal,
|
|
480
730
|
Show,
|
|
731
|
+
Suspense,
|
|
732
|
+
Switch,
|
|
481
733
|
batch,
|
|
482
734
|
catchError,
|
|
483
735
|
createContext,
|
|
484
736
|
createEffect,
|
|
485
737
|
createMemo,
|
|
738
|
+
createResource,
|
|
486
739
|
createRoot,
|
|
487
740
|
createSignal,
|
|
488
741
|
getRenderer,
|
|
@@ -490,6 +743,7 @@ function Fragment(props) {
|
|
|
490
743
|
jsx,
|
|
491
744
|
jsxDEV,
|
|
492
745
|
jsxs,
|
|
746
|
+
lazy,
|
|
493
747
|
onCleanup,
|
|
494
748
|
onMount,
|
|
495
749
|
provide,
|
package/dist/index.d.cts
CHANGED
|
@@ -17,6 +17,19 @@ declare function useContext<T>(ctx: Context<T>): T;
|
|
|
17
17
|
|
|
18
18
|
declare function createMemo<T>(fn: () => T): () => T;
|
|
19
19
|
|
|
20
|
+
interface Resource<T> {
|
|
21
|
+
(): T | undefined;
|
|
22
|
+
loading: () => boolean;
|
|
23
|
+
error: () => unknown;
|
|
24
|
+
}
|
|
25
|
+
interface ResourceActions<T> {
|
|
26
|
+
refetch: () => void;
|
|
27
|
+
mutate(value: T | undefined): void;
|
|
28
|
+
}
|
|
29
|
+
type ResourceTuple<T> = [Resource<T>, ResourceActions<T>];
|
|
30
|
+
declare function createResource<T>(fetcher: () => T | Promise<T>): ResourceTuple<T>;
|
|
31
|
+
declare function createResource<T, S>(source: () => S | false | null | undefined, fetcher: (source: S) => T | Promise<T>): ResourceTuple<T>;
|
|
32
|
+
|
|
20
33
|
type HostNode = object;
|
|
21
34
|
interface Renderer<N = any> {
|
|
22
35
|
createElement(tag: string): N;
|
|
@@ -28,6 +41,7 @@ interface Renderer<N = any> {
|
|
|
28
41
|
remove(node: N): void;
|
|
29
42
|
nextSibling(node: N): N | null;
|
|
30
43
|
parentNode(node: N): N | null;
|
|
44
|
+
portalRoot?(): N;
|
|
31
45
|
clear(parent: N): void;
|
|
32
46
|
setProp(el: N, name: string, value: unknown): void;
|
|
33
47
|
}
|
|
@@ -37,11 +51,11 @@ declare function h(tag: string, props: Record<string, any> | null, ...children:
|
|
|
37
51
|
|
|
38
52
|
declare function render(component: () => HostNode, container: HostNode): () => void;
|
|
39
53
|
|
|
40
|
-
type Lazy$
|
|
54
|
+
type Lazy$4<T> = T | (() => T);
|
|
41
55
|
declare function Show<T>(props: {
|
|
42
|
-
when: Lazy$
|
|
43
|
-
children?: Lazy$
|
|
44
|
-
fallback?: Lazy$
|
|
56
|
+
when: Lazy$4<T>;
|
|
57
|
+
children?: Lazy$4<Child>;
|
|
58
|
+
fallback?: Lazy$4<Child>;
|
|
45
59
|
}): () => string | number | boolean | object | null;
|
|
46
60
|
|
|
47
61
|
declare function For<T>(props: {
|
|
@@ -50,13 +64,51 @@ declare function For<T>(props: {
|
|
|
50
64
|
key?: (item: T, index: number) => unknown;
|
|
51
65
|
}): () => object[];
|
|
52
66
|
|
|
53
|
-
type Lazy<T> = T | (() => T);
|
|
67
|
+
type Lazy$3<T> = T | (() => T);
|
|
54
68
|
declare function ErrorBoundary(props: {
|
|
55
69
|
fallback: Child | ((error: unknown, reset: () => void) => Child);
|
|
56
|
-
children?: Lazy<Child>;
|
|
70
|
+
children?: Lazy$3<Child>;
|
|
57
71
|
onError?: (error: unknown) => void;
|
|
58
72
|
}): () => unknown;
|
|
59
73
|
|
|
74
|
+
type Lazy$2<T> = T | (() => T);
|
|
75
|
+
interface MatchDef {
|
|
76
|
+
__match: true;
|
|
77
|
+
when: Lazy$2<unknown>;
|
|
78
|
+
children?: Lazy$2<Child>;
|
|
79
|
+
}
|
|
80
|
+
declare function Match(props: {
|
|
81
|
+
when: Lazy$2<unknown>;
|
|
82
|
+
children?: Lazy$2<Child>;
|
|
83
|
+
}): MatchDef;
|
|
84
|
+
declare function Switch(props: {
|
|
85
|
+
fallback?: Lazy$2<Child>;
|
|
86
|
+
children?: unknown;
|
|
87
|
+
}): () => string | number | boolean | object | null;
|
|
88
|
+
|
|
89
|
+
type Target = string | ((props: any) => unknown) | null | undefined | false;
|
|
90
|
+
declare function Dynamic(props: {
|
|
91
|
+
is: Target | (() => Target);
|
|
92
|
+
[key: string]: unknown;
|
|
93
|
+
}): () => any;
|
|
94
|
+
|
|
95
|
+
type Lazy$1<T> = T | (() => T);
|
|
96
|
+
declare function Portal(props: {
|
|
97
|
+
mount?: HostNode;
|
|
98
|
+
children?: Lazy$1<Child>;
|
|
99
|
+
}): null;
|
|
100
|
+
|
|
101
|
+
type Lazy<T> = T | (() => T);
|
|
102
|
+
declare function Suspense(props: {
|
|
103
|
+
fallback?: Lazy<Child>;
|
|
104
|
+
children?: Lazy<Child>;
|
|
105
|
+
}): () => unknown;
|
|
106
|
+
|
|
107
|
+
type Component<P> = (props: P) => unknown;
|
|
108
|
+
declare function lazy<P = Record<string, unknown>>(loader: () => Promise<{
|
|
109
|
+
default: Component<P>;
|
|
110
|
+
} | Component<P>>): Component<P>;
|
|
111
|
+
|
|
60
112
|
declare function jsx(type: any, props: Record<string, any> | null): any;
|
|
61
113
|
declare const jsxs: typeof jsx;
|
|
62
114
|
declare const jsxDEV: typeof jsx;
|
|
@@ -76,5 +128,5 @@ declare function Fragment(props: {
|
|
|
76
128
|
declare function setRenderer(renderer: Renderer): void;
|
|
77
129
|
declare function getRenderer(): Renderer;
|
|
78
130
|
|
|
79
|
-
export { ErrorBoundary, For, Fragment, JSX, Show, batch, catchError, createContext, createEffect, createMemo, createRoot, createSignal, getRenderer, h, jsx, jsxDEV, jsxs, onCleanup, onMount, provide, render, setRenderer, untrack, useContext };
|
|
80
|
-
export type { Child, Context, HostNode, Renderer };
|
|
131
|
+
export { Dynamic, ErrorBoundary, For, Fragment, JSX, Match, Portal, Show, Suspense, Switch, batch, catchError, createContext, createEffect, createMemo, createResource, createRoot, createSignal, getRenderer, h, jsx, jsxDEV, jsxs, lazy, onCleanup, onMount, provide, render, setRenderer, untrack, useContext };
|
|
132
|
+
export type { Child, Context, HostNode, MatchDef, Renderer, Resource, ResourceActions };
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,19 @@ declare function useContext<T>(ctx: Context<T>): T;
|
|
|
17
17
|
|
|
18
18
|
declare function createMemo<T>(fn: () => T): () => T;
|
|
19
19
|
|
|
20
|
+
interface Resource<T> {
|
|
21
|
+
(): T | undefined;
|
|
22
|
+
loading: () => boolean;
|
|
23
|
+
error: () => unknown;
|
|
24
|
+
}
|
|
25
|
+
interface ResourceActions<T> {
|
|
26
|
+
refetch: () => void;
|
|
27
|
+
mutate(value: T | undefined): void;
|
|
28
|
+
}
|
|
29
|
+
type ResourceTuple<T> = [Resource<T>, ResourceActions<T>];
|
|
30
|
+
declare function createResource<T>(fetcher: () => T | Promise<T>): ResourceTuple<T>;
|
|
31
|
+
declare function createResource<T, S>(source: () => S | false | null | undefined, fetcher: (source: S) => T | Promise<T>): ResourceTuple<T>;
|
|
32
|
+
|
|
20
33
|
type HostNode = object;
|
|
21
34
|
interface Renderer<N = any> {
|
|
22
35
|
createElement(tag: string): N;
|
|
@@ -28,6 +41,7 @@ interface Renderer<N = any> {
|
|
|
28
41
|
remove(node: N): void;
|
|
29
42
|
nextSibling(node: N): N | null;
|
|
30
43
|
parentNode(node: N): N | null;
|
|
44
|
+
portalRoot?(): N;
|
|
31
45
|
clear(parent: N): void;
|
|
32
46
|
setProp(el: N, name: string, value: unknown): void;
|
|
33
47
|
}
|
|
@@ -37,11 +51,11 @@ declare function h(tag: string, props: Record<string, any> | null, ...children:
|
|
|
37
51
|
|
|
38
52
|
declare function render(component: () => HostNode, container: HostNode): () => void;
|
|
39
53
|
|
|
40
|
-
type Lazy$
|
|
54
|
+
type Lazy$4<T> = T | (() => T);
|
|
41
55
|
declare function Show<T>(props: {
|
|
42
|
-
when: Lazy$
|
|
43
|
-
children?: Lazy$
|
|
44
|
-
fallback?: Lazy$
|
|
56
|
+
when: Lazy$4<T>;
|
|
57
|
+
children?: Lazy$4<Child>;
|
|
58
|
+
fallback?: Lazy$4<Child>;
|
|
45
59
|
}): () => string | number | boolean | object | null;
|
|
46
60
|
|
|
47
61
|
declare function For<T>(props: {
|
|
@@ -50,13 +64,51 @@ declare function For<T>(props: {
|
|
|
50
64
|
key?: (item: T, index: number) => unknown;
|
|
51
65
|
}): () => object[];
|
|
52
66
|
|
|
53
|
-
type Lazy<T> = T | (() => T);
|
|
67
|
+
type Lazy$3<T> = T | (() => T);
|
|
54
68
|
declare function ErrorBoundary(props: {
|
|
55
69
|
fallback: Child | ((error: unknown, reset: () => void) => Child);
|
|
56
|
-
children?: Lazy<Child>;
|
|
70
|
+
children?: Lazy$3<Child>;
|
|
57
71
|
onError?: (error: unknown) => void;
|
|
58
72
|
}): () => unknown;
|
|
59
73
|
|
|
74
|
+
type Lazy$2<T> = T | (() => T);
|
|
75
|
+
interface MatchDef {
|
|
76
|
+
__match: true;
|
|
77
|
+
when: Lazy$2<unknown>;
|
|
78
|
+
children?: Lazy$2<Child>;
|
|
79
|
+
}
|
|
80
|
+
declare function Match(props: {
|
|
81
|
+
when: Lazy$2<unknown>;
|
|
82
|
+
children?: Lazy$2<Child>;
|
|
83
|
+
}): MatchDef;
|
|
84
|
+
declare function Switch(props: {
|
|
85
|
+
fallback?: Lazy$2<Child>;
|
|
86
|
+
children?: unknown;
|
|
87
|
+
}): () => string | number | boolean | object | null;
|
|
88
|
+
|
|
89
|
+
type Target = string | ((props: any) => unknown) | null | undefined | false;
|
|
90
|
+
declare function Dynamic(props: {
|
|
91
|
+
is: Target | (() => Target);
|
|
92
|
+
[key: string]: unknown;
|
|
93
|
+
}): () => any;
|
|
94
|
+
|
|
95
|
+
type Lazy$1<T> = T | (() => T);
|
|
96
|
+
declare function Portal(props: {
|
|
97
|
+
mount?: HostNode;
|
|
98
|
+
children?: Lazy$1<Child>;
|
|
99
|
+
}): null;
|
|
100
|
+
|
|
101
|
+
type Lazy<T> = T | (() => T);
|
|
102
|
+
declare function Suspense(props: {
|
|
103
|
+
fallback?: Lazy<Child>;
|
|
104
|
+
children?: Lazy<Child>;
|
|
105
|
+
}): () => unknown;
|
|
106
|
+
|
|
107
|
+
type Component<P> = (props: P) => unknown;
|
|
108
|
+
declare function lazy<P = Record<string, unknown>>(loader: () => Promise<{
|
|
109
|
+
default: Component<P>;
|
|
110
|
+
} | Component<P>>): Component<P>;
|
|
111
|
+
|
|
60
112
|
declare function jsx(type: any, props: Record<string, any> | null): any;
|
|
61
113
|
declare const jsxs: typeof jsx;
|
|
62
114
|
declare const jsxDEV: typeof jsx;
|
|
@@ -76,5 +128,5 @@ declare function Fragment(props: {
|
|
|
76
128
|
declare function setRenderer(renderer: Renderer): void;
|
|
77
129
|
declare function getRenderer(): Renderer;
|
|
78
130
|
|
|
79
|
-
export { ErrorBoundary, For, Fragment, JSX, Show, batch, catchError, createContext, createEffect, createMemo, createRoot, createSignal, getRenderer, h, jsx, jsxDEV, jsxs, onCleanup, onMount, provide, render, setRenderer, untrack, useContext };
|
|
80
|
-
export type { Child, Context, HostNode, Renderer };
|
|
131
|
+
export { Dynamic, ErrorBoundary, For, Fragment, JSX, Match, Portal, Show, Suspense, Switch, batch, catchError, createContext, createEffect, createMemo, createResource, createRoot, createSignal, getRenderer, h, jsx, jsxDEV, jsxs, lazy, onCleanup, onMount, provide, render, setRenderer, untrack, useContext };
|
|
132
|
+
export type { Child, Context, HostNode, MatchDef, Renderer, Resource, ResourceActions };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
// src/dev.ts
|
|
2
|
+
var muted = 0;
|
|
2
3
|
function isDev() {
|
|
3
4
|
return globalThis.__CAJOA_DEV__ === true;
|
|
4
5
|
}
|
|
6
|
+
function quietly(fn) {
|
|
7
|
+
muted++;
|
|
8
|
+
try {
|
|
9
|
+
return fn();
|
|
10
|
+
} finally {
|
|
11
|
+
muted--;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
5
14
|
function warn(message) {
|
|
6
|
-
if (isDev() && typeof console !== "undefined")
|
|
15
|
+
if (muted === 0 && isDev() && typeof console !== "undefined") {
|
|
7
16
|
console.warn(`[cajoa] ${message}`);
|
|
17
|
+
}
|
|
8
18
|
}
|
|
9
19
|
function logError(message, error) {
|
|
10
20
|
if (isDev() && typeof console !== "undefined")
|
|
@@ -211,6 +221,100 @@ function createMemo(fn) {
|
|
|
211
221
|
return value;
|
|
212
222
|
}
|
|
213
223
|
|
|
224
|
+
// src/reactivity/suspense.ts
|
|
225
|
+
var SuspenseContext = createContext(null);
|
|
226
|
+
function trackPending() {
|
|
227
|
+
const state = quietly(() => useContext(SuspenseContext));
|
|
228
|
+
return state ? () => state.add() : void 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/reactivity/resource.ts
|
|
232
|
+
function isPromiseLike(value) {
|
|
233
|
+
return typeof value?.then === "function";
|
|
234
|
+
}
|
|
235
|
+
function createResource(a, b) {
|
|
236
|
+
const hasSource = typeof b === "function";
|
|
237
|
+
const source = hasSource ? a : () => true;
|
|
238
|
+
const fetcher = hasSource ? b : a;
|
|
239
|
+
const [data, setData] = createSignal(void 0);
|
|
240
|
+
const [failure, setFailure] = createSignal(null);
|
|
241
|
+
const [loading, setLoading] = createSignal(false);
|
|
242
|
+
const [version, setVersion] = createSignal(0);
|
|
243
|
+
let run = 0;
|
|
244
|
+
const track = trackPending();
|
|
245
|
+
let release;
|
|
246
|
+
let settled = false;
|
|
247
|
+
const finish = (id, outcome) => {
|
|
248
|
+
if (id !== run) return;
|
|
249
|
+
settled = true;
|
|
250
|
+
batch(() => {
|
|
251
|
+
setLoading(false);
|
|
252
|
+
if ("error" in outcome) {
|
|
253
|
+
setFailure({ error: outcome.error });
|
|
254
|
+
} else {
|
|
255
|
+
setFailure(null);
|
|
256
|
+
setData(() => outcome.value);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
release?.();
|
|
260
|
+
release = void 0;
|
|
261
|
+
};
|
|
262
|
+
createEffect(() => {
|
|
263
|
+
const s = source();
|
|
264
|
+
version();
|
|
265
|
+
run++;
|
|
266
|
+
const id = run;
|
|
267
|
+
onCleanup(() => {
|
|
268
|
+
run++;
|
|
269
|
+
release?.();
|
|
270
|
+
release = void 0;
|
|
271
|
+
});
|
|
272
|
+
if (s === false || s === null || s === void 0) {
|
|
273
|
+
batch(() => {
|
|
274
|
+
setLoading(false);
|
|
275
|
+
setFailure(null);
|
|
276
|
+
setData(() => void 0);
|
|
277
|
+
});
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (!settled && track && !release) release = track();
|
|
281
|
+
setLoading(true);
|
|
282
|
+
setFailure(null);
|
|
283
|
+
let result;
|
|
284
|
+
try {
|
|
285
|
+
result = untrack(() => fetcher(s));
|
|
286
|
+
} catch (error) {
|
|
287
|
+
finish(id, { error });
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (isPromiseLike(result)) {
|
|
291
|
+
result.then(
|
|
292
|
+
(value) => finish(id, { value }),
|
|
293
|
+
(error) => finish(id, { error })
|
|
294
|
+
);
|
|
295
|
+
} else {
|
|
296
|
+
finish(id, { value: result });
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
const resource = (() => {
|
|
300
|
+
const failed = failure();
|
|
301
|
+
if (failed) throw failed.error;
|
|
302
|
+
return data();
|
|
303
|
+
});
|
|
304
|
+
resource.loading = loading;
|
|
305
|
+
resource.error = () => failure()?.error;
|
|
306
|
+
return [
|
|
307
|
+
resource,
|
|
308
|
+
{
|
|
309
|
+
refetch: () => setVersion((v) => v + 1),
|
|
310
|
+
mutate: (value) => batch(() => {
|
|
311
|
+
setFailure(null);
|
|
312
|
+
setData(() => value);
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
];
|
|
316
|
+
}
|
|
317
|
+
|
|
214
318
|
// src/renderer/index.ts
|
|
215
319
|
var current = null;
|
|
216
320
|
function setRenderer(renderer) {
|
|
@@ -410,6 +514,32 @@ function ErrorBoundary(props) {
|
|
|
410
514
|
};
|
|
411
515
|
}
|
|
412
516
|
|
|
517
|
+
// src/components/switch.ts
|
|
518
|
+
function resolve3(value) {
|
|
519
|
+
return typeof value === "function" ? value() : value;
|
|
520
|
+
}
|
|
521
|
+
function Match(props) {
|
|
522
|
+
return { __match: true, when: props.when, children: props.children };
|
|
523
|
+
}
|
|
524
|
+
var isMatch = (value) => typeof value === "object" && value !== null && value.__match === true;
|
|
525
|
+
function Switch(props) {
|
|
526
|
+
const matches = [props.children].flat(Infinity).filter(isMatch);
|
|
527
|
+
const selected = createMemo(() => {
|
|
528
|
+
for (let i = 0; i < matches.length; i++) {
|
|
529
|
+
if (resolve3(matches[i].when)) return i;
|
|
530
|
+
}
|
|
531
|
+
return -1;
|
|
532
|
+
});
|
|
533
|
+
return () => {
|
|
534
|
+
const i = selected();
|
|
535
|
+
return untrack(
|
|
536
|
+
() => resolve3(
|
|
537
|
+
i === -1 ? props.fallback ?? null : matches[i].children ?? null
|
|
538
|
+
)
|
|
539
|
+
);
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
413
543
|
// src/jsx-runtime.ts
|
|
414
544
|
function jsx(type, props) {
|
|
415
545
|
if (typeof type === "function") return type(props ?? {});
|
|
@@ -424,16 +554,132 @@ function Fragment(props) {
|
|
|
424
554
|
appendChildren(frag, props.children === void 0 ? [] : [props.children]);
|
|
425
555
|
return frag;
|
|
426
556
|
}
|
|
557
|
+
|
|
558
|
+
// src/components/dynamic.ts
|
|
559
|
+
function Dynamic(props) {
|
|
560
|
+
const { is, ...rest } = props;
|
|
561
|
+
const read = typeof is === "function" ? is : () => is;
|
|
562
|
+
return () => {
|
|
563
|
+
const target = read();
|
|
564
|
+
if (target == null || target === false) return null;
|
|
565
|
+
if (typeof target !== "string" && typeof target !== "function") {
|
|
566
|
+
throw new Error(
|
|
567
|
+
`<Dynamic>: \`is\` must be a tag name, or a function that returns a tag name or a component (got ${typeof target}). To render a component directly, write is={() => MyComponent}.`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
return untrack(() => jsx(target, rest));
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/components/portal.ts
|
|
575
|
+
function resolve4(value) {
|
|
576
|
+
return typeof value === "function" ? value() : value;
|
|
577
|
+
}
|
|
578
|
+
function Portal(props) {
|
|
579
|
+
const r = getRenderer();
|
|
580
|
+
const mount = props.mount ?? r.portalRoot?.();
|
|
581
|
+
if (!mount) {
|
|
582
|
+
throw new Error(
|
|
583
|
+
"<Portal>: nowhere to render into. Pass a `mount` element (this renderer has no default)."
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
const start = r.createMarker();
|
|
587
|
+
const end = r.createMarker();
|
|
588
|
+
r.insert(mount, start);
|
|
589
|
+
r.insert(
|
|
590
|
+
mount,
|
|
591
|
+
materialize(() => resolve4(props.children ?? null))
|
|
592
|
+
);
|
|
593
|
+
r.insert(mount, end);
|
|
594
|
+
onCleanup(() => {
|
|
595
|
+
let node = r.nextSibling(start);
|
|
596
|
+
while (node && node !== end) {
|
|
597
|
+
const next = r.nextSibling(node);
|
|
598
|
+
r.remove(node);
|
|
599
|
+
node = next;
|
|
600
|
+
}
|
|
601
|
+
r.remove(start);
|
|
602
|
+
r.remove(end);
|
|
603
|
+
});
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/components/suspense.ts
|
|
608
|
+
function resolve5(value) {
|
|
609
|
+
return typeof value === "function" ? value() : value;
|
|
610
|
+
}
|
|
611
|
+
function Suspense(props) {
|
|
612
|
+
const [pending2, setPending] = createSignal(0);
|
|
613
|
+
const [ready, setReady] = createSignal(false);
|
|
614
|
+
const state = {
|
|
615
|
+
add() {
|
|
616
|
+
setPending((n) => n + 1);
|
|
617
|
+
let released = false;
|
|
618
|
+
return () => {
|
|
619
|
+
if (released) return;
|
|
620
|
+
released = true;
|
|
621
|
+
setPending((n) => n - 1);
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
const content = untrack(
|
|
626
|
+
() => provide(
|
|
627
|
+
SuspenseContext,
|
|
628
|
+
state,
|
|
629
|
+
() => materialize(resolve5(props.children ?? null))
|
|
630
|
+
)
|
|
631
|
+
);
|
|
632
|
+
createEffect(() => {
|
|
633
|
+
if (pending2() === 0) setReady(true);
|
|
634
|
+
});
|
|
635
|
+
return () => ready() ? content : untrack(() => resolve5(props.fallback ?? null));
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/components/lazy.ts
|
|
639
|
+
function lazy(loader) {
|
|
640
|
+
let promise;
|
|
641
|
+
let component;
|
|
642
|
+
let failure = null;
|
|
643
|
+
const load = () => promise ??= loader().then(
|
|
644
|
+
(module) => {
|
|
645
|
+
component = typeof module === "function" ? module : module.default;
|
|
646
|
+
},
|
|
647
|
+
(error) => {
|
|
648
|
+
failure = { error };
|
|
649
|
+
}
|
|
650
|
+
);
|
|
651
|
+
return function LazyComponent(props) {
|
|
652
|
+
if (component) return component(props);
|
|
653
|
+
if (failure) throw failure.error;
|
|
654
|
+
const release = trackPending()?.();
|
|
655
|
+
const [done, setDone] = createSignal(false);
|
|
656
|
+
load().then(() => {
|
|
657
|
+
setDone(true);
|
|
658
|
+
release?.();
|
|
659
|
+
});
|
|
660
|
+
return () => {
|
|
661
|
+
done();
|
|
662
|
+
if (failure) throw failure.error;
|
|
663
|
+
return component ? untrack(() => component(props)) : null;
|
|
664
|
+
};
|
|
665
|
+
};
|
|
666
|
+
}
|
|
427
667
|
export {
|
|
668
|
+
Dynamic,
|
|
428
669
|
ErrorBoundary,
|
|
429
670
|
For,
|
|
430
671
|
Fragment,
|
|
672
|
+
Match,
|
|
673
|
+
Portal,
|
|
431
674
|
Show,
|
|
675
|
+
Suspense,
|
|
676
|
+
Switch,
|
|
432
677
|
batch,
|
|
433
678
|
catchError,
|
|
434
679
|
createContext,
|
|
435
680
|
createEffect,
|
|
436
681
|
createMemo,
|
|
682
|
+
createResource,
|
|
437
683
|
createRoot,
|
|
438
684
|
createSignal,
|
|
439
685
|
getRenderer,
|
|
@@ -441,6 +687,7 @@ export {
|
|
|
441
687
|
jsx,
|
|
442
688
|
jsxDEV,
|
|
443
689
|
jsxs,
|
|
690
|
+
lazy,
|
|
444
691
|
onCleanup,
|
|
445
692
|
onMount,
|
|
446
693
|
provide,
|