hadars 0.1.1
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 +118 -0
- package/cli-bun.ts +13 -0
- package/cli-lib.ts +203 -0
- package/cli.ts +13 -0
- package/dist/cli.js +1441 -0
- package/dist/index.cjs +303 -0
- package/dist/index.d.ts +160 -0
- package/dist/index.js +263 -0
- package/dist/loader.cjs +34 -0
- package/dist/ssr-render-worker.js +92 -0
- package/dist/ssr-watch.js +345 -0
- package/dist/template.html +11 -0
- package/dist/utils/clientScript.tsx +58 -0
- package/index.ts +15 -0
- package/package.json +99 -0
- package/src/build.ts +716 -0
- package/src/index.tsx +41 -0
- package/src/ssr-render-worker.ts +138 -0
- package/src/ssr-watch.ts +56 -0
- package/src/types/global.d.ts +5 -0
- package/src/types/ninety.ts +116 -0
- package/src/utils/Head.tsx +357 -0
- package/src/utils/clientScript.tsx +58 -0
- package/src/utils/cookies.ts +16 -0
- package/src/utils/loadModule.ts +4 -0
- package/src/utils/loader.ts +41 -0
- package/src/utils/proxyHandler.tsx +101 -0
- package/src/utils/request.tsx +9 -0
- package/src/utils/response.tsx +198 -0
- package/src/utils/rspack.ts +359 -0
- package/src/utils/runtime.ts +19 -0
- package/src/utils/serve.ts +140 -0
- package/src/utils/staticFile.ts +48 -0
- package/src/utils/template.html +11 -0
- package/src/utils/upgradeRequest.tsx +19 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.tsx
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
HadarsContext: () => HadarsContext,
|
|
34
|
+
HadarsHead: () => Head,
|
|
35
|
+
loadModule: () => loadModule,
|
|
36
|
+
useServerData: () => useServerData
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(src_exports);
|
|
39
|
+
|
|
40
|
+
// src/utils/Head.tsx
|
|
41
|
+
var import_react = __toESM(require("react"), 1);
|
|
42
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
43
|
+
var AppContext = import_react.default.createContext({
|
|
44
|
+
setTitle: () => {
|
|
45
|
+
console.warn("AppContext: setTitle called outside of provider");
|
|
46
|
+
},
|
|
47
|
+
addMeta: () => {
|
|
48
|
+
console.warn("AppContext: addMeta called outside of provider");
|
|
49
|
+
},
|
|
50
|
+
addLink: () => {
|
|
51
|
+
console.warn("AppContext: addLink called outside of provider");
|
|
52
|
+
},
|
|
53
|
+
addStyle: () => {
|
|
54
|
+
console.warn("AppContext: addStyle called outside of provider");
|
|
55
|
+
},
|
|
56
|
+
addScript: () => {
|
|
57
|
+
console.warn("AppContext: addScript called outside of provider");
|
|
58
|
+
},
|
|
59
|
+
setStatus: () => {
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
var AppProviderSSR = import_react.default.memo(({ children, context }) => {
|
|
63
|
+
const { head } = context;
|
|
64
|
+
const setTitle = import_react.default.useCallback((title) => {
|
|
65
|
+
head.title = title;
|
|
66
|
+
}, [head]);
|
|
67
|
+
const addMeta = import_react.default.useCallback((id, props) => {
|
|
68
|
+
head.meta[id] = props;
|
|
69
|
+
}, [head]);
|
|
70
|
+
const addLink = import_react.default.useCallback((id, props) => {
|
|
71
|
+
head.link[id] = props;
|
|
72
|
+
}, [head]);
|
|
73
|
+
const addStyle = import_react.default.useCallback((id, props) => {
|
|
74
|
+
head.style[id] = props;
|
|
75
|
+
}, [head]);
|
|
76
|
+
const addScript = import_react.default.useCallback((id, props) => {
|
|
77
|
+
head.script[id] = props;
|
|
78
|
+
}, [head]);
|
|
79
|
+
const setStatus = import_react.default.useCallback((status) => {
|
|
80
|
+
head.status = status;
|
|
81
|
+
}, [head]);
|
|
82
|
+
const contextValue = import_react.default.useMemo(() => ({
|
|
83
|
+
setTitle,
|
|
84
|
+
addMeta,
|
|
85
|
+
addLink,
|
|
86
|
+
addStyle,
|
|
87
|
+
addScript,
|
|
88
|
+
setStatus
|
|
89
|
+
}), [setTitle, addMeta, addLink, addStyle, addScript, setStatus]);
|
|
90
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppContext.Provider, { value: contextValue, children });
|
|
91
|
+
});
|
|
92
|
+
var AppProviderCSR = import_react.default.memo(({ children }) => {
|
|
93
|
+
const setTitle = import_react.default.useCallback((title) => {
|
|
94
|
+
document.title = title;
|
|
95
|
+
}, []);
|
|
96
|
+
const addMeta = import_react.default.useCallback((id, props) => {
|
|
97
|
+
let meta = document.querySelector(`#${id}`);
|
|
98
|
+
if (!meta) {
|
|
99
|
+
meta = document.createElement("meta");
|
|
100
|
+
meta.setAttribute("id", id);
|
|
101
|
+
document.head.appendChild(meta);
|
|
102
|
+
}
|
|
103
|
+
Object.keys(props).forEach((key) => {
|
|
104
|
+
const value = props[key];
|
|
105
|
+
if (value) {
|
|
106
|
+
meta.setAttribute(key, value);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}, []);
|
|
110
|
+
const addLink = import_react.default.useCallback((id, props) => {
|
|
111
|
+
let link = document.querySelector(`#${id}`);
|
|
112
|
+
if (!link) {
|
|
113
|
+
link = document.createElement("link");
|
|
114
|
+
link.setAttribute("id", id);
|
|
115
|
+
document.head.appendChild(link);
|
|
116
|
+
}
|
|
117
|
+
Object.keys(props).forEach((key) => {
|
|
118
|
+
const value = props[key];
|
|
119
|
+
if (value) {
|
|
120
|
+
link.setAttribute(key, value);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}, []);
|
|
124
|
+
const addStyle = import_react.default.useCallback((id, props) => {
|
|
125
|
+
let style = document.getElementById(id);
|
|
126
|
+
if (!style) {
|
|
127
|
+
style = document.createElement("style");
|
|
128
|
+
style.setAttribute("id", id);
|
|
129
|
+
document.head.appendChild(style);
|
|
130
|
+
}
|
|
131
|
+
Object.keys(props).forEach((key) => {
|
|
132
|
+
if (key === "dangerouslySetInnerHTML" && props[key] && props[key].__html) {
|
|
133
|
+
style.innerHTML = props[key].__html;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const value = props[key];
|
|
137
|
+
if (value) {
|
|
138
|
+
style[key] = value;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}, []);
|
|
142
|
+
const addScript = import_react.default.useCallback((id, props) => {
|
|
143
|
+
let script = document.getElementById(id);
|
|
144
|
+
if (!script) {
|
|
145
|
+
script = document.createElement("script");
|
|
146
|
+
script.setAttribute("id", id);
|
|
147
|
+
document.body.appendChild(script);
|
|
148
|
+
}
|
|
149
|
+
Object.keys(props).forEach((key) => {
|
|
150
|
+
if (key === "dangerouslySetInnerHTML" && props[key] && props[key].__html) {
|
|
151
|
+
script.innerHTML = props[key].__html;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const value = props[key];
|
|
155
|
+
if (value) {
|
|
156
|
+
script[key] = value;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}, []);
|
|
160
|
+
const contextValue = import_react.default.useMemo(() => ({
|
|
161
|
+
setTitle,
|
|
162
|
+
addMeta,
|
|
163
|
+
addLink,
|
|
164
|
+
addStyle,
|
|
165
|
+
addScript,
|
|
166
|
+
setStatus: () => {
|
|
167
|
+
}
|
|
168
|
+
}), [setTitle, addMeta, addLink, addStyle, addScript]);
|
|
169
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppContext.Provider, { value: contextValue, children });
|
|
170
|
+
});
|
|
171
|
+
var useApp = () => import_react.default.useContext(AppContext);
|
|
172
|
+
var clientServerDataCache = /* @__PURE__ */ new Map();
|
|
173
|
+
function useServerData(key, fn) {
|
|
174
|
+
const cacheKey = Array.isArray(key) ? key.join("\0") : key;
|
|
175
|
+
if (typeof window !== "undefined") {
|
|
176
|
+
if (clientServerDataCache.has(cacheKey)) {
|
|
177
|
+
return clientServerDataCache.get(cacheKey);
|
|
178
|
+
}
|
|
179
|
+
return fn();
|
|
180
|
+
}
|
|
181
|
+
const unsuspend = globalThis.__hadarsUnsuspend;
|
|
182
|
+
if (!unsuspend)
|
|
183
|
+
return void 0;
|
|
184
|
+
const existing = unsuspend.cache.get(cacheKey);
|
|
185
|
+
if (existing?.status === "suspense-resolved") {
|
|
186
|
+
try {
|
|
187
|
+
return fn();
|
|
188
|
+
} catch {
|
|
189
|
+
return void 0;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!existing) {
|
|
193
|
+
let result;
|
|
194
|
+
try {
|
|
195
|
+
result = fn();
|
|
196
|
+
} catch (thrown) {
|
|
197
|
+
if (thrown !== null && typeof thrown === "object" && typeof thrown.then === "function") {
|
|
198
|
+
const suspensePromise = Promise.resolve(thrown).then(
|
|
199
|
+
() => {
|
|
200
|
+
unsuspend.cache.set(cacheKey, { status: "suspense-resolved" });
|
|
201
|
+
},
|
|
202
|
+
() => {
|
|
203
|
+
unsuspend.cache.set(cacheKey, { status: "suspense-resolved" });
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
unsuspend.cache.set(cacheKey, { status: "pending", promise: suspensePromise });
|
|
207
|
+
unsuspend.hasPending = true;
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
throw thrown;
|
|
211
|
+
}
|
|
212
|
+
const isThenable = result !== null && typeof result === "object" && typeof result.then === "function";
|
|
213
|
+
if (!isThenable) {
|
|
214
|
+
const value = result;
|
|
215
|
+
unsuspend.cache.set(cacheKey, { status: "fulfilled", value });
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
const promise = result.then(
|
|
219
|
+
(value) => {
|
|
220
|
+
unsuspend.cache.set(cacheKey, { status: "fulfilled", value });
|
|
221
|
+
},
|
|
222
|
+
(reason) => {
|
|
223
|
+
unsuspend.cache.set(cacheKey, { status: "rejected", reason });
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
unsuspend.cache.set(cacheKey, { status: "pending", promise });
|
|
227
|
+
unsuspend.hasPending = true;
|
|
228
|
+
return void 0;
|
|
229
|
+
}
|
|
230
|
+
if (existing.status === "pending") {
|
|
231
|
+
unsuspend.hasPending = true;
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
if (existing.status === "rejected")
|
|
235
|
+
throw existing.reason;
|
|
236
|
+
return existing.value;
|
|
237
|
+
}
|
|
238
|
+
var genRandomId = () => {
|
|
239
|
+
return "head-" + Math.random().toString(36).substr(2, 9);
|
|
240
|
+
};
|
|
241
|
+
var Head = import_react.default.memo(({ children, status }) => {
|
|
242
|
+
const {
|
|
243
|
+
setStatus,
|
|
244
|
+
setTitle,
|
|
245
|
+
addMeta,
|
|
246
|
+
addLink,
|
|
247
|
+
addStyle,
|
|
248
|
+
addScript
|
|
249
|
+
} = useApp();
|
|
250
|
+
if (status) {
|
|
251
|
+
setStatus(status);
|
|
252
|
+
}
|
|
253
|
+
import_react.default.Children.forEach(children, (child) => {
|
|
254
|
+
if (!import_react.default.isValidElement(child))
|
|
255
|
+
return;
|
|
256
|
+
const childType = child.type;
|
|
257
|
+
const childProps = child.props;
|
|
258
|
+
const id = childProps["id"] || genRandomId();
|
|
259
|
+
switch (childType) {
|
|
260
|
+
case "title": {
|
|
261
|
+
setTitle(childProps["children"]);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
case "meta": {
|
|
265
|
+
addMeta(id.toString(), childProps);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
case "link": {
|
|
269
|
+
addLink(id.toString(), childProps);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
case "script": {
|
|
273
|
+
addScript(id.toString(), childProps);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
case "style": {
|
|
277
|
+
addStyle(id.toString(), childProps);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
default: {
|
|
281
|
+
console.warn(`HadarsHead: Unsupported child type: ${childType}`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
return null;
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// src/index.tsx
|
|
290
|
+
var HadarsContext = typeof window === "undefined" ? AppProviderSSR : AppProviderCSR;
|
|
291
|
+
function loadModule(path) {
|
|
292
|
+
return import(
|
|
293
|
+
/* webpackIgnore: true */
|
|
294
|
+
path
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
298
|
+
0 && (module.exports = {
|
|
299
|
+
HadarsContext,
|
|
300
|
+
HadarsHead,
|
|
301
|
+
loadModule,
|
|
302
|
+
useServerData
|
|
303
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as React$1 from 'react';
|
|
2
|
+
import React__default, { MetaHTMLAttributes, LinkHTMLAttributes, StyleHTMLAttributes, ScriptHTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
type HadarsGetInitialProps<T extends {}> = (req: HadarsRequest) => Promise<T> | T;
|
|
5
|
+
type HadarsGetClientProps<T extends {}> = (props: T) => Promise<T> | T;
|
|
6
|
+
type HadarsGetAfterRenderProps<T extends {}> = (props: HadarsProps<T>, html: string) => Promise<HadarsProps<T>> | HadarsProps<T>;
|
|
7
|
+
type HadarsGetFinalProps<T extends {}> = (props: HadarsProps<T>) => Promise<T> | T;
|
|
8
|
+
type HadarsApp<T extends {}> = React.FC<HadarsProps<T>>;
|
|
9
|
+
type HadarsEntryModule<T extends {}> = {
|
|
10
|
+
default: HadarsApp<T>;
|
|
11
|
+
getInitProps?: HadarsGetInitialProps<T>;
|
|
12
|
+
getAfterRenderProps?: HadarsGetAfterRenderProps<T>;
|
|
13
|
+
getFinalProps?: HadarsGetFinalProps<T>;
|
|
14
|
+
getClientProps?: HadarsGetClientProps<T>;
|
|
15
|
+
};
|
|
16
|
+
interface AppHead {
|
|
17
|
+
title: string;
|
|
18
|
+
status: number;
|
|
19
|
+
meta: Record<string, MetaProps>;
|
|
20
|
+
link: Record<string, LinkProps>;
|
|
21
|
+
style: Record<string, StyleProps>;
|
|
22
|
+
script: Record<string, ScriptProps>;
|
|
23
|
+
}
|
|
24
|
+
type UnsuspendEntry = {
|
|
25
|
+
status: 'pending';
|
|
26
|
+
promise: Promise<unknown>;
|
|
27
|
+
} | {
|
|
28
|
+
status: 'fulfilled';
|
|
29
|
+
value: unknown;
|
|
30
|
+
} | {
|
|
31
|
+
status: 'suspense-resolved';
|
|
32
|
+
} | {
|
|
33
|
+
status: 'rejected';
|
|
34
|
+
reason: unknown;
|
|
35
|
+
};
|
|
36
|
+
/** @internal Populated by the framework's render loop — use useServerData() instead. */
|
|
37
|
+
interface AppUnsuspend {
|
|
38
|
+
cache: Map<string, UnsuspendEntry>;
|
|
39
|
+
hasPending: boolean;
|
|
40
|
+
}
|
|
41
|
+
interface AppContext {
|
|
42
|
+
path?: string;
|
|
43
|
+
head: AppHead;
|
|
44
|
+
/** @internal Framework use only — use the useServerData() hook instead. */
|
|
45
|
+
_unsuspend?: AppUnsuspend;
|
|
46
|
+
}
|
|
47
|
+
type HadarsEntryBase = {
|
|
48
|
+
location: string;
|
|
49
|
+
context: AppContext;
|
|
50
|
+
};
|
|
51
|
+
type HadarsProps<T extends {}> = T & HadarsEntryBase;
|
|
52
|
+
type MetaProps = MetaHTMLAttributes<HTMLMetaElement>;
|
|
53
|
+
type LinkProps = LinkHTMLAttributes<HTMLLinkElement>;
|
|
54
|
+
type StyleProps = StyleHTMLAttributes<HTMLStyleElement>;
|
|
55
|
+
type ScriptProps = ScriptHTMLAttributes<HTMLScriptElement>;
|
|
56
|
+
interface HadarsOptions {
|
|
57
|
+
port?: number;
|
|
58
|
+
entry: string;
|
|
59
|
+
baseURL?: string;
|
|
60
|
+
swcPlugins?: SwcPluginList;
|
|
61
|
+
proxy?: Record<string, string> | ((req: HadarsRequest) => Promise<Response | null> | Response | null);
|
|
62
|
+
proxyCORS?: boolean;
|
|
63
|
+
define?: Record<string, string>;
|
|
64
|
+
/**
|
|
65
|
+
* Bun WebSocket handler passed directly to `Bun.serve()`.
|
|
66
|
+
* Ignored on Node.js and Deno — use `fetch` + a third-party WS library there.
|
|
67
|
+
* Pass a `Bun.WebSocketHandler` instance here when running on Bun.
|
|
68
|
+
*/
|
|
69
|
+
websocket?: unknown;
|
|
70
|
+
fetch?: (req: HadarsRequest) => Promise<Response | undefined> | Response | undefined;
|
|
71
|
+
wsPath?: string;
|
|
72
|
+
hmrPort?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Parallelism level for `run()` mode (production server). Defaults to 1.
|
|
75
|
+
* Has no effect in `dev()` mode.
|
|
76
|
+
*
|
|
77
|
+
* **Node.js** — forks this many worker processes via `node:cluster`, each
|
|
78
|
+
* binding to the same port via OS round-robin. Set to `os.cpus().length`
|
|
79
|
+
* to saturate all CPU cores.
|
|
80
|
+
*
|
|
81
|
+
* **Bun / Deno** — creates a `node:worker_threads` render pool of this size.
|
|
82
|
+
* Each thread handles the synchronous `renderToString` step, freeing the
|
|
83
|
+
* main event loop for I/O. Only applies when `streaming` is `false` (the default).
|
|
84
|
+
*/
|
|
85
|
+
workers?: number;
|
|
86
|
+
/**
|
|
87
|
+
* Whether to use streaming SSR (`renderToReadableStream`) for rendering React
|
|
88
|
+
* components. Defaults to `false`. Set to `true` to use streaming via
|
|
89
|
+
* `renderToReadableStream` instead of the synchronous `renderToString`.
|
|
90
|
+
*/
|
|
91
|
+
streaming?: boolean;
|
|
92
|
+
}
|
|
93
|
+
type SwcPluginItem = string | [string, Record<string, unknown>] | {
|
|
94
|
+
path: string;
|
|
95
|
+
options?: Record<string, unknown>;
|
|
96
|
+
} | ((...args: any[]) => any);
|
|
97
|
+
type SwcPluginList = SwcPluginItem[];
|
|
98
|
+
interface HadarsRequest extends Request {
|
|
99
|
+
pathname: string;
|
|
100
|
+
search: string;
|
|
101
|
+
location: string;
|
|
102
|
+
cookies: Record<string, string>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Fetch async data on the server during SSR. Returns `undefined` on the first
|
|
107
|
+
* render pass(es) while the promise is in flight; returns the resolved value
|
|
108
|
+
* once the framework's render loop has awaited it.
|
|
109
|
+
*
|
|
110
|
+
* On the client the pre-resolved value is read from the hydration cache
|
|
111
|
+
* serialised into the page by the server, so no fetch is issued in the browser.
|
|
112
|
+
*
|
|
113
|
+
* The `key` (string or array of strings) uniquely identifies the cached value
|
|
114
|
+
* across all SSR render passes and client hydration — it must be stable and
|
|
115
|
+
* unique within the page.
|
|
116
|
+
*
|
|
117
|
+
* `fn` may return a `Promise<T>` (normal async usage), return `T` synchronously,
|
|
118
|
+
* or throw a thenable (React Suspense protocol). All three cases are handled:
|
|
119
|
+
* - Async `Promise<T>`: awaited across render iterations, result cached.
|
|
120
|
+
* - Synchronous return: value stored immediately, returned on the same pass.
|
|
121
|
+
* - Thrown thenable (e.g. `useQuery({ suspense: true })`): the thrown promise is
|
|
122
|
+
* awaited, the cache entry is then cleared so that the next render re-calls
|
|
123
|
+
* `fn()` — at that point the Suspense hook returns synchronously.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* const user = useServerData('current_user', () => db.getUser(id));
|
|
127
|
+
* const post = useServerData(['post', postId], () => db.getPost(postId));
|
|
128
|
+
* if (!user) return null; // undefined while pending on the first SSR pass
|
|
129
|
+
*/
|
|
130
|
+
declare function useServerData<T>(key: string | string[], fn: () => Promise<T> | T): T | undefined;
|
|
131
|
+
declare const Head: React__default.FC<{
|
|
132
|
+
children?: React__default.ReactNode;
|
|
133
|
+
status?: number;
|
|
134
|
+
}>;
|
|
135
|
+
|
|
136
|
+
declare const HadarsContext: React$1.FC<{
|
|
137
|
+
children: React.ReactNode;
|
|
138
|
+
context: AppContext;
|
|
139
|
+
}>;
|
|
140
|
+
/**
|
|
141
|
+
* Dynamically loads a module with target-aware behaviour:
|
|
142
|
+
*
|
|
143
|
+
* - **Browser** (after loader transform): becomes `import('./path')`, which
|
|
144
|
+
* rspack splits into a separate chunk for true code splitting.
|
|
145
|
+
* - **SSR** (after loader transform): becomes
|
|
146
|
+
* `Promise.resolve(require('./path'))`, which rspack bundles statically.
|
|
147
|
+
*
|
|
148
|
+
* The hadars rspack loader must be active for the transform to apply.
|
|
149
|
+
* This runtime fallback uses a plain dynamic import (no code splitting).
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* // Code-split React component:
|
|
153
|
+
* const MyComp = React.lazy(() => loadModule('./MyComp'));
|
|
154
|
+
*
|
|
155
|
+
* // Dynamic data:
|
|
156
|
+
* const { default: fn } = await loadModule<typeof import('./util')>('./util');
|
|
157
|
+
*/
|
|
158
|
+
declare function loadModule<T = any>(path: string): Promise<T>;
|
|
159
|
+
|
|
160
|
+
export { HadarsApp, HadarsContext, HadarsEntryModule, HadarsGetAfterRenderProps, HadarsGetClientProps, HadarsGetFinalProps, HadarsGetInitialProps, Head as HadarsHead, HadarsOptions, HadarsProps, HadarsRequest, loadModule, useServerData };
|