@fluixi/core 1.0.0-alpha.83 → 1.0.0-alpha.85
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/cdn/core.global.js +2 -1
- package/dist/cli/generate.cjs +4117 -292
- package/dist/cli/generate.mjs +4122 -292
- package/dist/cli/index.cjs +4117 -292
- package/dist/cli/index.mjs +4122 -292
- package/dist/cli/run.cjs +63 -1
- package/dist/cli/run.mjs +39 -1
- package/dist/src/cdn/chunk-MDJLC3PE.mjs +830 -0
- package/dist/src/cdn/core.cjs +1365 -1
- package/dist/src/cdn/core.d.ts +3 -3
- package/dist/src/cdn/core.js +3 -3
- package/dist/src/cdn/core.mjs +642 -1
- package/dist/src/cdn/router-JYFZP3EZ.mjs +70 -0
- package/dist/src/index.cjs +1280 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -11
- package/dist/src/index.mjs +1393 -1
- package/dist/src/jsx-dev-runtime.cjs +26 -1
- package/dist/src/jsx-dev-runtime.mjs +24 -1
- package/dist/src/jsx-runtime.cjs +26 -1
- package/dist/src/jsx-runtime.js +0 -9
- package/dist/src/jsx-runtime.mjs +24 -1
- package/dist/src/lib/client/chunk-7IUN2FLI.mjs +133 -0
- package/dist/src/lib/client/index.cjs +956 -1
- package/dist/src/lib/client/index.d.ts +17 -6
- package/dist/src/lib/client/index.d.ts.map +1 -1
- package/dist/src/lib/client/index.js +26 -19
- package/dist/src/lib/client/index.mjs +222 -1
- package/dist/src/lib/client/router-NOFEMOEH.mjs +631 -0
- package/dist/src/lib/context/context.cjs +118 -1
- package/dist/src/lib/context/context.d.ts +19 -37
- package/dist/src/lib/context/context.d.ts.map +1 -1
- package/dist/src/lib/context/context.js +8 -45
- package/dist/src/lib/context/context.mjs +99 -1
- package/dist/src/lib/context/index.cjs +120 -1
- package/dist/src/lib/context/index.mjs +99 -1
- package/dist/src/lib/control-flow.cjs +35 -1
- package/dist/src/lib/control-flow.mjs +25 -1
- package/dist/src/lib/core.cjs +1279 -1
- package/dist/src/lib/core.d.ts +0 -2
- package/dist/src/lib/core.d.ts.map +1 -1
- package/dist/src/lib/core.js +14 -48
- package/dist/src/lib/core.mjs +1393 -1
- package/dist/src/lib/env/index.cjs +60 -1
- package/dist/src/lib/env/index.d.ts +5 -5
- package/dist/src/lib/env/index.js +5 -5
- package/dist/src/lib/env/index.mjs +39 -1
- package/dist/src/lib/i18n/index.cjs +108 -1
- package/dist/src/lib/i18n/index.d.ts +31 -10
- package/dist/src/lib/i18n/index.d.ts.map +1 -1
- package/dist/src/lib/i18n/index.js +31 -10
- package/dist/src/lib/i18n/index.mjs +88 -1
- package/dist/src/lib/index.cjs +633 -1
- package/dist/src/lib/index.d.ts +0 -2
- package/dist/src/lib/index.d.ts.map +1 -1
- package/dist/src/lib/index.js +5 -11
- package/dist/src/lib/index.mjs +721 -1
- package/dist/src/lib/isomorphic.cjs +85 -1
- package/dist/src/lib/isomorphic.d.ts +41 -8
- package/dist/src/lib/isomorphic.d.ts.map +1 -1
- package/dist/src/lib/isomorphic.js +38 -5
- package/dist/src/lib/isomorphic.mjs +64 -1
- package/dist/src/lib/jsx-runtime/index.cjs +21 -1
- package/dist/src/lib/jsx-runtime/index.d.ts +1 -1
- package/dist/src/lib/jsx-runtime/index.js +1 -11
- package/dist/src/lib/jsx-runtime/index.mjs +4 -1
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +21 -1
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +4 -1
- package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +302 -8
- package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts +15 -1
- package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts.map +1 -1
- package/dist/src/lib/plugins/fluixi-routes-plugin.js +19 -5
- package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +271 -8
- package/dist/src/lib/plugins/index.cjs +311 -8
- package/dist/src/lib/plugins/index.mjs +279 -8
- package/dist/src/lib/plugins/route-codegen.cjs +73 -8
- package/dist/src/lib/plugins/route-codegen.d.ts +0 -1
- package/dist/src/lib/plugins/route-codegen.d.ts.map +1 -1
- package/dist/src/lib/plugins/route-codegen.js +4 -5
- package/dist/src/lib/plugins/route-codegen.mjs +52 -8
- package/dist/src/lib/plugins/route-scanner.cjs +189 -1
- package/dist/src/lib/plugins/route-scanner.d.ts +1 -2
- package/dist/src/lib/plugins/route-scanner.d.ts.map +1 -1
- package/dist/src/lib/plugins/route-scanner.js +7 -8
- package/dist/src/lib/plugins/route-scanner.mjs +158 -1
- package/dist/src/lib/plugins/vite.cjs +27 -1
- package/dist/src/lib/plugins/vite.mjs +6 -1
- package/dist/src/lib/render/await.cjs +38 -1
- package/dist/src/lib/render/await.d.ts +18 -0
- package/dist/src/lib/render/await.d.ts.map +1 -1
- package/dist/src/lib/render/await.js +20 -2
- package/dist/src/lib/render/await.mjs +17 -1
- package/dist/src/lib/render/component.cjs +221 -1
- package/dist/src/lib/render/component.d.ts +3 -44
- package/dist/src/lib/render/component.d.ts.map +1 -1
- package/dist/src/lib/render/component.js +23 -55
- package/dist/src/lib/render/component.mjs +214 -1
- package/dist/src/lib/render/deferred.cjs +175 -1
- package/dist/src/lib/render/deferred.js +5 -5
- package/dist/src/lib/render/deferred.mjs +158 -1
- package/dist/src/lib/render/index.cjs +573 -1
- package/dist/src/lib/render/index.d.ts +3 -17
- package/dist/src/lib/render/index.d.ts.map +1 -1
- package/dist/src/lib/render/index.js +8 -22
- package/dist/src/lib/render/index.mjs +590 -1
- package/dist/src/lib/render/lazy.cjs +67 -1
- package/dist/src/lib/render/lazy.d.ts +22 -1
- package/dist/src/lib/render/lazy.d.ts.map +1 -1
- package/dist/src/lib/render/lazy.js +26 -5
- package/dist/src/lib/render/lazy.mjs +50 -1
- package/dist/src/lib/render/suspense.cjs +141 -1
- package/dist/src/lib/render/suspense.d.ts +40 -0
- package/dist/src/lib/render/suspense.d.ts.map +1 -1
- package/dist/src/lib/render/suspense.js +46 -6
- package/dist/src/lib/render/suspense.mjs +136 -1
- package/dist/src/lib/render/versions.cjs +35 -1
- package/dist/src/lib/render/versions.js +6 -6
- package/dist/src/lib/render/versions.mjs +14 -1
- package/dist/src/lib/router/data.cjs +230 -1
- package/dist/src/lib/router/data.d.ts +1 -13
- package/dist/src/lib/router/data.d.ts.map +1 -1
- package/dist/src/lib/router/data.js +0 -12
- package/dist/src/lib/router/data.mjs +214 -1
- package/dist/src/lib/router/index.cjs +709 -1
- package/dist/src/lib/router/index.d.ts +78 -23
- package/dist/src/lib/router/index.d.ts.map +1 -1
- package/dist/src/lib/router/index.js +161 -37
- package/dist/src/lib/router/index.mjs +722 -1
- package/dist/src/lib/router/transition.cjs +75 -0
- package/dist/src/lib/router/transition.d.ts +81 -0
- package/dist/src/lib/router/transition.d.ts.map +1 -0
- package/dist/src/lib/router/transition.js +136 -0
- package/dist/src/lib/router/transition.mjs +54 -0
- package/dist/src/lib/server/hydration.cjs +169 -1
- package/dist/src/lib/server/hydration.d.ts +15 -55
- package/dist/src/lib/server/hydration.d.ts.map +1 -1
- package/dist/src/lib/server/hydration.js +23 -62
- package/dist/src/lib/server/hydration.mjs +148 -1
- package/dist/src/lib/server/index.cjs +401 -11
- package/dist/src/lib/server/index.d.ts +30 -6
- package/dist/src/lib/server/index.d.ts.map +1 -1
- package/dist/src/lib/server/index.js +34 -11
- package/dist/src/lib/server/index.mjs +379 -11
- package/dist/src/lib/server/reactive.cjs +197 -1
- package/dist/src/lib/server/reactive.d.ts +18 -77
- package/dist/src/lib/server/reactive.d.ts.map +1 -1
- package/dist/src/lib/server/reactive.js +19 -78
- package/dist/src/lib/server/reactive.mjs +176 -1
- package/dist/src/lib/theme/index.cjs +110 -1
- package/dist/src/lib/theme/index.d.ts +19 -4
- package/dist/src/lib/theme/index.d.ts.map +1 -1
- package/dist/src/lib/theme/index.js +17 -2
- package/dist/src/lib/theme/index.mjs +89 -1
- package/dist/src/lib/utils/index.cjs +209 -1
- package/dist/src/lib/utils/index.d.ts +0 -93
- package/dist/src/lib/utils/index.d.ts.map +1 -1
- package/dist/src/lib/utils/index.js +0 -93
- package/dist/src/lib/utils/index.mjs +188 -1
- package/dist/src/routes.cjs +37 -1
- package/dist/src/routes.d.ts +1 -1
- package/dist/src/routes.js +2 -2
- package/dist/src/routes.mjs +17 -1
- package/dist/src/server.cjs +400 -11
- package/dist/src/server.d.ts +0 -1
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +0 -1
- package/dist/src/server.mjs +377 -11
- package/dist/src/utils.cjs +21 -1
- package/dist/src/utils.d.ts +1 -1
- package/dist/src/utils.js +1 -1
- package/dist/src/utils.mjs +4 -1
- package/dist/src/version.generated.cjs +26 -1
- package/dist/src/version.generated.d.ts +1 -1
- package/dist/src/version.generated.js +2 -2
- package/dist/src/version.generated.mjs +5 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +17 -16
- package/src/index.ts +2 -14
- package/dist/src/cdn/chunk-55BT3H6E.mjs +0 -1
- package/dist/src/cdn/router-5JJZBK4E.mjs +0 -1
- package/dist/src/lib/client/chunk-SM4WHBOP.mjs +0 -1
- package/dist/src/lib/client/router-IBD4WPQT.mjs +0 -1
|
@@ -1 +1,67 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/render/lazy.ts
|
|
21
|
+
var lazy_exports = {};
|
|
22
|
+
__export(lazy_exports, {
|
|
23
|
+
lazy: () => lazy
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(lazy_exports);
|
|
26
|
+
var import_dom = require("@fluixi/dom");
|
|
27
|
+
var import_signal = require("@fluixi/reactive/signal");
|
|
28
|
+
function fromChunk(value) {
|
|
29
|
+
if (value != null && (typeof value === "object" || typeof value === "function")) {
|
|
30
|
+
try {
|
|
31
|
+
value["$lazy"] = true;
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
function lazy(loader, options) {
|
|
38
|
+
let resolved;
|
|
39
|
+
let promise;
|
|
40
|
+
let component;
|
|
41
|
+
function load() {
|
|
42
|
+
if (!promise) {
|
|
43
|
+
promise = loader().then((mod) => resolved = mod.default);
|
|
44
|
+
}
|
|
45
|
+
return promise;
|
|
46
|
+
}
|
|
47
|
+
if (options?.preload) load();
|
|
48
|
+
const LazyWrapper = ((props) => {
|
|
49
|
+
if (resolved) {
|
|
50
|
+
const C = resolved;
|
|
51
|
+
return fromChunk((0, import_signal.untrack)(() => C(props)));
|
|
52
|
+
}
|
|
53
|
+
if (!component) {
|
|
54
|
+
const [resource] = (0, import_signal.createResource)(() => load(), { transport: false });
|
|
55
|
+
component = resource;
|
|
56
|
+
if (resource.error) throw resource.error;
|
|
57
|
+
}
|
|
58
|
+
return (0, import_signal.createMemo)(() => {
|
|
59
|
+
const C = component();
|
|
60
|
+
return C ? fromChunk((0, import_dom.createComponent)(C, props)) : void 0;
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
LazyWrapper.preload = () => load().then(() => void 0);
|
|
64
|
+
LazyWrapper.$lazy = true;
|
|
65
|
+
LazyWrapper.loader = loader;
|
|
66
|
+
return LazyWrapper;
|
|
67
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 1. `comp` signal is shared across all instances of this lazy component (module-level).
|
|
6
6
|
* 2. `createResource` at the component level registers the load promise with the
|
|
7
7
|
* nearest Suspense boundary (trackPromise / resource.loading).
|
|
8
|
-
* 3. A pure `createMemo` reads `comp()
|
|
8
|
+
* 3. A pure `createMemo` reads `comp()`: undefined while loading (Suspense shows
|
|
9
9
|
* fallback), the rendered component once loaded.
|
|
10
10
|
* 4. `preload()` is exposed so routes / link hover handlers can start the download
|
|
11
11
|
* before the component is rendered.
|
|
@@ -20,6 +20,27 @@ export interface LazyOptions {
|
|
|
20
20
|
/** If true, start loading immediately when lazy() is called */
|
|
21
21
|
preload?: boolean;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Defer a component to its own chunk, loaded the first time it renders.
|
|
25
|
+
*
|
|
26
|
+
* `loader` is a dynamic import, and its `default` export is the component. The returned
|
|
27
|
+
* function stands in for it and carries `preload()`, which starts the import without
|
|
28
|
+
* rendering anything. File routing uses that: `startClient` preloads the matched route
|
|
29
|
+
* before hydrating.
|
|
30
|
+
*
|
|
31
|
+
* Preloading is what keeps hydration intact. If the module has already resolved by the
|
|
32
|
+
* time this renders, the component renders synchronously on the first pass, so it adopts
|
|
33
|
+
* the server's DOM in place. On a cold client navigation it goes through a resource
|
|
34
|
+
* instead, suspends, and a `Suspense` boundary above it shows the fallback.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const Settings = lazy(() => import('./Settings.js'));
|
|
39
|
+
*
|
|
40
|
+
* // somewhere that knows the route is next
|
|
41
|
+
* void Settings.preload();
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
23
44
|
export declare function lazy<T extends (...args: any[]) => any>(loader: () => Promise<{
|
|
24
45
|
default: T;
|
|
25
46
|
}>, options?: LazyOptions): T & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../../../../src/lib/render/lazy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AAUzE,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,cAAc,IAAI,MAAM,OAAO,CAAC;IAC5D,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,+DAA+D;IAC/D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAmBD,wBAAgB,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACpD,MAAM,EAAE,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC,EACrC,OAAO,CAAC,EAAE,WAAW,GACpB,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CA2DtC"}
|
|
1
|
+
{"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../../../../src/lib/render/lazy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AAUzE,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,cAAc,IAAI,MAAM,OAAO,CAAC;IAC5D,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,+DAA+D;IAC/D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAmBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACpD,MAAM,EAAE,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC,EACrC,OAAO,CAAC,EAAE,WAAW,GACpB,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CA2DtC"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 1. `comp` signal is shared across all instances of this lazy component (module-level).
|
|
6
6
|
* 2. `createResource` at the component level registers the load promise with the
|
|
7
7
|
* nearest Suspense boundary (trackPromise / resource.loading).
|
|
8
|
-
* 3. A pure `createMemo` reads `comp()
|
|
8
|
+
* 3. A pure `createMemo` reads `comp()`: undefined while loading (Suspense shows
|
|
9
9
|
* fallback), the rendered component once loaded.
|
|
10
10
|
* 4. `preload()` is exposed so routes / link hover handlers can start the download
|
|
11
11
|
* before the component is rendered.
|
|
@@ -28,10 +28,31 @@ function fromChunk(value) {
|
|
|
28
28
|
return value;
|
|
29
29
|
}
|
|
30
30
|
// ─── Implementation ──────────────────────────────────────────────────────────
|
|
31
|
+
/**
|
|
32
|
+
* Defer a component to its own chunk, loaded the first time it renders.
|
|
33
|
+
*
|
|
34
|
+
* `loader` is a dynamic import, and its `default` export is the component. The returned
|
|
35
|
+
* function stands in for it and carries `preload()`, which starts the import without
|
|
36
|
+
* rendering anything. File routing uses that: `startClient` preloads the matched route
|
|
37
|
+
* before hydrating.
|
|
38
|
+
*
|
|
39
|
+
* Preloading is what keeps hydration intact. If the module has already resolved by the
|
|
40
|
+
* time this renders, the component renders synchronously on the first pass, so it adopts
|
|
41
|
+
* the server's DOM in place. On a cold client navigation it goes through a resource
|
|
42
|
+
* instead, suspends, and a `Suspense` boundary above it shows the fallback.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const Settings = lazy(() => import('./Settings.js'));
|
|
47
|
+
*
|
|
48
|
+
* // somewhere that knows the route is next
|
|
49
|
+
* void Settings.preload();
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
31
52
|
export function lazy(loader, options) {
|
|
32
53
|
// The resolved component, set once the module loads. The whole hydration story turns on
|
|
33
|
-
// this: when it's already set when LazyWrapper renders
|
|
34
|
-
// before hydration (see startClient
|
|
54
|
+
// this: when it's already set when LazyWrapper renders, because the route was preloaded
|
|
55
|
+
// before hydration (see startClient -> preloadRoutes): we render it SYNCHRONOUSLY on the
|
|
35
56
|
// first pass. No resource, no Suspense fallback, no late re-render. So the lazy file-route
|
|
36
57
|
// adopts the server DOM in place instead of resolving a tick later and building a duplicate
|
|
37
58
|
// next to it. The async path below is only for a cold client navigation.
|
|
@@ -50,7 +71,7 @@ export function lazy(loader, options) {
|
|
|
50
71
|
load();
|
|
51
72
|
const LazyWrapper = ((props) => {
|
|
52
73
|
// Fast path: module already loaded (eager preload, or preloaded before hydration).
|
|
53
|
-
// Render straight through
|
|
74
|
+
// Render straight through: under hydration this consumes the cursor in place.
|
|
54
75
|
if (resolved) {
|
|
55
76
|
const C = resolved;
|
|
56
77
|
return fromChunk(untrack(() => C(props)));
|
|
@@ -59,7 +80,7 @@ export function lazy(loader, options) {
|
|
|
59
80
|
// fetched). Register the import with the nearest Suspense via createResource so the
|
|
60
81
|
// fallback shows while it downloads, then swap in the component once it resolves.
|
|
61
82
|
if (!component) {
|
|
62
|
-
// transport:false
|
|
83
|
+
// transport:false, the value here is the component function, which never
|
|
63
84
|
// travelled to the client, and the id it used to consume made the page's own
|
|
64
85
|
// resource ids depend on whether this module was already loaded in the
|
|
65
86
|
// process. The client re-imports the chunk itself either way.
|
|
@@ -1 +1,50 @@
|
|
|
1
|
-
|
|
1
|
+
// src/lib/render/lazy.ts
|
|
2
|
+
import { createComponent } from "@fluixi/dom";
|
|
3
|
+
import {
|
|
4
|
+
untrack,
|
|
5
|
+
createMemo,
|
|
6
|
+
createResource
|
|
7
|
+
} from "@fluixi/reactive/signal";
|
|
8
|
+
function fromChunk(value) {
|
|
9
|
+
if (value != null && (typeof value === "object" || typeof value === "function")) {
|
|
10
|
+
try {
|
|
11
|
+
value["$lazy"] = true;
|
|
12
|
+
} catch {
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function lazy(loader, options) {
|
|
18
|
+
let resolved;
|
|
19
|
+
let promise;
|
|
20
|
+
let component;
|
|
21
|
+
function load() {
|
|
22
|
+
if (!promise) {
|
|
23
|
+
promise = loader().then((mod) => resolved = mod.default);
|
|
24
|
+
}
|
|
25
|
+
return promise;
|
|
26
|
+
}
|
|
27
|
+
if (options?.preload) load();
|
|
28
|
+
const LazyWrapper = ((props) => {
|
|
29
|
+
if (resolved) {
|
|
30
|
+
const C = resolved;
|
|
31
|
+
return fromChunk(untrack(() => C(props)));
|
|
32
|
+
}
|
|
33
|
+
if (!component) {
|
|
34
|
+
const [resource] = createResource(() => load(), { transport: false });
|
|
35
|
+
component = resource;
|
|
36
|
+
if (resource.error) throw resource.error;
|
|
37
|
+
}
|
|
38
|
+
return createMemo(() => {
|
|
39
|
+
const C = component();
|
|
40
|
+
return C ? fromChunk(createComponent(C, props)) : void 0;
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
LazyWrapper.preload = () => load().then(() => void 0);
|
|
44
|
+
LazyWrapper.$lazy = true;
|
|
45
|
+
LazyWrapper.loader = loader;
|
|
46
|
+
return LazyWrapper;
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
lazy
|
|
50
|
+
};
|
|
@@ -1 +1,141 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/render/suspense.ts
|
|
21
|
+
var suspense_exports = {};
|
|
22
|
+
__export(suspense_exports, {
|
|
23
|
+
$SUSPENSE: () => $SUSPENSE,
|
|
24
|
+
Suspense: () => Suspense,
|
|
25
|
+
SuspenseList: () => SuspenseList,
|
|
26
|
+
isJSXElement: () => isJSXElement,
|
|
27
|
+
isLitResult: () => isLitResult,
|
|
28
|
+
isNode: () => isNode,
|
|
29
|
+
isPromise: () => isPromise
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(suspense_exports);
|
|
32
|
+
var import_signal = require("@fluixi/reactive/signal");
|
|
33
|
+
var import_dom = require("@fluixi/dom");
|
|
34
|
+
var $SUSPENSE = /* @__PURE__ */ Symbol.for("suspense");
|
|
35
|
+
function isNode(value) {
|
|
36
|
+
if (typeof window === "undefined") return false;
|
|
37
|
+
if (typeof Node === "function" && value instanceof Node) return true;
|
|
38
|
+
return !!(value && typeof value === "object" && typeof value.nodeType === "number");
|
|
39
|
+
}
|
|
40
|
+
function isLitResult(value) {
|
|
41
|
+
if (!value || typeof value !== "object") return false;
|
|
42
|
+
return !!(typeof value["_$litType$"] !== "undefined" || typeof value["_$litDirective$"] !== "undefined" || Array.isArray(value.strings) && Array.isArray(value.values));
|
|
43
|
+
}
|
|
44
|
+
function isPromise(value) {
|
|
45
|
+
return !!(value && typeof value === "object" && typeof value.then === "function" || value instanceof Promise);
|
|
46
|
+
}
|
|
47
|
+
function isJSXElement(value) {
|
|
48
|
+
return !!(value && typeof value === "object" && "type" in value && "props" in value);
|
|
49
|
+
}
|
|
50
|
+
var SuspenseListContext = (0, import_signal.createContext)(void 0, "suspense-list");
|
|
51
|
+
function Suspense(props) {
|
|
52
|
+
return (0, import_dom.createComponent)(SuspenseImpl, props);
|
|
53
|
+
}
|
|
54
|
+
function SuspenseImpl(props) {
|
|
55
|
+
const boundary = (0, import_signal.createSuspenseBoundary)();
|
|
56
|
+
const listCtx = (0, import_signal.useContext)(SuspenseListContext);
|
|
57
|
+
const listState = listCtx ? listCtx.register(boundary.inFallback) : void 0;
|
|
58
|
+
function instantiateOnce(val) {
|
|
59
|
+
if (val == null || typeof val === "boolean") return null;
|
|
60
|
+
if (typeof val === "function" && (0, import_dom.isComponent)(val) && !(0, import_signal.isSignal)(val)) return val();
|
|
61
|
+
return val;
|
|
62
|
+
}
|
|
63
|
+
const fallback = (0, import_dom.runWithoutHydration)(() => instantiateOnce(props.fallback ?? null));
|
|
64
|
+
const children = (0, import_signal.provideSuspense)(boundary, () => (0, import_signal.readChildren)(() => props.children));
|
|
65
|
+
const track = (value, depth) => {
|
|
66
|
+
if (depth > 8) return;
|
|
67
|
+
if (Array.isArray(value)) {
|
|
68
|
+
for (const x of value) track(x, depth + 1);
|
|
69
|
+
} else if (typeof value === "function" && (0, import_signal.isSignal)(value)) {
|
|
70
|
+
track(value(), depth + 1);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
(0, import_signal.provideSuspense)(boundary, () => {
|
|
74
|
+
(0, import_signal.createRenderEffect)(() => track(children, 0));
|
|
75
|
+
});
|
|
76
|
+
const [resolved, setResolved] = (0, import_signal.createSignal)(void 0);
|
|
77
|
+
const childIsPromise = isPromise(children);
|
|
78
|
+
if (childIsPromise) {
|
|
79
|
+
boundary.increment();
|
|
80
|
+
children.then(
|
|
81
|
+
(v) => {
|
|
82
|
+
setResolved(() => v);
|
|
83
|
+
boundary.decrement();
|
|
84
|
+
},
|
|
85
|
+
() => boundary.decrement()
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return (0, import_signal.createMemo)(() => {
|
|
89
|
+
const showFallback = listState ? listState.showFallback() : boundary.inFallback();
|
|
90
|
+
if (showFallback) return fallback;
|
|
91
|
+
if (childIsPromise) return resolved();
|
|
92
|
+
return children;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function SuspenseList(props) {
|
|
96
|
+
return (0, import_dom.createComponent)(SuspenseListImpl, props);
|
|
97
|
+
}
|
|
98
|
+
function SuspenseListImpl(props) {
|
|
99
|
+
const { revealOrder = "together", tail } = props;
|
|
100
|
+
const registrations = [];
|
|
101
|
+
function coordinateReveal() {
|
|
102
|
+
if (revealOrder === "together") {
|
|
103
|
+
const allResolved = registrations.every((r) => !r.inFallback());
|
|
104
|
+
registrations.forEach((r) => {
|
|
105
|
+
r.setShowContent(allResolved);
|
|
106
|
+
r.setShowFallback(!allResolved);
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
const items = revealOrder === "backwards" ? [...registrations].reverse() : registrations;
|
|
110
|
+
let blocked = false;
|
|
111
|
+
for (const r of items) {
|
|
112
|
+
if (blocked || r.inFallback()) {
|
|
113
|
+
blocked = true;
|
|
114
|
+
r.setShowContent(false);
|
|
115
|
+
r.setShowFallback(tail !== "hidden");
|
|
116
|
+
} else {
|
|
117
|
+
r.setShowContent(true);
|
|
118
|
+
r.setShowFallback(false);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const coordinator = {
|
|
124
|
+
register: (inFallback) => {
|
|
125
|
+
const [showContent, setShowContent] = (0, import_signal.createSignal)(true);
|
|
126
|
+
const [showFallback, setShowFallback] = (0, import_signal.createSignal)(false);
|
|
127
|
+
registrations.push({ inFallback, setShowContent, setShowFallback });
|
|
128
|
+
(0, import_signal.createEffect)(() => {
|
|
129
|
+
inFallback();
|
|
130
|
+
coordinateReveal();
|
|
131
|
+
});
|
|
132
|
+
return { showContent, showFallback };
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
return SuspenseListContext.Provider({
|
|
136
|
+
value: coordinator,
|
|
137
|
+
children: props.children
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
(0, import_dom.asSuspense)(Suspense);
|
|
141
|
+
Object.defineProperty(Suspense, "__suspense", { value: true, enumerable: true });
|
|
@@ -19,7 +19,47 @@ declare function isNode(value: any): value is Node;
|
|
|
19
19
|
declare function isLitResult(value: any): boolean;
|
|
20
20
|
declare function isPromise<T = any>(value: any): value is Promise<T>;
|
|
21
21
|
declare function isJSXElement(value: any): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Show `fallback` while anything below is still pending.
|
|
24
|
+
*
|
|
25
|
+
* A boundary is a counter on the owner tree. A read that suspends, which is a resource
|
|
26
|
+
* throwing its pending promise, bubbles to the effect that was reading, and the runtime
|
|
27
|
+
* hands it to the nearest boundary above. The promise settling decrements the counter,
|
|
28
|
+
* and the content replaces the fallback through ordinary reactivity.
|
|
29
|
+
*
|
|
30
|
+
* Nothing needs to be wired by hand. Anything that suspends reports here on its own,
|
|
31
|
+
* including a `lazy` component on a cold navigation and a `createResource` still loading.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* html`
|
|
36
|
+
* <${Suspense} fallback=${html`<p>loading...</p>`}>
|
|
37
|
+
* <${Profile} />
|
|
38
|
+
* </${Suspense}>`;
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
22
41
|
export declare function Suspense(props: SuspenseProps): any;
|
|
42
|
+
/**
|
|
43
|
+
* Order how a group of sibling `Suspense` boundaries reveal themselves.
|
|
44
|
+
*
|
|
45
|
+
* Without it, each boundary swaps its fallback for content the moment its own work
|
|
46
|
+
* finishes, so a list of independently-loading rows appears in whatever order the
|
|
47
|
+
* network happened to return. `revealOrder` imposes one instead.
|
|
48
|
+
*
|
|
49
|
+
* `forwards` and `backwards` hold a boundary in its fallback until the ones before it in
|
|
50
|
+
* that direction have resolved. `together`, the default, holds all of them until every
|
|
51
|
+
* one has. `tail` decides what the unrevealed ones show: `collapsed` keeps only the next
|
|
52
|
+
* fallback, `hidden` shows none.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* html`
|
|
57
|
+
* <${SuspenseList} revealOrder="forwards" tail="collapsed">
|
|
58
|
+
* <${Suspense} fallback=${skeleton}><${Row} id=${1} /></${Suspense}>
|
|
59
|
+
* <${Suspense} fallback=${skeleton}><${Row} id=${2} /></${Suspense}>
|
|
60
|
+
* </${SuspenseList}>`;
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
23
63
|
export declare function SuspenseList(props: SuspenseListProps): any;
|
|
24
64
|
export { isNode, isLitResult, isPromise, isJSXElement };
|
|
25
65
|
//# sourceMappingURL=suspense.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suspense.d.ts","sourceRoot":"","sources":["../../../../src/lib/render/suspense.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuBH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;IACpD,IAAI,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC;CAC/B;AAED,eAAO,MAAM,SAAS,eAAyB,CAAC;AAEhD,iBAAS,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,IAAI,CAIzC;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAOxC;AAED,iBAAS,SAAS,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,CAK3D;AAED,iBAAS,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAEzC;AAYD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,GAAG,CAElD;AA+ED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,GAAG,CAE1D;AAwDD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"suspense.d.ts","sourceRoot":"","sources":["../../../../src/lib/render/suspense.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuBH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;IACpD,IAAI,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC;CAC/B;AAED,eAAO,MAAM,SAAS,eAAyB,CAAC;AAEhD,iBAAS,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,IAAI,CAIzC;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAOxC;AAED,iBAAS,SAAS,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,CAK3D;AAED,iBAAS,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAEzC;AAYD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,GAAG,CAElD;AA+ED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,GAAG,CAE1D;AAwDD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -29,6 +29,25 @@ function isJSXElement(value) {
|
|
|
29
29
|
return !!(value && typeof value === 'object' && 'type' in value && 'props' in value);
|
|
30
30
|
}
|
|
31
31
|
const SuspenseListContext = createContext(undefined, 'suspense-list');
|
|
32
|
+
/**
|
|
33
|
+
* Show `fallback` while anything below is still pending.
|
|
34
|
+
*
|
|
35
|
+
* A boundary is a counter on the owner tree. A read that suspends, which is a resource
|
|
36
|
+
* throwing its pending promise, bubbles to the effect that was reading, and the runtime
|
|
37
|
+
* hands it to the nearest boundary above. The promise settling decrements the counter,
|
|
38
|
+
* and the content replaces the fallback through ordinary reactivity.
|
|
39
|
+
*
|
|
40
|
+
* Nothing needs to be wired by hand. Anything that suspends reports here on its own,
|
|
41
|
+
* including a `lazy` component on a cold navigation and a `createResource` still loading.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* html`
|
|
46
|
+
* <${Suspense} fallback=${html`<p>loading...</p>`}>
|
|
47
|
+
* <${Profile} />
|
|
48
|
+
* </${Suspense}>`;
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
32
51
|
export function Suspense(props) {
|
|
33
52
|
return createComponent(SuspenseImpl, props);
|
|
34
53
|
}
|
|
@@ -44,30 +63,30 @@ function SuspenseImpl(props) {
|
|
|
44
63
|
return val();
|
|
45
64
|
return val;
|
|
46
65
|
}
|
|
47
|
-
// Build the fallback off the hydration cursor
|
|
66
|
+
// Build the fallback off the hydration cursor, it isn't shown when the server
|
|
48
67
|
// already rendered content, so it must not adopt (and duplicate) server nodes.
|
|
49
68
|
const fallback = runWithoutHydration(() => instantiateOnce(props.fallback ?? null));
|
|
50
69
|
// Read the children under the boundary so the memos they create capture its owner;
|
|
51
|
-
// then a suspended resource read inside them throws to
|
|
70
|
+
// then a suspended resource read inside them throws to this boundary, not the parent.
|
|
52
71
|
//
|
|
53
72
|
// `readChildren`, not a bare `props.children` read: a component child arrives as a THUNK
|
|
54
73
|
// that instantiates when called, so reading the prop alone hands back a function and the
|
|
55
74
|
// component never runs here. It would then be instantiated later, downstream of the output
|
|
56
|
-
// memo, where a suspend has no boundary that can re-run it
|
|
75
|
+
// memo, where a suspend has no boundary that can re-run it, the failure that produced an
|
|
57
76
|
// empty node on a client navigation into a not-yet-loaded lazy route: no fallback, no
|
|
58
77
|
// content, no error. `readChildren` unwraps the thunks (so the lazy component runs under
|
|
59
78
|
// this boundary), stops at signals so dynamic children stay reactive, and memoises the
|
|
60
79
|
// result so the tracking effect below and the output render the same instantiation.
|
|
61
80
|
const children = provideSuspense(boundary, () => readChildren(() => props.children));
|
|
62
81
|
// Instantiate the children now, in their own effect under the boundary. A suspended
|
|
63
|
-
// resource read inside them throws here synchronously
|
|
64
|
-
// below
|
|
82
|
+
// resource read inside them throws here synchronously, a sibling of the output memo
|
|
83
|
+
// below, so the increment reschedules the output cleanly (matching the canary). If we
|
|
65
84
|
// only rendered children through the output's return, the resource read would be nested
|
|
66
85
|
// inside the output's own insert effect, which then never re-runs to show the fallback.
|
|
67
86
|
//
|
|
68
87
|
// Follow NESTED accessors, not just the outer one: the compiler wraps a dynamic child in
|
|
69
88
|
// its own memo, and `readChildren` stops at signals on purpose so they stay reactive. Stop
|
|
70
|
-
// at the outer read and a lazy component behind a condition is never instantiated here
|
|
89
|
+
// at the outer read and a lazy component behind a condition is never instantiated here
|
|
71
90
|
// it runs later, downstream, where its suspend reaches no boundary. Reading a memo is
|
|
72
91
|
// idempotent (it caches, and the output reads the same one), so following the chain costs
|
|
73
92
|
// nothing and cannot duplicate the subtree.
|
|
@@ -104,6 +123,27 @@ function SuspenseImpl(props) {
|
|
|
104
123
|
return children;
|
|
105
124
|
});
|
|
106
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Order how a group of sibling `Suspense` boundaries reveal themselves.
|
|
128
|
+
*
|
|
129
|
+
* Without it, each boundary swaps its fallback for content the moment its own work
|
|
130
|
+
* finishes, so a list of independently-loading rows appears in whatever order the
|
|
131
|
+
* network happened to return. `revealOrder` imposes one instead.
|
|
132
|
+
*
|
|
133
|
+
* `forwards` and `backwards` hold a boundary in its fallback until the ones before it in
|
|
134
|
+
* that direction have resolved. `together`, the default, holds all of them until every
|
|
135
|
+
* one has. `tail` decides what the unrevealed ones show: `collapsed` keeps only the next
|
|
136
|
+
* fallback, `hidden` shows none.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* html`
|
|
141
|
+
* <${SuspenseList} revealOrder="forwards" tail="collapsed">
|
|
142
|
+
* <${Suspense} fallback=${skeleton}><${Row} id=${1} /></${Suspense}>
|
|
143
|
+
* <${Suspense} fallback=${skeleton}><${Row} id=${2} /></${Suspense}>
|
|
144
|
+
* </${SuspenseList}>`;
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
107
147
|
export function SuspenseList(props) {
|
|
108
148
|
return createComponent(SuspenseListImpl, props);
|
|
109
149
|
}
|
|
@@ -1 +1,136 @@
|
|
|
1
|
-
|
|
1
|
+
// src/lib/render/suspense.ts
|
|
2
|
+
import {
|
|
3
|
+
createSignal,
|
|
4
|
+
createMemo,
|
|
5
|
+
createEffect,
|
|
6
|
+
createRenderEffect,
|
|
7
|
+
isSignal,
|
|
8
|
+
readChildren,
|
|
9
|
+
useContext,
|
|
10
|
+
createContext,
|
|
11
|
+
createSuspenseBoundary,
|
|
12
|
+
provideSuspense
|
|
13
|
+
} from "@fluixi/reactive/signal";
|
|
14
|
+
import {
|
|
15
|
+
asSuspense,
|
|
16
|
+
isComponent,
|
|
17
|
+
createComponent,
|
|
18
|
+
runWithoutHydration
|
|
19
|
+
} from "@fluixi/dom";
|
|
20
|
+
var $SUSPENSE = /* @__PURE__ */ Symbol.for("suspense");
|
|
21
|
+
function isNode(value) {
|
|
22
|
+
if (typeof window === "undefined") return false;
|
|
23
|
+
if (typeof Node === "function" && value instanceof Node) return true;
|
|
24
|
+
return !!(value && typeof value === "object" && typeof value.nodeType === "number");
|
|
25
|
+
}
|
|
26
|
+
function isLitResult(value) {
|
|
27
|
+
if (!value || typeof value !== "object") return false;
|
|
28
|
+
return !!(typeof value["_$litType$"] !== "undefined" || typeof value["_$litDirective$"] !== "undefined" || Array.isArray(value.strings) && Array.isArray(value.values));
|
|
29
|
+
}
|
|
30
|
+
function isPromise(value) {
|
|
31
|
+
return !!(value && typeof value === "object" && typeof value.then === "function" || value instanceof Promise);
|
|
32
|
+
}
|
|
33
|
+
function isJSXElement(value) {
|
|
34
|
+
return !!(value && typeof value === "object" && "type" in value && "props" in value);
|
|
35
|
+
}
|
|
36
|
+
var SuspenseListContext = createContext(void 0, "suspense-list");
|
|
37
|
+
function Suspense(props) {
|
|
38
|
+
return createComponent(SuspenseImpl, props);
|
|
39
|
+
}
|
|
40
|
+
function SuspenseImpl(props) {
|
|
41
|
+
const boundary = createSuspenseBoundary();
|
|
42
|
+
const listCtx = useContext(SuspenseListContext);
|
|
43
|
+
const listState = listCtx ? listCtx.register(boundary.inFallback) : void 0;
|
|
44
|
+
function instantiateOnce(val) {
|
|
45
|
+
if (val == null || typeof val === "boolean") return null;
|
|
46
|
+
if (typeof val === "function" && isComponent(val) && !isSignal(val)) return val();
|
|
47
|
+
return val;
|
|
48
|
+
}
|
|
49
|
+
const fallback = runWithoutHydration(() => instantiateOnce(props.fallback ?? null));
|
|
50
|
+
const children = provideSuspense(boundary, () => readChildren(() => props.children));
|
|
51
|
+
const track = (value, depth) => {
|
|
52
|
+
if (depth > 8) return;
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
for (const x of value) track(x, depth + 1);
|
|
55
|
+
} else if (typeof value === "function" && isSignal(value)) {
|
|
56
|
+
track(value(), depth + 1);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
provideSuspense(boundary, () => {
|
|
60
|
+
createRenderEffect(() => track(children, 0));
|
|
61
|
+
});
|
|
62
|
+
const [resolved, setResolved] = createSignal(void 0);
|
|
63
|
+
const childIsPromise = isPromise(children);
|
|
64
|
+
if (childIsPromise) {
|
|
65
|
+
boundary.increment();
|
|
66
|
+
children.then(
|
|
67
|
+
(v) => {
|
|
68
|
+
setResolved(() => v);
|
|
69
|
+
boundary.decrement();
|
|
70
|
+
},
|
|
71
|
+
() => boundary.decrement()
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return createMemo(() => {
|
|
75
|
+
const showFallback = listState ? listState.showFallback() : boundary.inFallback();
|
|
76
|
+
if (showFallback) return fallback;
|
|
77
|
+
if (childIsPromise) return resolved();
|
|
78
|
+
return children;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function SuspenseList(props) {
|
|
82
|
+
return createComponent(SuspenseListImpl, props);
|
|
83
|
+
}
|
|
84
|
+
function SuspenseListImpl(props) {
|
|
85
|
+
const { revealOrder = "together", tail } = props;
|
|
86
|
+
const registrations = [];
|
|
87
|
+
function coordinateReveal() {
|
|
88
|
+
if (revealOrder === "together") {
|
|
89
|
+
const allResolved = registrations.every((r) => !r.inFallback());
|
|
90
|
+
registrations.forEach((r) => {
|
|
91
|
+
r.setShowContent(allResolved);
|
|
92
|
+
r.setShowFallback(!allResolved);
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
const items = revealOrder === "backwards" ? [...registrations].reverse() : registrations;
|
|
96
|
+
let blocked = false;
|
|
97
|
+
for (const r of items) {
|
|
98
|
+
if (blocked || r.inFallback()) {
|
|
99
|
+
blocked = true;
|
|
100
|
+
r.setShowContent(false);
|
|
101
|
+
r.setShowFallback(tail !== "hidden");
|
|
102
|
+
} else {
|
|
103
|
+
r.setShowContent(true);
|
|
104
|
+
r.setShowFallback(false);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const coordinator = {
|
|
110
|
+
register: (inFallback) => {
|
|
111
|
+
const [showContent, setShowContent] = createSignal(true);
|
|
112
|
+
const [showFallback, setShowFallback] = createSignal(false);
|
|
113
|
+
registrations.push({ inFallback, setShowContent, setShowFallback });
|
|
114
|
+
createEffect(() => {
|
|
115
|
+
inFallback();
|
|
116
|
+
coordinateReveal();
|
|
117
|
+
});
|
|
118
|
+
return { showContent, showFallback };
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
return SuspenseListContext.Provider({
|
|
122
|
+
value: coordinator,
|
|
123
|
+
children: props.children
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
asSuspense(Suspense);
|
|
127
|
+
Object.defineProperty(Suspense, "__suspense", { value: true, enumerable: true });
|
|
128
|
+
export {
|
|
129
|
+
$SUSPENSE,
|
|
130
|
+
Suspense,
|
|
131
|
+
SuspenseList,
|
|
132
|
+
isJSXElement,
|
|
133
|
+
isLitResult,
|
|
134
|
+
isNode,
|
|
135
|
+
isPromise
|
|
136
|
+
};
|