@fluixi/core 1.0.0-alpha.53
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 +611 -0
- package/dist/cdn/core.global.js +3 -0
- package/dist/cli/generate.cjs +1814 -0
- package/dist/cli/generate.d.ts +7 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +2580 -0
- package/dist/cli/generate.mjs +1814 -0
- package/dist/cli/index.cjs +1814 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2580 -0
- package/dist/cli/index.mjs +1814 -0
- package/dist/cli/run.cjs +2 -0
- package/dist/cli/run.d.ts +3 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +62 -0
- package/dist/cli/run.mjs +2 -0
- package/dist/fluixi.d.ts +5 -0
- package/dist/src/cdn/chunk-PFM5O5UE.mjs +1 -0
- package/dist/src/cdn/core.cjs +1 -0
- package/dist/src/cdn/core.d.ts +16 -0
- package/dist/src/cdn/core.d.ts.map +1 -0
- package/dist/src/cdn/core.js +15 -0
- package/dist/src/cdn/core.mjs +1 -0
- package/dist/src/cdn/router-next-WNWCFVC2.mjs +1 -0
- package/dist/src/index.cjs +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +26 -0
- package/dist/src/index.mjs +1 -0
- package/dist/src/jsx-dev-runtime.cjs +1 -0
- package/dist/src/jsx-dev-runtime.d.ts +2 -0
- package/dist/src/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/src/jsx-dev-runtime.js +1 -0
- package/dist/src/jsx-dev-runtime.mjs +1 -0
- package/dist/src/jsx-runtime.cjs +1 -0
- package/dist/src/jsx-runtime.d.ts +7 -0
- package/dist/src/jsx-runtime.d.ts.map +1 -0
- package/dist/src/jsx-runtime.js +17 -0
- package/dist/src/jsx-runtime.mjs +1 -0
- package/dist/src/lib/client/chunk-D3VTWVNJ.mjs +1 -0
- package/dist/src/lib/client/index.cjs +1 -0
- package/dist/src/lib/client/index.d.ts +54 -0
- package/dist/src/lib/client/index.d.ts.map +1 -0
- package/dist/src/lib/client/index.js +142 -0
- package/dist/src/lib/client/index.mjs +1 -0
- package/dist/src/lib/client/router-next-UYIYMRJL.mjs +1 -0
- package/dist/src/lib/context/context.cjs +1 -0
- package/dist/src/lib/context/context.d.ts +337 -0
- package/dist/src/lib/context/context.d.ts.map +1 -0
- package/dist/src/lib/context/context.js +462 -0
- package/dist/src/lib/context/context.mjs +1 -0
- package/dist/src/lib/context/index.cjs +1 -0
- package/dist/src/lib/context/index.d.ts +3 -0
- package/dist/src/lib/context/index.d.ts.map +1 -0
- package/dist/src/lib/context/index.js +5 -0
- package/dist/src/lib/context/index.mjs +1 -0
- package/dist/src/lib/control-flow.cjs +1 -0
- package/dist/src/lib/control-flow.d.ts +4 -0
- package/dist/src/lib/control-flow.d.ts.map +1 -0
- package/dist/src/lib/control-flow.js +1 -0
- package/dist/src/lib/control-flow.mjs +1 -0
- package/dist/src/lib/core.cjs +1 -0
- package/dist/src/lib/core.d.ts +90 -0
- package/dist/src/lib/core.d.ts.map +1 -0
- package/dist/src/lib/core.js +181 -0
- package/dist/src/lib/core.mjs +1 -0
- package/dist/src/lib/directives/lit.cjs +1 -0
- package/dist/src/lib/directives/lit.d.ts +2 -0
- package/dist/src/lib/directives/lit.d.ts.map +1 -0
- package/dist/src/lib/directives/lit.js +31 -0
- package/dist/src/lib/directives/lit.mjs +0 -0
- package/dist/src/lib/env/index.cjs +1 -0
- package/dist/src/lib/env/index.d.ts +39 -0
- package/dist/src/lib/env/index.d.ts.map +1 -0
- package/dist/src/lib/env/index.js +50 -0
- package/dist/src/lib/env/index.mjs +1 -0
- package/dist/src/lib/i18n/index.cjs +1 -0
- package/dist/src/lib/i18n/index.d.ts +79 -0
- package/dist/src/lib/i18n/index.d.ts.map +1 -0
- package/dist/src/lib/i18n/index.js +127 -0
- package/dist/src/lib/i18n/index.mjs +1 -0
- package/dist/src/lib/index.cjs +1 -0
- package/dist/src/lib/index.d.ts +83 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +147 -0
- package/dist/src/lib/index.mjs +1 -0
- package/dist/src/lib/isomorphic.cjs +1 -0
- package/dist/src/lib/isomorphic.d.ts +69 -0
- package/dist/src/lib/isomorphic.d.ts.map +1 -0
- package/dist/src/lib/isomorphic.js +92 -0
- package/dist/src/lib/isomorphic.mjs +1 -0
- package/dist/src/lib/jsx-runtime/index.cjs +1 -0
- package/dist/src/lib/jsx-runtime/index.d.ts +13 -0
- package/dist/src/lib/jsx-runtime/index.d.ts.map +1 -0
- package/dist/src/lib/jsx-runtime/index.js +23 -0
- package/dist/src/lib/jsx-runtime/index.mjs +1 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +1 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts +2 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.js +1 -0
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +1 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +13 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts +21 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts.map +1 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.js +102 -0
- package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +13 -0
- package/dist/src/lib/plugins/index.cjs +13 -0
- package/dist/src/lib/plugins/index.d.ts +4 -0
- package/dist/src/lib/plugins/index.d.ts.map +1 -0
- package/dist/src/lib/plugins/index.js +3 -0
- package/dist/src/lib/plugins/index.mjs +13 -0
- package/dist/src/lib/plugins/route-codegen.cjs +8 -0
- package/dist/src/lib/plugins/route-codegen.d.ts +13 -0
- package/dist/src/lib/plugins/route-codegen.d.ts.map +1 -0
- package/dist/src/lib/plugins/route-codegen.js +81 -0
- package/dist/src/lib/plugins/route-codegen.mjs +8 -0
- package/dist/src/lib/plugins/route-scanner.cjs +1 -0
- package/dist/src/lib/plugins/route-scanner.d.ts +39 -0
- package/dist/src/lib/plugins/route-scanner.d.ts.map +1 -0
- package/dist/src/lib/plugins/route-scanner.js +216 -0
- package/dist/src/lib/plugins/route-scanner.mjs +1 -0
- package/dist/src/lib/plugins/vite.cjs +1 -0
- package/dist/src/lib/plugins/vite.d.ts +3 -0
- package/dist/src/lib/plugins/vite.d.ts.map +1 -0
- package/dist/src/lib/plugins/vite.js +1 -0
- package/dist/src/lib/plugins/vite.mjs +1 -0
- package/dist/src/lib/render/component.cjs +1 -0
- package/dist/src/lib/render/component.d.ts +328 -0
- package/dist/src/lib/render/component.d.ts.map +1 -0
- package/dist/src/lib/render/component.js +520 -0
- package/dist/src/lib/render/component.mjs +1 -0
- package/dist/src/lib/render/index.cjs +1 -0
- package/dist/src/lib/render/index.d.ts +113 -0
- package/dist/src/lib/render/index.d.ts.map +1 -0
- package/dist/src/lib/render/index.js +201 -0
- package/dist/src/lib/render/index.mjs +1 -0
- package/dist/src/lib/render/lazy.cjs +1 -0
- package/dist/src/lib/render/lazy.d.ts +28 -0
- package/dist/src/lib/render/lazy.d.ts.map +1 -0
- package/dist/src/lib/render/lazy.js +62 -0
- package/dist/src/lib/render/lazy.mjs +1 -0
- package/dist/src/lib/render/suspense.cjs +1 -0
- package/dist/src/lib/render/suspense.d.ts +25 -0
- package/dist/src/lib/render/suspense.d.ts.map +1 -0
- package/dist/src/lib/render/suspense.js +139 -0
- package/dist/src/lib/render/suspense.mjs +1 -0
- package/dist/src/lib/router-next/data.cjs +1 -0
- package/dist/src/lib/router-next/data.d.ts +101 -0
- package/dist/src/lib/router-next/data.d.ts.map +1 -0
- package/dist/src/lib/router-next/data.js +179 -0
- package/dist/src/lib/router-next/data.mjs +1 -0
- package/dist/src/lib/router-next/index.cjs +1 -0
- package/dist/src/lib/router-next/index.d.ts +156 -0
- package/dist/src/lib/router-next/index.d.ts.map +1 -0
- package/dist/src/lib/router-next/index.js +464 -0
- package/dist/src/lib/router-next/index.mjs +1 -0
- package/dist/src/lib/server/hydration.cjs +1 -0
- package/dist/src/lib/server/hydration.d.ts +238 -0
- package/dist/src/lib/server/hydration.d.ts.map +1 -0
- package/dist/src/lib/server/hydration.js +384 -0
- package/dist/src/lib/server/hydration.mjs +1 -0
- package/dist/src/lib/server/index.cjs +16 -0
- package/dist/src/lib/server/index.d.ts +91 -0
- package/dist/src/lib/server/index.d.ts.map +1 -0
- package/dist/src/lib/server/index.js +141 -0
- package/dist/src/lib/server/index.mjs +16 -0
- package/dist/src/lib/server/reactive.cjs +1 -0
- package/dist/src/lib/server/reactive.d.ts +298 -0
- package/dist/src/lib/server/reactive.d.ts.map +1 -0
- package/dist/src/lib/server/reactive.js +472 -0
- package/dist/src/lib/server/reactive.mjs +1 -0
- package/dist/src/lib/theme/index.cjs +1 -0
- package/dist/src/lib/theme/index.d.ts +51 -0
- package/dist/src/lib/theme/index.d.ts.map +1 -0
- package/dist/src/lib/theme/index.js +79 -0
- package/dist/src/lib/theme/index.mjs +1 -0
- package/dist/src/lib/utils/index.cjs +1 -0
- package/dist/src/lib/utils/index.d.ts +379 -0
- package/dist/src/lib/utils/index.d.ts.map +1 -0
- package/dist/src/lib/utils/index.js +527 -0
- package/dist/src/lib/utils/index.mjs +1 -0
- package/dist/src/rx.cjs +1 -0
- package/dist/src/rx.d.ts +2 -0
- package/dist/src/rx.d.ts.map +1 -0
- package/dist/src/rx.js +101 -0
- package/dist/src/rx.mjs +0 -0
- package/dist/src/server.cjs +16 -0
- package/dist/src/server.d.ts +64 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +63 -0
- package/dist/src/server.mjs +16 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +128 -0
- package/src/index.ts +31 -0
- package/types/fluixi.d.ts +5 -0
- package/types/vinxi.d.ts +50 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Client-Side Rendering for Fluixi
|
|
3
|
+
* @module @fluixi/core/render
|
|
4
|
+
*
|
|
5
|
+
* Client-side rendering:
|
|
6
|
+
* - Real DOM
|
|
7
|
+
* - Fine-grained mutation
|
|
8
|
+
*
|
|
9
|
+
* Following SolidJS structure: render logic separated from component creation
|
|
10
|
+
*/
|
|
11
|
+
import { createRoot } from '@fluixi/reactive/signal';
|
|
12
|
+
import { isDOMNode, delegateEvents, registerLitRender, insert, } from '@fluixi/dom';
|
|
13
|
+
import { ensureIntegration, } from './component.js';
|
|
14
|
+
// Register Lit's render function with the DOM runtime so reactive updates can render
|
|
15
|
+
// Lit templates. CLIENT-ONLY and lazily imported: lit extends HTMLElement at module
|
|
16
|
+
// init, so a static import would crash SSR — this keeps lit out of server bundles.
|
|
17
|
+
if (typeof window !== 'undefined') {
|
|
18
|
+
import('lit').then((lit) => registerLitRender(lit.render));
|
|
19
|
+
}
|
|
20
|
+
const isServer = typeof window === 'undefined';
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// SECTION 7 — RENDERING (CLIENT)
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Render a component into a DOM element
|
|
26
|
+
*
|
|
27
|
+
* Section 7 — CLIENT:
|
|
28
|
+
* - Real DOM (no Virtual DOM)
|
|
29
|
+
* - Fine-grained mutation (no reconciliation)
|
|
30
|
+
*
|
|
31
|
+
* Section 1: Component executes once during render
|
|
32
|
+
* Section 5: Creates reactive owner (root)
|
|
33
|
+
*
|
|
34
|
+
* @param {() => any} code - Component or function to render
|
|
35
|
+
* @param {HTMLElement} container - Target DOM element
|
|
36
|
+
* @param {RenderOptions} options - Render options
|
|
37
|
+
* @returns {() => void} Dispose function
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* import { render } from '@fluixi/core';
|
|
42
|
+
* import { createSignal } from '@fluixi/reactive';
|
|
43
|
+
* import { html } from 'lit';
|
|
44
|
+
*
|
|
45
|
+
* const App = () => {
|
|
46
|
+
* const [count, setCount] = createSignal(0);
|
|
47
|
+
* return html`
|
|
48
|
+
* <button @click=${() => setCount(count() + 1)}>
|
|
49
|
+
* Count: ${count()}
|
|
50
|
+
* </button>
|
|
51
|
+
* `;
|
|
52
|
+
* };
|
|
53
|
+
*
|
|
54
|
+
* const dispose = render(App, document.getElementById('app'));
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function render(code, container, options = {}) {
|
|
58
|
+
ensureIntegration('render');
|
|
59
|
+
delegateEvents(['click', 'input', 'change', 'submit']);
|
|
60
|
+
if (isServer) {
|
|
61
|
+
throw new Error('render() is for client-side only. Use renderToString() on server.');
|
|
62
|
+
}
|
|
63
|
+
if (!container) {
|
|
64
|
+
throw new Error('Container element is required');
|
|
65
|
+
}
|
|
66
|
+
let disposer;
|
|
67
|
+
// domRender(code, container)
|
|
68
|
+
createRoot((dispose) => {
|
|
69
|
+
disposer = dispose;
|
|
70
|
+
// Section 1: Execute code function once — Providers run here with Owner = root
|
|
71
|
+
// so "also stamp" in createProvider sets context on root.contexts, making
|
|
72
|
+
// it reachable from every render effect created by insert() below.
|
|
73
|
+
const result = typeof code === 'function' ? code() : code;
|
|
74
|
+
// Section 7: Insert into real DOM using the SAME root as code() so that
|
|
75
|
+
// render effects share the Owner chain with Provider context nodes.
|
|
76
|
+
// Using domRender() would create a second createRoot, severing the chain.
|
|
77
|
+
container.textContent = '';
|
|
78
|
+
container.setAttribute('fluixi-js', 'v1');
|
|
79
|
+
// Wrap the (already-computed) result in a STABLE thunk so insert() creates a
|
|
80
|
+
// top-level render effect. Without this, a static array result — e.g. the
|
|
81
|
+
// imperative backend's `[createMemo(() => createComponent(App)), " "]` — is
|
|
82
|
+
// flattened ONCE with no enclosing effect, so the nested memos are read
|
|
83
|
+
// untracked and reactive boundaries deep in the tree (Suspense fallback ->
|
|
84
|
+
// content, Outlet, Show/For) never update. The thunk returns the same result
|
|
85
|
+
// each run, so code()/Providers are not re-executed (context stays on root).
|
|
86
|
+
insert(container, () => result);
|
|
87
|
+
});
|
|
88
|
+
return () => {
|
|
89
|
+
if (disposer) {
|
|
90
|
+
disposer();
|
|
91
|
+
disposer = undefined;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Type guard for Lit template results
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
function isLitTemplateResult(value) {
|
|
100
|
+
return (value &&
|
|
101
|
+
typeof value === 'object' &&
|
|
102
|
+
(value['_$litType$'] !== undefined ||
|
|
103
|
+
value['_$litDirective$'] !== undefined));
|
|
104
|
+
}
|
|
105
|
+
// ============================================================================
|
|
106
|
+
// PORTAL RENDERING
|
|
107
|
+
// ============================================================================
|
|
108
|
+
/**
|
|
109
|
+
* Render into a different part of the DOM tree
|
|
110
|
+
*
|
|
111
|
+
* @param {() => any} code - Component to render
|
|
112
|
+
* @param {HTMLElement} target - Target element (outside current tree)
|
|
113
|
+
* @param {RenderOptions} options - Render options
|
|
114
|
+
* @returns {() => void} Dispose function
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* import { portal } from '@fluixi/core';
|
|
119
|
+
*
|
|
120
|
+
* const Modal = () => {
|
|
121
|
+
* return portal(
|
|
122
|
+
* () => html`<div class="modal">Content</div>`,
|
|
123
|
+
* document.body
|
|
124
|
+
* );
|
|
125
|
+
* };
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function portal(code, target, options = {}) {
|
|
129
|
+
const container = document.createElement('div');
|
|
130
|
+
container.setAttribute('data-portal', 'true');
|
|
131
|
+
target.appendChild(container);
|
|
132
|
+
const dispose = render(code, container, options);
|
|
133
|
+
return () => {
|
|
134
|
+
dispose();
|
|
135
|
+
if (target.contains(container)) {
|
|
136
|
+
target.removeChild(container);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// ============================================================================
|
|
141
|
+
// UTILITIES
|
|
142
|
+
// ============================================================================
|
|
143
|
+
/**
|
|
144
|
+
* Check if a value can be rendered
|
|
145
|
+
*
|
|
146
|
+
* @param {any} value - Value to check
|
|
147
|
+
* @returns {boolean} True if renderable
|
|
148
|
+
*/
|
|
149
|
+
export function isRenderable(value) {
|
|
150
|
+
if (value == null)
|
|
151
|
+
return true;
|
|
152
|
+
if (typeof value === 'function')
|
|
153
|
+
return true;
|
|
154
|
+
if (isDOMNode(value))
|
|
155
|
+
return true;
|
|
156
|
+
if (isLitTemplateResult(value))
|
|
157
|
+
return true;
|
|
158
|
+
if (Array.isArray(value))
|
|
159
|
+
return true;
|
|
160
|
+
if (typeof value === 'string' ||
|
|
161
|
+
typeof value === 'number' ||
|
|
162
|
+
typeof value === 'boolean') {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Initialize event delegation
|
|
169
|
+
* Should be called once when app starts
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* import { initDelegation } from '@fluixi/core';
|
|
174
|
+
*
|
|
175
|
+
* initDelegation();
|
|
176
|
+
* render(App, document.getElementById('app'));
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export function initDelegation() {
|
|
180
|
+
if (isServer)
|
|
181
|
+
return;
|
|
182
|
+
delegateEvents(['click', 'input', 'change', 'submit']);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Cleanup event delegation
|
|
186
|
+
* Should be called when app unmounts
|
|
187
|
+
*/
|
|
188
|
+
export function cleanupDelegation() {
|
|
189
|
+
if (isServer)
|
|
190
|
+
return;
|
|
191
|
+
// Clear delegated events manually if needed
|
|
192
|
+
}
|
|
193
|
+
// ============================================================================
|
|
194
|
+
// EXPORTS
|
|
195
|
+
// ============================================================================
|
|
196
|
+
// Re-export component creation
|
|
197
|
+
export { createComponent, onMount, onUnmount, useEffect, useRef, useReducer, useMemo, useEventListener, useCallback, onCleanup, batch, createMemo, createEffect, getOwner, runWithOwner, isDev, devOnly, DEV, getSharedConfig, $component, } from './component.js';
|
|
198
|
+
export { Suspense, SuspenseList,
|
|
199
|
+
// type ShowProps,
|
|
200
|
+
isNode, isLitResult, isPromise, isJSXElement, } from './suspense.js';
|
|
201
|
+
export { lazy } from "./lazy.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createRoot as be}from"@fluixi/reactive/signal";import{isDOMNode as he,delegateEvents as D,registerLitRender as xe,insert as Ce}from"@fluixi/dom";import{registerCreateComponent as $,asComponent as _}from"@fluixi/dom";import{untrack as w,onCleanup as f,getOwner as m,createEffect as l,createMemo as v,createSignal as z,isSignal as U,batch as N,runWithOwner as L}from"@fluixi/reactive/signal";var W={context:null,owner:null};function I(){return W}var b=typeof process<"u"&&!1,y=Symbol.for("fluixi-lifecycle");function g(){let e=m();if(!e)return null;if(!e[y]){let n={mounts:[],effects:[],scheduled:!1};e[y]=n}return e[y]}function E(e){if(e.scheduled)return;e.scheduled=!0;let n=m();queueMicrotask(()=>{L(n,()=>{for(let o of e.mounts)try{let t=o();typeof t=="function"&&f(t)}catch(t){console.error("Error in onMount callback:",t)}for(let o of e.effects){let t;l(()=>{t&&t(),t=o()}),f(()=>{t&&t()})}e.mounts=[],e.effects=[]})})}function T(e){$(O)}function O(e,n){return k(e,n)}function k(e,n){let o=typeof e=="function"?w(()=>e(n)):e;if(o!=null&&(typeof o=="function"||typeof o=="object")&&!U(o)){try{o.$name=e.name}catch{}return _(o)}return o}function V(e,n=!0){let o=g();if(o){if(!n){o.scheduled||e();return}o.mounts.push(e),E(o)}else{if(!n){e();return}l(()=>{let t=w(e);typeof t=="function"&&f(t)})}}function B(e){f(e)}function R(e){let n=g();if(n)n.effects.push(e),E(n);else{let o;l(()=>{o&&o(),o=e()}),f(()=>{o&&o()})}}function H(e){return v(e)}function K(e){let n={current:e};return f(()=>{n.current=null}),n}function q(e,n){let[o,t]=z(n);return[o,u=>{t(e(o(),u))}]}function J(e,n,o,t){R(()=>{let c=e&&"current"in e?e.current:e;if(c)return c.addEventListener(n,o,t),()=>{c.removeEventListener(n,o,t)}})}function X(e){return e}function Y(){return b}function G(e){b&&e()}import{createSignal as x,createMemo as Q,createEffect as Z,createRenderEffect as ee,isSignal as h,useContext as ne,createContext as te,createSuspenseBoundary as oe,provideSuspense as P}from"@fluixi/reactive/signal";import{asSuspense as re,isComponent as ie,createComponent as A,runWithoutHydration as se}from"@fluixi/dom";function ce(e){return typeof window>"u"?!1:typeof Node=="function"&&e instanceof Node?!0:!!(e&&typeof e=="object"&&typeof e.nodeType=="number")}function ae(e){return!e||typeof e!="object"?!1:!!(typeof e._$litType$<"u"||typeof e._$litDirective$<"u"||Array.isArray(e.strings)&&Array.isArray(e.values))}function j(e){return!!(e&&typeof e=="object"&&typeof e.then=="function"||e instanceof Promise)}function ue(e){return!!(e&&typeof e=="object"&&"type"in e&&"props"in e)}var F=te();function C(e){return A(fe,e)}function fe(e){let n=oe(),o=ne(F),t=o?o.register(n.inFallback):void 0;function c(i){return i==null||typeof i=="boolean"?null:typeof i=="function"&&ie(i)&&!h(i)?i():i}let u=se(()=>c(e.fallback??null)),s=P(n,()=>e.children);P(n,()=>{ee(()=>{let i=s;if(Array.isArray(i))for(let d of i)typeof d=="function"&&h(d)&&d();else typeof i=="function"&&h(i)&&i()})});let[a,r]=x(void 0),p=j(s);return p&&(n.increment(),s.then(i=>{r(()=>i),n.decrement()},()=>n.decrement())),Q(()=>(t?t.showFallback():n.inFallback())?u:p?a():s)}function pe(e){return A(le,e)}function le(e){let{revealOrder:n="together",tail:o}=e,t=[];function c(){if(n==="together"){let s=t.every(a=>!a.inFallback());t.forEach(a=>{a.setShowContent(s),a.setShowFallback(!s)})}else{let s=n==="backwards"?[...t].reverse():t,a=!1;for(let r of s)a||r.inFallback()?(a=!0,r.setShowContent(!1),r.setShowFallback(o!=="hidden")):(r.setShowContent(!0),r.setShowFallback(!1))}}let u={register:s=>{let[a,r]=x(!0),[p,i]=x(!1);return t.push({inFallback:s,setShowContent:r,setShowFallback:i}),Z(()=>{s(),c()}),{showContent:a,showFallback:p}}};return F.Provider({value:u,children:e.children})}re(C);Object.defineProperty(C,"__suspense",{value:!0,enumerable:!0});import{untrack as M,createMemo as de,createResource as ye}from"@fluixi/reactive/signal";function me(e,n){let o,t,c;function u(){return t||(t=e().then(a=>o=a.default)),t}n?.preload&&u();let s=(a=>{if(o){let r=o;return M(()=>r(a))}if(!c){let[r]=ye(()=>u());if(c=r,r.error)throw r.error}return de(()=>{let r=c();return r?M(()=>r(a)):void 0},void 0,{name:"lazy"})});return s.preload=()=>u().then(()=>{}),s.$lazy=!0,s.loader=e,s}typeof window<"u"&&import("lit").then(e=>xe(e.render));var S=typeof window>"u";function Se(e,n,o={}){if(T("render"),D(["click","input","change","submit"]),S)throw new Error("render() is for client-side only. Use renderToString() on server.");if(!n)throw new Error("Container element is required");let t;return be(c=>{t=c;let u=typeof e=="function"?e():e;n.textContent="",n.setAttribute("fluixi-js","v1"),Ce(n,()=>u)}),()=>{t&&(t(),t=void 0)}}function we(e){return e&&typeof e=="object"&&(e._$litType$!==void 0||e._$litDirective$!==void 0)}function Ve(e,n,o={}){let t=document.createElement("div");t.setAttribute("data-portal","true"),n.appendChild(t);let c=Se(e,t,o);return()=>{c(),n.contains(t)&&n.removeChild(t)}}function Be(e){return!!(e==null||typeof e=="function"||he(e)||we(e)||Array.isArray(e)||typeof e=="string"||typeof e=="number"||typeof e=="boolean")}function He(){S||D(["click","input","change","submit"])}function Ke(){}export{k as $component,b as DEV,C as Suspense,pe as SuspenseList,N as batch,Ke as cleanupDelegation,O as createComponent,l as createEffect,v as createMemo,G as devOnly,m as getOwner,I as getSharedConfig,He as initDelegation,Y as isDev,ue as isJSXElement,ae as isLitResult,ce as isNode,j as isPromise,Be as isRenderable,me as lazy,f as onCleanup,V as onMount,B as onUnmount,Ve as portal,Se as render,L as runWithOwner,X as useCallback,R as useEffect,J as useEventListener,H as useMemo,q as useReducer,K as useRef};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var m=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var c=(n,e)=>{for(var r in e)m(n,r,{get:e[r],enumerable:!0})},y=(n,e,r,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of f(e))!l.call(n,o)&&o!==r&&m(n,o,{get:()=>e[o],enumerable:!(i=u(e,o))||i.enumerable});return n};var C=n=>y(m({},"__esModule",{value:!0}),n);var z={};c(z,{lazy:()=>P});module.exports=C(z);var a=require("@fluixi/reactive/signal");function P(n,e){let r,i,o;function p(){return i||(i=n().then(s=>r=s.default)),i}e?.preload&&p();let d=(s=>{if(r){let t=r;return(0,a.untrack)(()=>t(s))}if(!o){let[t]=(0,a.createResource)(()=>p());if(o=t,t.error)throw t.error}return(0,a.createMemo)(()=>{let t=o();return t?(0,a.untrack)(()=>t(s)):void 0},void 0,{name:"lazy"})});return d.preload=()=>p().then(()=>{}),d.$lazy=!0,d.loader=n,d}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy-load a component module.
|
|
3
|
+
*
|
|
4
|
+
* SolidJS-aligned pattern:
|
|
5
|
+
* 1. `comp` signal is shared across all instances of this lazy component (module-level).
|
|
6
|
+
* 2. `createResource` at the component level registers the load promise with the
|
|
7
|
+
* nearest Suspense boundary (trackPromise / resource.loading).
|
|
8
|
+
* 3. A pure `createMemo` reads `comp()` — undefined while loading (Suspense shows
|
|
9
|
+
* fallback), the rendered component once loaded.
|
|
10
|
+
* 4. `preload()` is exposed so routes / link hover handlers can start the download
|
|
11
|
+
* before the component is rendered.
|
|
12
|
+
*/
|
|
13
|
+
import { Component, ComponentProps } from '@fluixi/dom';
|
|
14
|
+
export type LazyComponent<P = ComponentProps> = () => Promise<{
|
|
15
|
+
default: Component<P>;
|
|
16
|
+
}>;
|
|
17
|
+
export interface LazyOptions {
|
|
18
|
+
/** Show this while the module is loading */
|
|
19
|
+
fallback?: any;
|
|
20
|
+
/** If true, start loading immediately when lazy() is called */
|
|
21
|
+
preload?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function lazy<T extends (...args: any[]) => any>(loader: () => Promise<{
|
|
24
|
+
default: T;
|
|
25
|
+
}>, options?: LazyOptions): T & {
|
|
26
|
+
preload: () => Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=lazy.d.ts.map
|
|
@@ -0,0 +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,EAAE,MAAM,aAAa,CAAC;AAUxD,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;AAID,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,CAqDtC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy-load a component module.
|
|
3
|
+
*
|
|
4
|
+
* SolidJS-aligned pattern:
|
|
5
|
+
* 1. `comp` signal is shared across all instances of this lazy component (module-level).
|
|
6
|
+
* 2. `createResource` at the component level registers the load promise with the
|
|
7
|
+
* nearest Suspense boundary (trackPromise / resource.loading).
|
|
8
|
+
* 3. A pure `createMemo` reads `comp()` — undefined while loading (Suspense shows
|
|
9
|
+
* fallback), the rendered component once loaded.
|
|
10
|
+
* 4. `preload()` is exposed so routes / link hover handlers can start the download
|
|
11
|
+
* before the component is rendered.
|
|
12
|
+
*/
|
|
13
|
+
import { untrack, createMemo, createResource, } from '@fluixi/reactive/signal';
|
|
14
|
+
// ─── Implementation ──────────────────────────────────────────────────────────
|
|
15
|
+
export function lazy(loader, options) {
|
|
16
|
+
// The resolved component, set once the module loads. The whole hydration story turns on
|
|
17
|
+
// this: when it's already set when LazyWrapper renders — because the route was preloaded
|
|
18
|
+
// before hydration (see startClient → preloadRoutes) — we render it SYNCHRONOUSLY on the
|
|
19
|
+
// first pass. No resource, no Suspense fallback, no late re-render. So the lazy file-route
|
|
20
|
+
// adopts the server DOM in place instead of resolving a tick later and building a duplicate
|
|
21
|
+
// next to it. The async path below is only for a cold client navigation.
|
|
22
|
+
let resolved;
|
|
23
|
+
// Single in-flight / completed import. Idempotent: repeat calls reuse the same promise.
|
|
24
|
+
let promise;
|
|
25
|
+
// Suspense-tracked resource, created lazily on the cold (not-preloaded) path only.
|
|
26
|
+
let component;
|
|
27
|
+
function load() {
|
|
28
|
+
if (!promise) {
|
|
29
|
+
promise = loader().then((mod) => (resolved = mod.default));
|
|
30
|
+
}
|
|
31
|
+
return promise;
|
|
32
|
+
}
|
|
33
|
+
if (options?.preload)
|
|
34
|
+
load();
|
|
35
|
+
const LazyWrapper = ((props) => {
|
|
36
|
+
// Fast path: module already loaded (eager preload, or preloaded before hydration).
|
|
37
|
+
// Render straight through — under hydration this consumes the cursor in place.
|
|
38
|
+
if (resolved) {
|
|
39
|
+
const C = resolved;
|
|
40
|
+
return untrack(() => C(props));
|
|
41
|
+
}
|
|
42
|
+
// Cold path: not loaded yet (client-side navigation to a route whose chunk hasn't been
|
|
43
|
+
// fetched). Register the import with the nearest Suspense via createResource so the
|
|
44
|
+
// fallback shows while it downloads, then swap in the component once it resolves.
|
|
45
|
+
if (!component) {
|
|
46
|
+
const [resource] = createResource(() => load());
|
|
47
|
+
component = resource;
|
|
48
|
+
if (resource.error)
|
|
49
|
+
throw resource.error;
|
|
50
|
+
}
|
|
51
|
+
return createMemo(() => {
|
|
52
|
+
const C = component();
|
|
53
|
+
return C ? untrack(() => C(props)) : undefined;
|
|
54
|
+
}, undefined, { name: 'lazy' });
|
|
55
|
+
});
|
|
56
|
+
// Callers (link hover, router prefetch, the hydration preload pass) start the download
|
|
57
|
+
// before the component enters the view: <a onMouseEnter={() => LazyFoo.preload()}>.
|
|
58
|
+
LazyWrapper.preload = () => load().then(() => undefined);
|
|
59
|
+
LazyWrapper.$lazy = true;
|
|
60
|
+
LazyWrapper.loader = loader;
|
|
61
|
+
return LazyWrapper;
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{untrack as s,createMemo as m,createResource as u}from"@fluixi/reactive/signal";function c(d,p){let r,t,a;function i(){return t||(t=d().then(o=>r=o.default)),t}p?.preload&&i();let n=(o=>{if(r){let e=r;return s(()=>e(o))}if(!a){let[e]=u(()=>i());if(a=e,e.error)throw e.error}return m(()=>{let e=a();return e?s(()=>e(o)):void 0},void 0,{name:"lazy"})});return n.preload=()=>i().then(()=>{}),n.$lazy=!0,n.loader=d,n}export{c as lazy};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var y=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var C=(e,t)=>{for(var a in t)y(e,a,{get:t[a],enumerable:!0})},F=(e,t,a,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of w(t))!k.call(e,c)&&c!==a&&y(e,c,{get:()=>t[c],enumerable:!(r=m(t,c))||r.enumerable});return e};var P=e=>F(y({},"__esModule",{value:!0}),e);var N={};C(N,{$SUSPENSE:()=>g,Suspense:()=>b,SuspenseList:()=>E,isJSXElement:()=>x,isLitResult:()=>L,isNode:()=>A,isPromise:()=>S});module.exports=P(N);var n=require("@fluixi/reactive/signal"),f=require("@fluixi/dom"),g=Symbol.for("suspense");function A(e){return typeof window>"u"?!1:typeof Node=="function"&&e instanceof Node?!0:!!(e&&typeof e=="object"&&typeof e.nodeType=="number")}function L(e){return!e||typeof e!="object"?!1:!!(typeof e._$litType$<"u"||typeof e._$litDirective$<"u"||Array.isArray(e.strings)&&Array.isArray(e.values))}function S(e){return!!(e&&typeof e=="object"&&typeof e.then=="function"||e instanceof Promise)}function x(e){return!!(e&&typeof e=="object"&&"type"in e&&"props"in e)}var h=(0,n.createContext)();function b(e){return(0,f.createComponent)(R,e)}function R(e){let t=(0,n.createSuspenseBoundary)(),a=(0,n.useContext)(h),r=a?a.register(t.inFallback):void 0;function c(o){return o==null||typeof o=="boolean"?null:typeof o=="function"&&(0,f.isComponent)(o)&&!(0,n.isSignal)(o)?o():o}let p=(0,f.runWithoutHydration)(()=>c(e.fallback??null)),s=(0,n.provideSuspense)(t,()=>e.children);(0,n.provideSuspense)(t,()=>{(0,n.createRenderEffect)(()=>{let o=s;if(Array.isArray(o))for(let d of o)typeof d=="function"&&(0,n.isSignal)(d)&&d();else typeof o=="function"&&(0,n.isSignal)(o)&&o()})});let[i,l]=(0,n.createSignal)(void 0),u=S(s);return u&&(t.increment(),s.then(o=>{l(()=>o),t.decrement()},()=>t.decrement())),(0,n.createMemo)(()=>(r?r.showFallback():t.inFallback())?p:u?i():s)}function E(e){return(0,f.createComponent)(j,e)}function j(e){let{revealOrder:t="together",tail:a}=e,r=[];function c(){if(t==="together"){let s=r.every(i=>!i.inFallback());r.forEach(i=>{i.setShowContent(s),i.setShowFallback(!s)})}else{let s=t==="backwards"?[...r].reverse():r,i=!1;for(let l of s)i||l.inFallback()?(i=!0,l.setShowContent(!1),l.setShowFallback(a!=="hidden")):(l.setShowContent(!0),l.setShowFallback(!1))}}let p={register:s=>{let[i,l]=(0,n.createSignal)(!0),[u,o]=(0,n.createSignal)(!1);return r.push({inFallback:s,setShowContent:l,setShowFallback:o}),(0,n.createEffect)(()=>{s(),c()}),{showContent:i,showFallback:u}}};return h.Provider({value:p,children:e.children})}(0,f.asSuspense)(b);Object.defineProperty(b,"__suspense",{value:!0,enumerable:!0});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suspense on the TC39 boundary. A createResource read throws its promise while
|
|
3
|
+
* pending; the effect running it reports that to the nearest boundary (stamped as
|
|
4
|
+
* SuspenseContext on this component's owner), which flips inFallback. The output
|
|
5
|
+
* memo swaps between fallback and children on that signal.
|
|
6
|
+
*/
|
|
7
|
+
export interface SuspenseProps {
|
|
8
|
+
fallback?: any;
|
|
9
|
+
children?: any;
|
|
10
|
+
id?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SuspenseListProps {
|
|
13
|
+
children: any;
|
|
14
|
+
revealOrder?: 'forwards' | 'backwards' | 'together';
|
|
15
|
+
tail?: 'collapsed' | 'hidden';
|
|
16
|
+
}
|
|
17
|
+
export declare const $SUSPENSE: unique symbol;
|
|
18
|
+
declare function isNode(value: any): value is Node;
|
|
19
|
+
declare function isLitResult(value: any): boolean;
|
|
20
|
+
declare function isPromise<T = any>(value: any): value is Promise<T>;
|
|
21
|
+
declare function isJSXElement(value: any): boolean;
|
|
22
|
+
export declare function Suspense(props: SuspenseProps): any;
|
|
23
|
+
export declare function SuspenseList(props: SuspenseListProps): any;
|
|
24
|
+
export { isNode, isLitResult, isPromise, isJSXElement };
|
|
25
|
+
//# sourceMappingURL=suspense.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suspense.d.ts","sourceRoot":"","sources":["../../../../src/lib/render/suspense.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsBH,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;AA6DD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,GAAG,CAE1D;AAwDD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suspense on the TC39 boundary. A createResource read throws its promise while
|
|
3
|
+
* pending; the effect running it reports that to the nearest boundary (stamped as
|
|
4
|
+
* SuspenseContext on this component's owner), which flips inFallback. The output
|
|
5
|
+
* memo swaps between fallback and children on that signal.
|
|
6
|
+
*/
|
|
7
|
+
import { createSignal, createMemo, createEffect, createRenderEffect, isSignal, useContext, createContext, createSuspenseBoundary, provideSuspense, } from '@fluixi/reactive/signal';
|
|
8
|
+
import { asSuspense, isComponent, createComponent, runWithoutHydration, } from '@fluixi/dom';
|
|
9
|
+
export const $SUSPENSE = Symbol.for('suspense');
|
|
10
|
+
function isNode(value) {
|
|
11
|
+
if (typeof window === 'undefined')
|
|
12
|
+
return false;
|
|
13
|
+
if (typeof Node === 'function' && value instanceof Node)
|
|
14
|
+
return true;
|
|
15
|
+
return !!(value && typeof value === 'object' && typeof value.nodeType === 'number');
|
|
16
|
+
}
|
|
17
|
+
function isLitResult(value) {
|
|
18
|
+
if (!value || typeof value !== 'object')
|
|
19
|
+
return false;
|
|
20
|
+
return !!(typeof value['_$litType$'] !== 'undefined' ||
|
|
21
|
+
typeof value['_$litDirective$'] !== 'undefined' ||
|
|
22
|
+
(Array.isArray(value.strings) && Array.isArray(value.values)));
|
|
23
|
+
}
|
|
24
|
+
function isPromise(value) {
|
|
25
|
+
return !!((value && typeof value === 'object' && typeof value.then === 'function') ||
|
|
26
|
+
value instanceof Promise);
|
|
27
|
+
}
|
|
28
|
+
function isJSXElement(value) {
|
|
29
|
+
return !!(value && typeof value === 'object' && 'type' in value && 'props' in value);
|
|
30
|
+
}
|
|
31
|
+
const SuspenseListContext = createContext();
|
|
32
|
+
export function Suspense(props) {
|
|
33
|
+
return createComponent(SuspenseImpl, props);
|
|
34
|
+
}
|
|
35
|
+
function SuspenseImpl(props) {
|
|
36
|
+
const boundary = createSuspenseBoundary();
|
|
37
|
+
// SuspenseList coordination, if any.
|
|
38
|
+
const listCtx = useContext(SuspenseListContext);
|
|
39
|
+
const listState = listCtx ? listCtx.register(boundary.inFallback) : undefined;
|
|
40
|
+
function instantiateOnce(val) {
|
|
41
|
+
if (val == null || typeof val === 'boolean')
|
|
42
|
+
return null;
|
|
43
|
+
if (typeof val === 'function' && isComponent(val) && !isSignal(val))
|
|
44
|
+
return val();
|
|
45
|
+
return val;
|
|
46
|
+
}
|
|
47
|
+
// Build the fallback off the hydration cursor — it isn't shown when the server
|
|
48
|
+
// already rendered content, so it must not adopt (and duplicate) server nodes.
|
|
49
|
+
const fallback = runWithoutHydration(() => instantiateOnce(props.fallback ?? null));
|
|
50
|
+
// Read the children under the boundary so the memos they create capture its owner;
|
|
51
|
+
// then a suspended resource read inside them throws to THIS boundary, not the parent.
|
|
52
|
+
const children = provideSuspense(boundary, () => props.children);
|
|
53
|
+
// Instantiate the children now, in their own effect under the boundary. A suspended
|
|
54
|
+
// resource read inside them throws here synchronously — a sibling of the output memo
|
|
55
|
+
// below — so the increment reschedules the output cleanly (matching the canary). If we
|
|
56
|
+
// only rendered children through the output's return, the resource read would be nested
|
|
57
|
+
// inside the output's own insert effect, which then never re-runs to show the fallback.
|
|
58
|
+
provideSuspense(boundary, () => {
|
|
59
|
+
createRenderEffect(() => {
|
|
60
|
+
const c = children;
|
|
61
|
+
if (Array.isArray(c)) {
|
|
62
|
+
for (const x of c)
|
|
63
|
+
if (typeof x === 'function' && isSignal(x))
|
|
64
|
+
x();
|
|
65
|
+
}
|
|
66
|
+
else if (typeof c === 'function' && isSignal(c)) {
|
|
67
|
+
c();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
// A raw promise child: track it directly on the boundary.
|
|
72
|
+
const [resolved, setResolved] = createSignal(undefined);
|
|
73
|
+
const childIsPromise = isPromise(children);
|
|
74
|
+
if (childIsPromise) {
|
|
75
|
+
boundary.increment();
|
|
76
|
+
children.then((v) => {
|
|
77
|
+
setResolved(() => v);
|
|
78
|
+
boundary.decrement();
|
|
79
|
+
}, () => boundary.decrement());
|
|
80
|
+
}
|
|
81
|
+
return createMemo(() => {
|
|
82
|
+
const showFallback = listState ? listState.showFallback() : boundary.inFallback();
|
|
83
|
+
if (showFallback)
|
|
84
|
+
return fallback;
|
|
85
|
+
if (childIsPromise)
|
|
86
|
+
return resolved();
|
|
87
|
+
return children;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
export function SuspenseList(props) {
|
|
91
|
+
return createComponent(SuspenseListImpl, props);
|
|
92
|
+
}
|
|
93
|
+
function SuspenseListImpl(props) {
|
|
94
|
+
const { revealOrder = 'together', tail } = props;
|
|
95
|
+
const registrations = [];
|
|
96
|
+
function coordinateReveal() {
|
|
97
|
+
if (revealOrder === 'together') {
|
|
98
|
+
const allResolved = registrations.every((r) => !r.inFallback());
|
|
99
|
+
registrations.forEach((r) => {
|
|
100
|
+
r.setShowContent(allResolved);
|
|
101
|
+
r.setShowFallback(!allResolved);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const items = revealOrder === 'backwards' ? [...registrations].reverse() : registrations;
|
|
106
|
+
let blocked = false;
|
|
107
|
+
for (const r of items) {
|
|
108
|
+
if (blocked || r.inFallback()) {
|
|
109
|
+
blocked = true;
|
|
110
|
+
r.setShowContent(false);
|
|
111
|
+
r.setShowFallback(tail !== 'hidden');
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
r.setShowContent(true);
|
|
115
|
+
r.setShowFallback(false);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const coordinator = {
|
|
121
|
+
register: (inFallback) => {
|
|
122
|
+
const [showContent, setShowContent] = createSignal(true);
|
|
123
|
+
const [showFallback, setShowFallback] = createSignal(false);
|
|
124
|
+
registrations.push({ inFallback, setShowContent, setShowFallback });
|
|
125
|
+
createEffect(() => {
|
|
126
|
+
inFallback();
|
|
127
|
+
coordinateReveal();
|
|
128
|
+
});
|
|
129
|
+
return { showContent, showFallback };
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
return SuspenseListContext.Provider({
|
|
133
|
+
value: coordinator,
|
|
134
|
+
children: props.children,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
asSuspense(Suspense);
|
|
138
|
+
Object.defineProperty(Suspense, '__suspense', { value: true, enumerable: true });
|
|
139
|
+
export { isNode, isLitResult, isPromise, isJSXElement };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createSignal as d,createMemo as m,createEffect as w,createRenderEffect as k,isSignal as p,useContext as C,createContext as F,createSuspenseBoundary as P,provideSuspense as y}from"@fluixi/reactive/signal";import{asSuspense as g,isComponent as A,createComponent as b,runWithoutHydration as L}from"@fluixi/dom";var $=Symbol.for("suspense");function O(e){return typeof window>"u"?!1:typeof Node=="function"&&e instanceof Node?!0:!!(e&&typeof e=="object"&&typeof e.nodeType=="number")}function T(e){return!e||typeof e!="object"?!1:!!(typeof e._$litType$<"u"||typeof e._$litDirective$<"u"||Array.isArray(e.strings)&&Array.isArray(e.values))}function x(e){return!!(e&&typeof e=="object"&&typeof e.then=="function"||e instanceof Promise)}function _(e){return!!(e&&typeof e=="object"&&"type"in e&&"props"in e)}var S=F();function h(e){return b(R,e)}function R(e){let s=P(),a=C(S),i=a?a.register(s.inFallback):void 0;function l(n){return n==null||typeof n=="boolean"?null:typeof n=="function"&&A(n)&&!p(n)?n():n}let f=L(()=>l(e.fallback??null)),t=y(s,()=>e.children);y(s,()=>{k(()=>{let n=t;if(Array.isArray(n))for(let u of n)typeof u=="function"&&p(u)&&u();else typeof n=="function"&&p(n)&&n()})});let[o,r]=d(void 0),c=x(t);return c&&(s.increment(),t.then(n=>{r(()=>n),s.decrement()},()=>s.decrement())),m(()=>(i?i.showFallback():s.inFallback())?f:c?o():t)}function B(e){return b(E,e)}function E(e){let{revealOrder:s="together",tail:a}=e,i=[];function l(){if(s==="together"){let t=i.every(o=>!o.inFallback());i.forEach(o=>{o.setShowContent(t),o.setShowFallback(!t)})}else{let t=s==="backwards"?[...i].reverse():i,o=!1;for(let r of t)o||r.inFallback()?(o=!0,r.setShowContent(!1),r.setShowFallback(a!=="hidden")):(r.setShowContent(!0),r.setShowFallback(!1))}}let f={register:t=>{let[o,r]=d(!0),[c,n]=d(!1);return i.push({inFallback:t,setShowContent:r,setShowFallback:n}),w(()=>{t(),l()}),{showContent:o,showFallback:c}}};return S.Provider({value:f,children:e.children})}g(h);Object.defineProperty(h,"__suspense",{value:!0,enumerable:!0});export{$ as $SUSPENSE,h as Suspense,B as SuspenseList,_ as isJSXElement,T as isLitResult,O as isNode,x as isPromise};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var x=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var _=(e,t)=>{for(var n in t)x(e,n,{get:t[n],enumerable:!0})},k=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of D(t))!L.call(e,o)&&o!==n&&x(e,o,{get:()=>t[o],enumerable:!(r=O(t,o))||r.enumerable});return e};var H=e=>k(x({},"__esModule",{value:!0}),e);var q={};_(q,{RouteDataContext:()=>m,RouteDataProvider:()=>Y,action:()=>R,cache:()=>F,createAsync:()=>K,revalidate:()=>z,useAction:()=>X,useRouteData:()=>B,useSubmission:()=>J});module.exports=H(q);var u=require("@fluixi/reactive/signal");var M=!1,T=new Map;function v(){return M}function h(e,t){T.set(e,t)}function C(e){return T.get(e)}function U(){T.clear()}function j(){if(typeof window>"u")return;let e=document.getElementById("__FLUIXI_HYDRATION__");e&&e.remove();let t=document.createTreeWalker(document.body,NodeFilter.SHOW_COMMENT,null),n=[],r;for(;r=t.nextNode();){let o=r,i=o.nodeValue||"";(i.startsWith("#")||i.startsWith("/"))&&n.push(o)}n.forEach(o=>o.remove()),U()}typeof window<"u"&&window.addEventListener("load",()=>{setTimeout(j,100)});function b(){return typeof window>"u"}var S=require("@fluixi/dom"),l=require("@fluixi/reactive/signal"),V=require("@fluixi/reactive/signal");var f=require("@fluixi/dom"),s=require("@fluixi/reactive/signal");function A(e){(0,f.registerCreateComponent)(I)}function I(e,t){return N(e,t)}function N(e,t){let n=typeof e=="function"?(0,s.untrack)(()=>e(t)):e;if(n!=null&&(typeof n=="function"||typeof n=="object")&&!(0,s.isSignal)(n)){try{n.$name=e.name}catch{}return(0,f.asComponent)(n)}return n}A("index");function w(e){let t=(0,l.createContext)(e),n=r=>(r.debug,t.Provider({value:r.value,get children(){return r.children},debug:r.debug}));return typeof Symbol<"u"&&Symbol.for&&(n.$$typeof=Symbol.for("react.provider")),n.$$typeof=Symbol.for("react.provider"),{id:t.id,Provider:(0,S.asProvider)(n),defaultValue:e}}function P(e){let t=(0,l.useContext)(e);return t!==void 0?t:e.defaultValue}var d=new Map,c=new Map;function F(e,t){return async(...n)=>{let r=t+":"+JSON.stringify(n);if(v()||typeof window<"u"){let i=C(r);if(i!==void 0)return i}if(d.has(r))return d.get(r);if(c.has(r))return c.get(r);let o=Promise.resolve(e(...n)).then(i=>(d.set(r,i),c.delete(r),b()&&h(r,i),i),i=>{throw c.delete(r),i});return c.set(r,o),o}}var[Te,W]=(0,u.createSignal)(0),$=new Set;function z(e){let t=typeof e=="string"?n=>n.startsWith(e):e;for(let n of d.keys())t(n)&&(d.delete(n),$.add(n));W(n=>n+1)}function K(e,t){let[n]=(0,u.createResource)(t||(()=>!0),e);return n}var g=new Map;function E(e){return g.has(e)||g.set(e,(0,u.createSignal)(void 0)),g.get(e)}function R(e,t){let n=t||e.name||"action",r=async o=>{let[,i]=E(n),p=()=>i(void 0),y=()=>r(o);i({input:o,pending:!0,clear:p,retry:y});try{let a=await e(o);return i({input:o,result:a,pending:!1,clear:p,retry:y}),a}catch(a){throw i({input:o,error:a,pending:!1,clear:p,retry:y}),a}};return r.url=n,r.with=o=>R((...i)=>e(o,...i),t),r}function J(e){let[t]=E(e.url);return t}function X(e){return e}var m=w(),Y=m.Provider;function B(){let e=P(m);return typeof e=="function"?e:()=>e}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Data fetching and caching primitives for Fluixi Router
|
|
3
|
+
* @module @fluixi/core/router/data
|
|
4
|
+
*/
|
|
5
|
+
import { type Accessor, Resource } from '@fluixi/reactive/signal';
|
|
6
|
+
export type CacheFn<T, Args extends any[]> = (...args: Args) => Promise<T> | T | undefined;
|
|
7
|
+
export interface Action<T, Args extends any[]> {
|
|
8
|
+
(...args: Args): Promise<T>;
|
|
9
|
+
url?: string;
|
|
10
|
+
with?: (args: any) => Action<T, Args>;
|
|
11
|
+
}
|
|
12
|
+
export interface Submission<T, Args extends any[]> {
|
|
13
|
+
input: Args;
|
|
14
|
+
result?: T;
|
|
15
|
+
error?: any;
|
|
16
|
+
pending: boolean;
|
|
17
|
+
clear: () => void;
|
|
18
|
+
retry: () => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Cache an async function for data fetching
|
|
22
|
+
*
|
|
23
|
+
* Supports:
|
|
24
|
+
* - Deduplication
|
|
25
|
+
* - Hydration (Server -> Client)
|
|
26
|
+
* - Revalidation
|
|
27
|
+
*
|
|
28
|
+
* @param fn Async function to cache
|
|
29
|
+
* @param name Unique name for the cache key
|
|
30
|
+
*/
|
|
31
|
+
export declare function cache<T, Args extends any[]>(fn: (...args: Args) => Promise<T> | T, name: string): (...args: Args) => Promise<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Revalidate a cache key. Clears the memory cache and triggers
|
|
34
|
+
* reactive invalidation so resources using createAsync re-fetch.
|
|
35
|
+
*
|
|
36
|
+
* @param key Key prefix string or predicate function to match cache keys
|
|
37
|
+
*/
|
|
38
|
+
export declare function revalidate(key: string | ((key: string) => boolean)): void;
|
|
39
|
+
/**
|
|
40
|
+
* Create an async resource from a promise or cache function
|
|
41
|
+
*
|
|
42
|
+
* @param fetcher Function that returns a promise
|
|
43
|
+
* @param source Dependency signal (optional)
|
|
44
|
+
*/
|
|
45
|
+
export declare function createAsync<T, S extends any = true>(fetcher: (source: S) => Promise<T> | T, source?: () => S): Resource<S>;
|
|
46
|
+
/**
|
|
47
|
+
* Create a server action
|
|
48
|
+
*
|
|
49
|
+
* @param fn Function to execute (server-side)
|
|
50
|
+
* @param name Action name
|
|
51
|
+
*/
|
|
52
|
+
export declare function action<T, Args extends any[]>(fn: (...args: Args) => Promise<T> | T, name?: string): Action<T, Args>;
|
|
53
|
+
/**
|
|
54
|
+
* Hook to track action submission state
|
|
55
|
+
*/
|
|
56
|
+
export declare function useSubmission<T, Args extends any[]>(actionFn: Action<T, Args>): Accessor<Submission<T, Args> | undefined>;
|
|
57
|
+
/**
|
|
58
|
+
* Hook to get action executor
|
|
59
|
+
*/
|
|
60
|
+
export declare function useAction<T, Args extends any[]>(actionFn: Action<T, Args>): (...args: Args) => Promise<T>;
|
|
61
|
+
export interface RouteDataArgs {
|
|
62
|
+
/**
|
|
63
|
+
* Route params — either a reactive signal accessor (when called from the
|
|
64
|
+
* router's createRoot during layout creation) or a plain snapshot object
|
|
65
|
+
* (legacy / manual usage). Always normalise with:
|
|
66
|
+
* `const getParams = typeof args.params === 'function' ? args.params : () => args.params;`
|
|
67
|
+
*/
|
|
68
|
+
params: Record<string, string> | (() => Record<string, string>);
|
|
69
|
+
location: {
|
|
70
|
+
pathname: string;
|
|
71
|
+
search: string;
|
|
72
|
+
} | (() => {
|
|
73
|
+
pathname: string;
|
|
74
|
+
search: string;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
export type RouteLoadFunc = (args: RouteDataArgs, owner?: any, isCancelled?: () => boolean) => any;
|
|
78
|
+
export declare const RouteDataContext: {
|
|
79
|
+
id: symbol;
|
|
80
|
+
Provider: (props: {
|
|
81
|
+
value: any;
|
|
82
|
+
children?: any;
|
|
83
|
+
debug?: boolean;
|
|
84
|
+
}) => any;
|
|
85
|
+
defaultValue: any;
|
|
86
|
+
};
|
|
87
|
+
export declare const RouteDataProvider: (props: {
|
|
88
|
+
value: any;
|
|
89
|
+
children?: any;
|
|
90
|
+
debug?: boolean;
|
|
91
|
+
}) => any;
|
|
92
|
+
/**
|
|
93
|
+
* Access the data returned by the nearest ancestor route's `routeData` export.
|
|
94
|
+
*
|
|
95
|
+
* Returns a stable accessor function. Call it to get the current route data:
|
|
96
|
+
*
|
|
97
|
+
* const data = useRouteData<WorkspaceRouteData>();
|
|
98
|
+
* createEffect(() => console.log(data()?.workspace()));
|
|
99
|
+
*/
|
|
100
|
+
export declare function useRouteData<T>(): () => T | undefined;
|
|
101
|
+
//# sourceMappingURL=data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../../src/lib/router-next/data.ts"],"names":[],"mappings":"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"}
|