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/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
+ });
@@ -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 };