hadars 0.1.35 → 0.1.37
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/README.md +77 -0
- package/cli-lib.ts +90 -5
- package/dist/chunk-F7IIMM3J.js +1060 -0
- package/dist/chunk-UNQSQIOO.js +60 -0
- package/dist/cli.js +285 -261
- package/dist/hadars-B3b_6sj2.d.cts +188 -0
- package/dist/hadars-B3b_6sj2.d.ts +188 -0
- package/dist/index.cjs +11 -19
- package/dist/index.d.cts +105 -0
- package/dist/index.d.ts +4 -175
- package/dist/index.js +14 -42
- package/dist/{jsx-runtime-97ca74a5.d.ts → jsx-runtime-BOYrELJb.d.cts} +1 -1
- package/dist/jsx-runtime-BOYrELJb.d.ts +18 -0
- package/dist/lambda.cjs +1405 -0
- package/dist/lambda.d.cts +93 -0
- package/dist/lambda.d.ts +93 -0
- package/dist/lambda.js +499 -0
- package/dist/loader.cjs +22 -44
- package/dist/slim-react/index.cjs +29 -58
- package/dist/slim-react/index.d.cts +190 -0
- package/dist/slim-react/index.d.ts +3 -3
- package/dist/slim-react/index.js +34 -1043
- package/dist/slim-react/jsx-runtime.d.cts +1 -0
- package/dist/slim-react/jsx-runtime.d.ts +1 -1
- package/dist/ssr-render-worker.js +44 -78
- package/dist/ssr-watch.js +24 -7
- package/dist/utils/Head.tsx +2 -2
- package/package.json +11 -6
- package/src/build.ts +8 -166
- package/src/lambda.ts +287 -0
- package/src/types/hadars.ts +10 -0
- package/src/utils/Head.tsx +2 -2
- package/src/utils/rspack.ts +27 -0
- package/src/utils/ssrHandler.ts +185 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { MetaHTMLAttributes, LinkHTMLAttributes, StyleHTMLAttributes, ScriptHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
type HadarsGetInitialProps<T extends {}> = (req: HadarsRequest) => Promise<T> | T;
|
|
4
|
+
type HadarsGetClientProps<T extends {}> = (props: T) => Promise<T> | T;
|
|
5
|
+
type HadarsGetAfterRenderProps<T extends {}> = (props: HadarsProps<T>, html: string) => Promise<HadarsProps<T>> | HadarsProps<T>;
|
|
6
|
+
type HadarsGetFinalProps<T extends {}> = (props: HadarsProps<T>) => Promise<T> | T;
|
|
7
|
+
type HadarsApp<T extends {}> = React.FC<HadarsProps<T>>;
|
|
8
|
+
type HadarsEntryModule<T extends {}> = {
|
|
9
|
+
default: HadarsApp<T>;
|
|
10
|
+
getInitProps?: HadarsGetInitialProps<T>;
|
|
11
|
+
getAfterRenderProps?: HadarsGetAfterRenderProps<T>;
|
|
12
|
+
getFinalProps?: HadarsGetFinalProps<T>;
|
|
13
|
+
getClientProps?: HadarsGetClientProps<T>;
|
|
14
|
+
};
|
|
15
|
+
interface AppHead {
|
|
16
|
+
title: string;
|
|
17
|
+
status: number;
|
|
18
|
+
meta: Record<string, MetaProps>;
|
|
19
|
+
link: Record<string, LinkProps>;
|
|
20
|
+
style: Record<string, StyleProps>;
|
|
21
|
+
script: Record<string, ScriptProps>;
|
|
22
|
+
}
|
|
23
|
+
type UnsuspendEntry = {
|
|
24
|
+
status: 'pending';
|
|
25
|
+
promise: Promise<unknown>;
|
|
26
|
+
} | {
|
|
27
|
+
status: 'fulfilled';
|
|
28
|
+
value: unknown;
|
|
29
|
+
} | {
|
|
30
|
+
status: 'rejected';
|
|
31
|
+
reason: unknown;
|
|
32
|
+
};
|
|
33
|
+
/** @internal Populated by the framework's render loop — use useServerData() instead. */
|
|
34
|
+
interface AppUnsuspend {
|
|
35
|
+
cache: Map<string, UnsuspendEntry>;
|
|
36
|
+
}
|
|
37
|
+
interface AppContext {
|
|
38
|
+
path?: string;
|
|
39
|
+
head: AppHead;
|
|
40
|
+
/** @internal Framework use only — use the useServerData() hook instead. */
|
|
41
|
+
_unsuspend?: AppUnsuspend;
|
|
42
|
+
}
|
|
43
|
+
type HadarsEntryBase = {
|
|
44
|
+
location: string;
|
|
45
|
+
context: AppContext;
|
|
46
|
+
};
|
|
47
|
+
type HadarsProps<T extends {}> = T & HadarsEntryBase;
|
|
48
|
+
type MetaProps = MetaHTMLAttributes<HTMLMetaElement>;
|
|
49
|
+
type LinkProps = LinkHTMLAttributes<HTMLLinkElement>;
|
|
50
|
+
type StyleProps = StyleHTMLAttributes<HTMLStyleElement>;
|
|
51
|
+
type ScriptProps = ScriptHTMLAttributes<HTMLScriptElement>;
|
|
52
|
+
interface HadarsOptions {
|
|
53
|
+
port?: number;
|
|
54
|
+
entry: string;
|
|
55
|
+
baseURL?: string;
|
|
56
|
+
swcPlugins?: SwcPluginList;
|
|
57
|
+
proxy?: Record<string, string> | ((req: HadarsRequest) => Promise<Response | null> | Response | null);
|
|
58
|
+
proxyCORS?: boolean;
|
|
59
|
+
define?: Record<string, string>;
|
|
60
|
+
/**
|
|
61
|
+
* Bun WebSocket handler passed directly to `Bun.serve()`.
|
|
62
|
+
* Ignored on Node.js and Deno — use `fetch` + a third-party WS library there.
|
|
63
|
+
* Pass a `Bun.WebSocketHandler` instance here when running on Bun.
|
|
64
|
+
*/
|
|
65
|
+
websocket?: unknown;
|
|
66
|
+
fetch?: (req: HadarsRequest) => Promise<Response | undefined> | Response | undefined;
|
|
67
|
+
wsPath?: string;
|
|
68
|
+
hmrPort?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Parallelism level for `run()` mode (production server). Defaults to 1.
|
|
71
|
+
* Has no effect in `dev()` mode.
|
|
72
|
+
*
|
|
73
|
+
* **Node.js** — forks this many worker processes via `node:cluster`, each
|
|
74
|
+
* binding to the same port via OS round-robin. Set to `os.cpus().length`
|
|
75
|
+
* to saturate all CPU cores.
|
|
76
|
+
*
|
|
77
|
+
* **Bun / Deno** — creates a `node:worker_threads` render pool of this size.
|
|
78
|
+
* Each thread handles the synchronous `renderToString` step, freeing the
|
|
79
|
+
* main event loop for I/O.
|
|
80
|
+
*/
|
|
81
|
+
workers?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Override or extend rspack's `optimization` config for production client builds.
|
|
84
|
+
* Merged on top of hadars defaults (splitChunks vendor splitting, deterministic moduleIds).
|
|
85
|
+
* Has no effect on the SSR bundle or dev mode.
|
|
86
|
+
*/
|
|
87
|
+
optimization?: Record<string, unknown>;
|
|
88
|
+
/**
|
|
89
|
+
* Path to a custom HTML template file (relative to the project root).
|
|
90
|
+
* Replaces the built-in minimal template used to generate the HTML shell.
|
|
91
|
+
*
|
|
92
|
+
* The file must include two marker elements so hadars can inject the
|
|
93
|
+
* per-request head tags and the server-rendered body:
|
|
94
|
+
*
|
|
95
|
+
* ```html
|
|
96
|
+
* <meta name="HADARS_HEAD"> <!-- replaced with <title>, <meta>, <link>, <style> tags -->
|
|
97
|
+
* <meta name="HADARS_BODY"> <!-- replaced with the SSR-rendered React tree -->
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* Any `<style>` blocks in the template are automatically processed through
|
|
101
|
+
* PostCSS (using the project's `postcss.config.js`) at build/dev startup time,
|
|
102
|
+
* so `@import "tailwindcss"` and other PostCSS directives work as expected.
|
|
103
|
+
* Note: inline styles are processed once at startup and are not live-reloaded.
|
|
104
|
+
*/
|
|
105
|
+
htmlTemplate?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Force the React runtime mode independently of the build mode.
|
|
108
|
+
* Useful when you need production build optimizations (minification, tree-shaking)
|
|
109
|
+
* but want React's development build for debugging hydration mismatches or
|
|
110
|
+
* component stack traces.
|
|
111
|
+
*
|
|
112
|
+
* - `'development'` — forces `process.env.NODE_ENV = "development"` and enables
|
|
113
|
+
* JSX source info even in `hadars build`. React prints detailed hydration error
|
|
114
|
+
* messages and component stacks.
|
|
115
|
+
* - `'production'` — the default; React uses the optimised production bundle.
|
|
116
|
+
*
|
|
117
|
+
* Only affects the **client** bundle. The SSR bundle always uses slim-react.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* // hadars.config.ts — debug hydration errors in a production build
|
|
121
|
+
* reactMode: 'development'
|
|
122
|
+
*/
|
|
123
|
+
reactMode?: 'development' | 'production';
|
|
124
|
+
/**
|
|
125
|
+
* Additional rspack module rules appended to the built-in rule set.
|
|
126
|
+
* Applied to both the client and the SSR bundle.
|
|
127
|
+
*
|
|
128
|
+
* Useful for loaders not included by default, such as `@mdx-js/loader`,
|
|
129
|
+
* `less-loader`, `yaml-loader`, etc.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* moduleRules: [
|
|
133
|
+
* {
|
|
134
|
+
* test: /\.mdx?$/,
|
|
135
|
+
* use: [{ loader: '@mdx-js/loader' }],
|
|
136
|
+
* },
|
|
137
|
+
* ]
|
|
138
|
+
*/
|
|
139
|
+
moduleRules?: Record<string, any>[];
|
|
140
|
+
/**
|
|
141
|
+
* Additional rspack/webpack-compatible plugins applied to both the client
|
|
142
|
+
* and SSR bundles. Any object that implements the `apply(compiler)` method
|
|
143
|
+
* (the standard webpack/rspack plugin interface) is accepted.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* import { SubresourceIntegrityPlugin } from 'webpack-subresource-integrity';
|
|
147
|
+
* plugins: [new SubresourceIntegrityPlugin()]
|
|
148
|
+
*/
|
|
149
|
+
plugins?: Array<{
|
|
150
|
+
apply(compiler: any): void;
|
|
151
|
+
}>;
|
|
152
|
+
/**
|
|
153
|
+
* SSR response cache for `run()` mode. Has no effect in `dev()` mode.
|
|
154
|
+
*
|
|
155
|
+
* Receives the incoming request and should return `{ key, ttl? }` to cache
|
|
156
|
+
* the response, or `null`/`undefined` to skip caching for that request.
|
|
157
|
+
* `ttl` is the time-to-live in milliseconds; omit for entries that never expire.
|
|
158
|
+
* The function may be async.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* // Cache every page by pathname (no per-user personalisation):
|
|
162
|
+
* cache: (req) => ({ key: req.pathname })
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* // Cache with a per-route TTL, skip authenticated requests:
|
|
166
|
+
* cache: (req) => req.cookies.session ? null : { key: req.pathname, ttl: 60_000 }
|
|
167
|
+
*/
|
|
168
|
+
cache?: (req: HadarsRequest) => {
|
|
169
|
+
key: string;
|
|
170
|
+
ttl?: number;
|
|
171
|
+
} | null | undefined | Promise<{
|
|
172
|
+
key: string;
|
|
173
|
+
ttl?: number;
|
|
174
|
+
} | null | undefined>;
|
|
175
|
+
}
|
|
176
|
+
type SwcPluginItem = string | [string, Record<string, unknown>] | {
|
|
177
|
+
path: string;
|
|
178
|
+
options?: Record<string, unknown>;
|
|
179
|
+
} | ((...args: any[]) => any);
|
|
180
|
+
type SwcPluginList = SwcPluginItem[];
|
|
181
|
+
interface HadarsRequest extends Request {
|
|
182
|
+
pathname: string;
|
|
183
|
+
search: string;
|
|
184
|
+
location: string;
|
|
185
|
+
cookies: Record<string, string>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export type { AppContext as A, HadarsEntryModule as H, HadarsOptions as a, HadarsApp as b, HadarsGetAfterRenderProps as c, HadarsGetClientProps as d, HadarsGetFinalProps as e, HadarsGetInitialProps as f, HadarsProps as g, HadarsRequest as h };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { MetaHTMLAttributes, LinkHTMLAttributes, StyleHTMLAttributes, ScriptHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
type HadarsGetInitialProps<T extends {}> = (req: HadarsRequest) => Promise<T> | T;
|
|
4
|
+
type HadarsGetClientProps<T extends {}> = (props: T) => Promise<T> | T;
|
|
5
|
+
type HadarsGetAfterRenderProps<T extends {}> = (props: HadarsProps<T>, html: string) => Promise<HadarsProps<T>> | HadarsProps<T>;
|
|
6
|
+
type HadarsGetFinalProps<T extends {}> = (props: HadarsProps<T>) => Promise<T> | T;
|
|
7
|
+
type HadarsApp<T extends {}> = React.FC<HadarsProps<T>>;
|
|
8
|
+
type HadarsEntryModule<T extends {}> = {
|
|
9
|
+
default: HadarsApp<T>;
|
|
10
|
+
getInitProps?: HadarsGetInitialProps<T>;
|
|
11
|
+
getAfterRenderProps?: HadarsGetAfterRenderProps<T>;
|
|
12
|
+
getFinalProps?: HadarsGetFinalProps<T>;
|
|
13
|
+
getClientProps?: HadarsGetClientProps<T>;
|
|
14
|
+
};
|
|
15
|
+
interface AppHead {
|
|
16
|
+
title: string;
|
|
17
|
+
status: number;
|
|
18
|
+
meta: Record<string, MetaProps>;
|
|
19
|
+
link: Record<string, LinkProps>;
|
|
20
|
+
style: Record<string, StyleProps>;
|
|
21
|
+
script: Record<string, ScriptProps>;
|
|
22
|
+
}
|
|
23
|
+
type UnsuspendEntry = {
|
|
24
|
+
status: 'pending';
|
|
25
|
+
promise: Promise<unknown>;
|
|
26
|
+
} | {
|
|
27
|
+
status: 'fulfilled';
|
|
28
|
+
value: unknown;
|
|
29
|
+
} | {
|
|
30
|
+
status: 'rejected';
|
|
31
|
+
reason: unknown;
|
|
32
|
+
};
|
|
33
|
+
/** @internal Populated by the framework's render loop — use useServerData() instead. */
|
|
34
|
+
interface AppUnsuspend {
|
|
35
|
+
cache: Map<string, UnsuspendEntry>;
|
|
36
|
+
}
|
|
37
|
+
interface AppContext {
|
|
38
|
+
path?: string;
|
|
39
|
+
head: AppHead;
|
|
40
|
+
/** @internal Framework use only — use the useServerData() hook instead. */
|
|
41
|
+
_unsuspend?: AppUnsuspend;
|
|
42
|
+
}
|
|
43
|
+
type HadarsEntryBase = {
|
|
44
|
+
location: string;
|
|
45
|
+
context: AppContext;
|
|
46
|
+
};
|
|
47
|
+
type HadarsProps<T extends {}> = T & HadarsEntryBase;
|
|
48
|
+
type MetaProps = MetaHTMLAttributes<HTMLMetaElement>;
|
|
49
|
+
type LinkProps = LinkHTMLAttributes<HTMLLinkElement>;
|
|
50
|
+
type StyleProps = StyleHTMLAttributes<HTMLStyleElement>;
|
|
51
|
+
type ScriptProps = ScriptHTMLAttributes<HTMLScriptElement>;
|
|
52
|
+
interface HadarsOptions {
|
|
53
|
+
port?: number;
|
|
54
|
+
entry: string;
|
|
55
|
+
baseURL?: string;
|
|
56
|
+
swcPlugins?: SwcPluginList;
|
|
57
|
+
proxy?: Record<string, string> | ((req: HadarsRequest) => Promise<Response | null> | Response | null);
|
|
58
|
+
proxyCORS?: boolean;
|
|
59
|
+
define?: Record<string, string>;
|
|
60
|
+
/**
|
|
61
|
+
* Bun WebSocket handler passed directly to `Bun.serve()`.
|
|
62
|
+
* Ignored on Node.js and Deno — use `fetch` + a third-party WS library there.
|
|
63
|
+
* Pass a `Bun.WebSocketHandler` instance here when running on Bun.
|
|
64
|
+
*/
|
|
65
|
+
websocket?: unknown;
|
|
66
|
+
fetch?: (req: HadarsRequest) => Promise<Response | undefined> | Response | undefined;
|
|
67
|
+
wsPath?: string;
|
|
68
|
+
hmrPort?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Parallelism level for `run()` mode (production server). Defaults to 1.
|
|
71
|
+
* Has no effect in `dev()` mode.
|
|
72
|
+
*
|
|
73
|
+
* **Node.js** — forks this many worker processes via `node:cluster`, each
|
|
74
|
+
* binding to the same port via OS round-robin. Set to `os.cpus().length`
|
|
75
|
+
* to saturate all CPU cores.
|
|
76
|
+
*
|
|
77
|
+
* **Bun / Deno** — creates a `node:worker_threads` render pool of this size.
|
|
78
|
+
* Each thread handles the synchronous `renderToString` step, freeing the
|
|
79
|
+
* main event loop for I/O.
|
|
80
|
+
*/
|
|
81
|
+
workers?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Override or extend rspack's `optimization` config for production client builds.
|
|
84
|
+
* Merged on top of hadars defaults (splitChunks vendor splitting, deterministic moduleIds).
|
|
85
|
+
* Has no effect on the SSR bundle or dev mode.
|
|
86
|
+
*/
|
|
87
|
+
optimization?: Record<string, unknown>;
|
|
88
|
+
/**
|
|
89
|
+
* Path to a custom HTML template file (relative to the project root).
|
|
90
|
+
* Replaces the built-in minimal template used to generate the HTML shell.
|
|
91
|
+
*
|
|
92
|
+
* The file must include two marker elements so hadars can inject the
|
|
93
|
+
* per-request head tags and the server-rendered body:
|
|
94
|
+
*
|
|
95
|
+
* ```html
|
|
96
|
+
* <meta name="HADARS_HEAD"> <!-- replaced with <title>, <meta>, <link>, <style> tags -->
|
|
97
|
+
* <meta name="HADARS_BODY"> <!-- replaced with the SSR-rendered React tree -->
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* Any `<style>` blocks in the template are automatically processed through
|
|
101
|
+
* PostCSS (using the project's `postcss.config.js`) at build/dev startup time,
|
|
102
|
+
* so `@import "tailwindcss"` and other PostCSS directives work as expected.
|
|
103
|
+
* Note: inline styles are processed once at startup and are not live-reloaded.
|
|
104
|
+
*/
|
|
105
|
+
htmlTemplate?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Force the React runtime mode independently of the build mode.
|
|
108
|
+
* Useful when you need production build optimizations (minification, tree-shaking)
|
|
109
|
+
* but want React's development build for debugging hydration mismatches or
|
|
110
|
+
* component stack traces.
|
|
111
|
+
*
|
|
112
|
+
* - `'development'` — forces `process.env.NODE_ENV = "development"` and enables
|
|
113
|
+
* JSX source info even in `hadars build`. React prints detailed hydration error
|
|
114
|
+
* messages and component stacks.
|
|
115
|
+
* - `'production'` — the default; React uses the optimised production bundle.
|
|
116
|
+
*
|
|
117
|
+
* Only affects the **client** bundle. The SSR bundle always uses slim-react.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* // hadars.config.ts — debug hydration errors in a production build
|
|
121
|
+
* reactMode: 'development'
|
|
122
|
+
*/
|
|
123
|
+
reactMode?: 'development' | 'production';
|
|
124
|
+
/**
|
|
125
|
+
* Additional rspack module rules appended to the built-in rule set.
|
|
126
|
+
* Applied to both the client and the SSR bundle.
|
|
127
|
+
*
|
|
128
|
+
* Useful for loaders not included by default, such as `@mdx-js/loader`,
|
|
129
|
+
* `less-loader`, `yaml-loader`, etc.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* moduleRules: [
|
|
133
|
+
* {
|
|
134
|
+
* test: /\.mdx?$/,
|
|
135
|
+
* use: [{ loader: '@mdx-js/loader' }],
|
|
136
|
+
* },
|
|
137
|
+
* ]
|
|
138
|
+
*/
|
|
139
|
+
moduleRules?: Record<string, any>[];
|
|
140
|
+
/**
|
|
141
|
+
* Additional rspack/webpack-compatible plugins applied to both the client
|
|
142
|
+
* and SSR bundles. Any object that implements the `apply(compiler)` method
|
|
143
|
+
* (the standard webpack/rspack plugin interface) is accepted.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* import { SubresourceIntegrityPlugin } from 'webpack-subresource-integrity';
|
|
147
|
+
* plugins: [new SubresourceIntegrityPlugin()]
|
|
148
|
+
*/
|
|
149
|
+
plugins?: Array<{
|
|
150
|
+
apply(compiler: any): void;
|
|
151
|
+
}>;
|
|
152
|
+
/**
|
|
153
|
+
* SSR response cache for `run()` mode. Has no effect in `dev()` mode.
|
|
154
|
+
*
|
|
155
|
+
* Receives the incoming request and should return `{ key, ttl? }` to cache
|
|
156
|
+
* the response, or `null`/`undefined` to skip caching for that request.
|
|
157
|
+
* `ttl` is the time-to-live in milliseconds; omit for entries that never expire.
|
|
158
|
+
* The function may be async.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* // Cache every page by pathname (no per-user personalisation):
|
|
162
|
+
* cache: (req) => ({ key: req.pathname })
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* // Cache with a per-route TTL, skip authenticated requests:
|
|
166
|
+
* cache: (req) => req.cookies.session ? null : { key: req.pathname, ttl: 60_000 }
|
|
167
|
+
*/
|
|
168
|
+
cache?: (req: HadarsRequest) => {
|
|
169
|
+
key: string;
|
|
170
|
+
ttl?: number;
|
|
171
|
+
} | null | undefined | Promise<{
|
|
172
|
+
key: string;
|
|
173
|
+
ttl?: number;
|
|
174
|
+
} | null | undefined>;
|
|
175
|
+
}
|
|
176
|
+
type SwcPluginItem = string | [string, Record<string, unknown>] | {
|
|
177
|
+
path: string;
|
|
178
|
+
options?: Record<string, unknown>;
|
|
179
|
+
} | ((...args: any[]) => any);
|
|
180
|
+
type SwcPluginList = SwcPluginItem[];
|
|
181
|
+
interface HadarsRequest extends Request {
|
|
182
|
+
pathname: string;
|
|
183
|
+
search: string;
|
|
184
|
+
location: string;
|
|
185
|
+
cookies: Record<string, string>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export type { AppContext as A, HadarsEntryModule as H, HadarsOptions as a, HadarsApp as b, HadarsGetAfterRenderProps as c, HadarsGetClientProps as d, HadarsGetFinalProps as e, HadarsGetInitialProps as f, HadarsProps as g, HadarsRequest as h };
|
package/dist/index.cjs
CHANGED
|
@@ -28,8 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
30
|
// src/index.tsx
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
33
|
CacheSegment: () => CacheSegment,
|
|
34
34
|
HadarsContext: () => HadarsContext,
|
|
35
35
|
HadarsHead: () => Head,
|
|
@@ -39,7 +39,7 @@ __export(src_exports, {
|
|
|
39
39
|
loadModule: () => loadModule,
|
|
40
40
|
useServerData: () => useServerData
|
|
41
41
|
});
|
|
42
|
-
module.exports = __toCommonJS(
|
|
42
|
+
module.exports = __toCommonJS(index_exports);
|
|
43
43
|
|
|
44
44
|
// src/utils/Head.tsx
|
|
45
45
|
var import_react = __toESM(require("react"), 1);
|
|
@@ -179,8 +179,7 @@ var fetchedPaths = /* @__PURE__ */ new Set();
|
|
|
179
179
|
var ssrInitialKeys = null;
|
|
180
180
|
var unclaimedKeyCheckScheduled = false;
|
|
181
181
|
function scheduleUnclaimedKeyCheck() {
|
|
182
|
-
if (unclaimedKeyCheckScheduled)
|
|
183
|
-
return;
|
|
182
|
+
if (unclaimedKeyCheckScheduled) return;
|
|
184
183
|
unclaimedKeyCheckScheduled = true;
|
|
185
184
|
setTimeout(() => {
|
|
186
185
|
unclaimedKeyCheckScheduled = false;
|
|
@@ -241,13 +240,10 @@ function useServerData(key, fn) {
|
|
|
241
240
|
throw pendingDataFetch.get(pathKey);
|
|
242
241
|
}
|
|
243
242
|
const unsuspend = globalThis.__hadarsUnsuspend;
|
|
244
|
-
if (!unsuspend)
|
|
245
|
-
return void 0;
|
|
243
|
+
if (!unsuspend) return void 0;
|
|
246
244
|
const _u = unsuspend;
|
|
247
|
-
if (!_u.seenThisPass)
|
|
248
|
-
|
|
249
|
-
if (!_u.seenLastPass)
|
|
250
|
-
_u.seenLastPass = /* @__PURE__ */ new Set();
|
|
245
|
+
if (!_u.seenThisPass) _u.seenThisPass = /* @__PURE__ */ new Set();
|
|
246
|
+
if (!_u.seenLastPass) _u.seenLastPass = /* @__PURE__ */ new Set();
|
|
251
247
|
if (_u.newPassStarting) {
|
|
252
248
|
_u.seenLastPass = new Set(_u.seenThisPass);
|
|
253
249
|
_u.seenThisPass.clear();
|
|
@@ -289,8 +285,7 @@ function useServerData(key, fn) {
|
|
|
289
285
|
_u.newPassStarting = true;
|
|
290
286
|
throw existing.promise;
|
|
291
287
|
}
|
|
292
|
-
if (existing.status === "rejected")
|
|
293
|
-
throw existing.reason;
|
|
288
|
+
if (existing.status === "rejected") throw existing.reason;
|
|
294
289
|
return existing.value;
|
|
295
290
|
}
|
|
296
291
|
var genRandomId = () => {
|
|
@@ -309,8 +304,7 @@ var Head = import_react.default.memo(({ children, status }) => {
|
|
|
309
304
|
setStatus(status);
|
|
310
305
|
}
|
|
311
306
|
import_react.default.Children.forEach(children, (child) => {
|
|
312
|
-
if (!import_react.default.isValidElement(child))
|
|
313
|
-
return;
|
|
307
|
+
if (!import_react.default.isValidElement(child)) return;
|
|
314
308
|
const childType = child.type;
|
|
315
309
|
const childProps = child.props;
|
|
316
310
|
const id = childProps["id"] || genRandomId();
|
|
@@ -357,8 +351,7 @@ function getStore() {
|
|
|
357
351
|
}
|
|
358
352
|
function getSegment(key) {
|
|
359
353
|
const entry = getStore().get(key);
|
|
360
|
-
if (!entry)
|
|
361
|
-
return null;
|
|
354
|
+
if (!entry) return null;
|
|
362
355
|
if (entry.expiresAt !== null && Date.now() >= entry.expiresAt) {
|
|
363
356
|
getStore().delete(key);
|
|
364
357
|
return null;
|
|
@@ -391,8 +384,7 @@ function CacheSegment({ cacheKey, ttl, children }) {
|
|
|
391
384
|
"data-key": cacheKey,
|
|
392
385
|
"data-cache": "miss"
|
|
393
386
|
};
|
|
394
|
-
if (ttl != null)
|
|
395
|
-
props["data-ttl"] = ttl;
|
|
387
|
+
if (ttl != null) props["data-ttl"] = ttl;
|
|
396
388
|
return import_react2.default.createElement(CACHE_TAG, props, children);
|
|
397
389
|
}
|
|
398
390
|
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as React$1 from 'react';
|
|
2
|
+
import React__default from 'react';
|
|
3
|
+
import { A as AppContext } from './hadars-B3b_6sj2.cjs';
|
|
4
|
+
export { b as HadarsApp, H as HadarsEntryModule, c as HadarsGetAfterRenderProps, d as HadarsGetClientProps, e as HadarsGetFinalProps, f as HadarsGetInitialProps, a as HadarsOptions, g as HadarsProps, h as HadarsRequest } from './hadars-B3b_6sj2.cjs';
|
|
5
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
/** Call this before hydrating to seed the client cache from the server's data.
|
|
8
|
+
* Invoked automatically by the hadars client bootstrap.
|
|
9
|
+
* Always clears the existing cache before populating — call with `{}` to just clear. */
|
|
10
|
+
declare function initServerDataCache(data: Record<string, unknown>): void;
|
|
11
|
+
/**
|
|
12
|
+
* Fetch async data on the server during SSR. Returns `undefined` on the first
|
|
13
|
+
* render pass(es) while the promise is in flight; returns the resolved value
|
|
14
|
+
* once the framework's render loop has awaited it.
|
|
15
|
+
*
|
|
16
|
+
* On the client the pre-resolved value is read from the hydration cache
|
|
17
|
+
* serialised into the page by the server, so no fetch is issued in the browser.
|
|
18
|
+
*
|
|
19
|
+
* The `key` (string or array of strings) uniquely identifies the cached value
|
|
20
|
+
* across all SSR render passes and client hydration — it must be stable and
|
|
21
|
+
* unique within the page.
|
|
22
|
+
*
|
|
23
|
+
* `fn` may return a `Promise<T>` (async usage) or return `T` synchronously.
|
|
24
|
+
* The resolved value is serialised into `__serverData` and returned from cache
|
|
25
|
+
* during hydration.
|
|
26
|
+
*
|
|
27
|
+
* `fn` is **server-only**: it is never called in the browser. On client-side
|
|
28
|
+
* navigation (after the initial SSR load), hadars automatically fires a
|
|
29
|
+
* data-only request to the current URL (`X-Hadars-Data: 1`) and suspends via
|
|
30
|
+
* React Suspense until the server returns the JSON map of resolved values.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const user = useServerData('current_user', () => db.getUser(id));
|
|
34
|
+
* const post = useServerData(['post', postId], () => db.getPost(postId));
|
|
35
|
+
* if (!user) return null; // undefined while pending on the first SSR pass
|
|
36
|
+
*/
|
|
37
|
+
declare function useServerData<T>(key: string | string[], fn: () => Promise<T> | T): T | undefined;
|
|
38
|
+
declare const Head: React__default.FC<{
|
|
39
|
+
children?: React__default.ReactNode;
|
|
40
|
+
status?: number;
|
|
41
|
+
}>;
|
|
42
|
+
|
|
43
|
+
interface CacheSegmentProps {
|
|
44
|
+
/**
|
|
45
|
+
* Unique cache key for this segment. Use a key that encodes all values
|
|
46
|
+
* the output depends on, e.g. `"product-" + product.id`.
|
|
47
|
+
*/
|
|
48
|
+
cacheKey: string;
|
|
49
|
+
/**
|
|
50
|
+
* Time-to-live in milliseconds. Omit for entries that never expire.
|
|
51
|
+
*/
|
|
52
|
+
ttl?: number;
|
|
53
|
+
children: React__default.ReactNode;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Caches the server-rendered HTML of its children across requests.
|
|
57
|
+
*
|
|
58
|
+
* **Server (SSR):**
|
|
59
|
+
* - Cache miss — children are rendered normally as part of the main
|
|
60
|
+
* `renderToString` call, so React context propagates correctly. The output
|
|
61
|
+
* is wrapped in a `<hadars-c>` marker that `processSegmentCache` uses to
|
|
62
|
+
* extract and store the HTML. The marker is stripped before the response
|
|
63
|
+
* is sent; the browser never sees it.
|
|
64
|
+
* - Cache hit — children are **not** rendered at all. The cached HTML is
|
|
65
|
+
* injected directly, saving the entire subtree render cost.
|
|
66
|
+
*
|
|
67
|
+
* **Client:** renders children normally (no caching). Because the server
|
|
68
|
+
* strips the marker wrapper, the client output matches the server HTML and
|
|
69
|
+
* React hydration succeeds without warnings for deterministic components.
|
|
70
|
+
*
|
|
71
|
+
* **Note:** components that rely on request-specific data (cookies, auth,
|
|
72
|
+
* personalisation) must not be wrapped in `CacheSegment` unless the cache
|
|
73
|
+
* key encodes that data — otherwise a cached response for one user could be
|
|
74
|
+
* served to another.
|
|
75
|
+
*/
|
|
76
|
+
declare function CacheSegment({ cacheKey, ttl, children }: CacheSegmentProps): react_jsx_runtime.JSX.Element;
|
|
77
|
+
|
|
78
|
+
declare function deleteSegment(key: string): void;
|
|
79
|
+
declare function clearSegments(): void;
|
|
80
|
+
|
|
81
|
+
declare const HadarsContext: React$1.FC<{
|
|
82
|
+
children: React.ReactNode;
|
|
83
|
+
context: AppContext;
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* Dynamically loads a module with target-aware behaviour:
|
|
87
|
+
*
|
|
88
|
+
* - **Browser** (after loader transform): becomes `import('./path')`, which
|
|
89
|
+
* rspack splits into a separate chunk for true code splitting.
|
|
90
|
+
* - **SSR** (after loader transform): becomes
|
|
91
|
+
* `Promise.resolve(require('./path'))`, which rspack bundles statically.
|
|
92
|
+
*
|
|
93
|
+
* The hadars rspack loader must be active for the transform to apply.
|
|
94
|
+
* This runtime fallback uses a plain dynamic import (no code splitting).
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* // Code-split React component:
|
|
98
|
+
* const MyComp = React.lazy(() => loadModule('./MyComp'));
|
|
99
|
+
*
|
|
100
|
+
* // Dynamic data:
|
|
101
|
+
* const { default: fn } = await loadModule<typeof import('./util')>('./util');
|
|
102
|
+
*/
|
|
103
|
+
declare function loadModule<T = any>(path: string): Promise<T>;
|
|
104
|
+
|
|
105
|
+
export { CacheSegment, HadarsContext, Head as HadarsHead, clearSegments, deleteSegment, initServerDataCache, loadModule, useServerData };
|