hadars 0.4.1 → 0.4.2

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.
Files changed (58) hide show
  1. package/dist/{chunk-TV37IMRB.js → chunk-2TMQUXFL.js} +10 -10
  2. package/dist/{chunk-2J2L2H3H.js → chunk-NYLXE7T7.js} +6 -6
  3. package/dist/{chunk-OS3V4CPN.js → chunk-OZUZS2PD.js} +4 -4
  4. package/dist/cli.js +462 -496
  5. package/dist/cloudflare.cjs +11 -11
  6. package/dist/cloudflare.js +3 -3
  7. package/dist/index.d.cts +8 -4
  8. package/dist/index.d.ts +8 -4
  9. package/dist/lambda.cjs +11 -11
  10. package/dist/lambda.js +7 -7
  11. package/dist/loader.cjs +90 -54
  12. package/dist/slim-react/index.cjs +13 -13
  13. package/dist/slim-react/index.js +2 -2
  14. package/dist/slim-react/jsx-runtime.cjs +2 -4
  15. package/dist/slim-react/jsx-runtime.js +1 -1
  16. package/dist/ssr-render-worker.js +174 -161
  17. package/dist/ssr-watch.js +40 -74
  18. package/package.json +8 -10
  19. package/cli-lib.ts +0 -676
  20. package/cli.ts +0 -36
  21. package/index.ts +0 -17
  22. package/src/build.ts +0 -805
  23. package/src/cloudflare.ts +0 -140
  24. package/src/index.tsx +0 -41
  25. package/src/lambda.ts +0 -287
  26. package/src/slim-react/context.ts +0 -55
  27. package/src/slim-react/dispatcher.ts +0 -87
  28. package/src/slim-react/hooks.ts +0 -137
  29. package/src/slim-react/index.ts +0 -232
  30. package/src/slim-react/jsx-runtime.ts +0 -7
  31. package/src/slim-react/jsx.ts +0 -53
  32. package/src/slim-react/render.ts +0 -1101
  33. package/src/slim-react/renderContext.ts +0 -294
  34. package/src/slim-react/types.ts +0 -33
  35. package/src/source/context.ts +0 -113
  36. package/src/source/graphiql.ts +0 -101
  37. package/src/source/inference.ts +0 -260
  38. package/src/source/runner.ts +0 -138
  39. package/src/source/store.ts +0 -50
  40. package/src/ssr-render-worker.ts +0 -116
  41. package/src/ssr-watch.ts +0 -62
  42. package/src/static.ts +0 -109
  43. package/src/types/global.d.ts +0 -5
  44. package/src/types/hadars.ts +0 -350
  45. package/src/utils/Head.tsx +0 -462
  46. package/src/utils/clientScript.tsx +0 -71
  47. package/src/utils/cookies.ts +0 -16
  48. package/src/utils/loader.ts +0 -335
  49. package/src/utils/proxyHandler.tsx +0 -104
  50. package/src/utils/request.tsx +0 -9
  51. package/src/utils/response.tsx +0 -141
  52. package/src/utils/rspack.ts +0 -467
  53. package/src/utils/runtime.ts +0 -19
  54. package/src/utils/serve.ts +0 -155
  55. package/src/utils/ssrHandler.ts +0 -239
  56. package/src/utils/staticFile.ts +0 -43
  57. package/src/utils/template.html +0 -11
  58. package/src/utils/upgradeRequest.tsx +0 -19
package/src/static.ts DELETED
@@ -1,109 +0,0 @@
1
- /**
2
- * Static site export core — rendering and file I/O.
3
- *
4
- * Imported by cli-lib.ts (bundled into dist/cli.js via esbuild) and by tests.
5
- * Has no dependency on the rspack build pipeline — only SSR utilities.
6
- */
7
-
8
- import { cp, mkdir, writeFile } from 'node:fs/promises';
9
- import { join, dirname, basename } from 'node:path';
10
- import { parseRequest } from './utils/request';
11
- import { getReactResponse, buildHeadHtml } from './utils/response';
12
- import { buildSsrHtml, makePrecontentHtmlGetter } from './utils/ssrHandler';
13
- import type { HadarsEntryModule, HadarsStaticContext, GraphQLExecutor } from './types/hadars';
14
-
15
- export interface StaticRenderResult {
16
- /** URL paths that were successfully rendered. */
17
- rendered: string[];
18
- /** Paths that failed, with the caught error. */
19
- errors: Array<{ path: string; error: Error }>;
20
- }
21
-
22
- /**
23
- * Pre-renders a list of URL paths to HTML files and copies static assets.
24
- *
25
- * Pages are rendered serially — `getReactResponse` writes to
26
- * `globalThis.__hadarsUnsuspend / __hadarsContext` which are not re-entrant-safe.
27
- */
28
- export async function renderStaticSite(opts: {
29
- ssrModule: HadarsEntryModule<any>;
30
- htmlSource: string;
31
- staticSrc: string;
32
- paths: string[];
33
- outputDir: string;
34
- graphql?: GraphQLExecutor;
35
- }): Promise<StaticRenderResult> {
36
- const { ssrModule, htmlSource, staticSrc, paths, outputDir } = opts;
37
-
38
- const staticCtx: HadarsStaticContext = {
39
- graphql: opts.graphql ?? (() => Promise.reject(
40
- new Error('[hadars] No graphql executor configured. Add a `graphql` function to your hadars.config.'),
41
- )),
42
- };
43
- const getPrecontentHtml = makePrecontentHtmlGetter(Promise.resolve(htmlSource));
44
-
45
- await mkdir(outputDir, { recursive: true });
46
-
47
- const rendered: string[] = [];
48
- const errors: Array<{ path: string; error: Error }> = [];
49
-
50
- for (const urlPath of paths) {
51
- try {
52
- const req = parseRequest(new Request('http://localhost' + urlPath));
53
-
54
- // Expose the executor globally so useGraphQL() in components can reach it.
55
- (globalThis as any).__hadarsGraphQL = staticCtx.graphql;
56
-
57
- const { head, getAppBody, finalize } = await getReactResponse(req, {
58
- document: {
59
- body: ssrModule.default as any,
60
- getInitProps: ssrModule.getInitProps,
61
- getFinalProps: ssrModule.getFinalProps,
62
- },
63
- staticCtx,
64
- });
65
-
66
- const bodyHtml = await getAppBody();
67
- const { clientProps } = await finalize();
68
- const headHtml = buildHeadHtml(head);
69
- // Inject a flag so the client knows it's a static export and should
70
- // fetch index.json sidecars directly instead of hitting a live server.
71
- const staticClientProps = { ...clientProps, __hadarsStatic: true };
72
- const html = await buildSsrHtml(bodyHtml, staticClientProps, headHtml, getPrecontentHtml);
73
-
74
- // '/' → <outputDir>/index.html
75
- // '/about' → <outputDir>/about/index.html
76
- const cleanPath = urlPath.replace(/\/$/, '');
77
- const pageDir = cleanPath === '' ? outputDir : join(outputDir, cleanPath);
78
- await mkdir(pageDir, { recursive: true });
79
- await writeFile(join(pageDir, 'index.html'), html, 'utf-8');
80
-
81
- // Write a JSON sidecar so useServerData can hydrate on client-side
82
- // navigation without a live server. The format matches the live
83
- // server's Accept: application/json response: { serverData: {...} }.
84
- const serverData = (staticClientProps as any).__serverData ?? {};
85
- await writeFile(
86
- join(pageDir, 'index.json'),
87
- JSON.stringify({ serverData }),
88
- 'utf-8',
89
- );
90
-
91
- rendered.push(urlPath);
92
- } catch (err: any) {
93
- errors.push({
94
- path: urlPath,
95
- error: err instanceof Error ? err : new Error(String(err)),
96
- });
97
- }
98
- }
99
-
100
- // Copy .hadars/static/ → <outputDir>/static/, excluding the SSR template.
101
- const staticDest = join(outputDir, 'static');
102
- await mkdir(staticDest, { recursive: true });
103
- await cp(staticSrc, staticDest, {
104
- recursive: true,
105
- filter: (src: string) => basename(src) !== 'out.html',
106
- });
107
-
108
- return { rendered, errors };
109
- }
@@ -1,5 +0,0 @@
1
- declare module '$_MOD_PATH$' {
2
- const value: any;
3
- export default value;
4
- }
5
-
@@ -1,350 +0,0 @@
1
- import type { LinkHTMLAttributes, MetaHTMLAttributes, ScriptHTMLAttributes, StyleHTMLAttributes } from "react";
2
-
3
- /**
4
- * Minimal structural representation of a typed GraphQL document node.
5
- *
6
- * Compatible with `TypedDocumentNode` from `@graphql-typed-document-node/core`
7
- * and the documents emitted by graphql-codegen's `client` preset — so passing
8
- * a generated document object gives you fully-inferred result and variable types
9
- * without writing explicit generics.
10
- *
11
- * hadars intentionally avoids importing from `graphql` or
12
- * `@graphql-typed-document-node/core` so that neither is a required dependency.
13
- */
14
- export interface HadarsDocumentNode<
15
- TResult = Record<string, unknown>,
16
- TVariables = Record<string, unknown>,
17
- > {
18
- /** @internal Used by TypeScript to carry the result type. */
19
- readonly __apiType?: (variables: TVariables) => TResult;
20
- /** At least one definition — ensures a plain string is not assignable. */
21
- readonly definitions: ReadonlyArray<{ readonly kind: string }>;
22
- }
23
-
24
- /**
25
- * In-process GraphQL executor passed to `getInitProps` and `paths` during
26
- * `hadars export static`. Hadars is executor-agnostic — configure it in
27
- * `hadars.config.ts` using any GraphQL library (e.g. `graphql-js`):
28
- *
29
- * ```ts
30
- * import { graphql as gql, buildSchema } from 'graphql';
31
- * const schema = buildSchema(`type Query { hello: String }`);
32
- * const rootValue = { hello: () => 'world' };
33
- *
34
- * export default {
35
- * graphql: (query, variables) =>
36
- * gql({ schema, rootValue, source: query, variableValues: variables }),
37
- * } satisfies HadarsOptions;
38
- * ```
39
- *
40
- * The executor is generic — call it with explicit type parameters or pass a
41
- * `TypedDocumentNode` / codegen-generated document to get inferred types:
42
- *
43
- * ```ts
44
- * // Explicit generics:
45
- * const { data } = await ctx.graphql<GetPostQuery, GetPostQueryVariables>(
46
- * `query GetPost($slug: String) { blogPost(slug: $slug) { title } }`,
47
- * { slug },
48
- * );
49
- *
50
- * // Inferred via TypedDocumentNode (graphql-codegen client preset):
51
- * import { GetPostDocument } from './gql';
52
- * const { data } = await ctx.graphql(GetPostDocument, { slug });
53
- * ```
54
- */
55
- export type GraphQLExecutor = <
56
- TData = any,
57
- TVariables extends Record<string, unknown> = Record<string, unknown>,
58
- >(
59
- query: string | HadarsDocumentNode<TData, TVariables>,
60
- variables?: TVariables,
61
- ) => Promise<{ data?: TData; errors?: ReadonlyArray<{ message: string }> }>;
62
-
63
- /**
64
- * Context passed as the second argument to `getInitProps` and `paths`
65
- * during `hadars export static`. Not present in dev/run mode.
66
- */
67
- export interface HadarsStaticContext {
68
- graphql: GraphQLExecutor;
69
- }
70
-
71
- export type HadarsGetInitialProps<T extends {}> = (req: HadarsRequest, ctx?: HadarsStaticContext) => Promise<T> | T;
72
- export type HadarsGetClientProps<T extends {}> = (props: T) => Promise<T> | T;
73
- export type HadarsGetFinalProps<T extends {}> = (props: HadarsProps<T>) => Promise<T> | T;
74
- export type HadarsApp<T extends {}> = React.FC<HadarsProps<T>>;
75
-
76
- export type HadarsEntryModule<T extends {}> = {
77
- default: HadarsApp<T>;
78
- getInitProps?: HadarsGetInitialProps<T>;
79
- getFinalProps?: HadarsGetFinalProps<T>;
80
- getClientProps?: HadarsGetClientProps<T>;
81
- }
82
-
83
- export interface AppHead {
84
- title: string;
85
- status: number;
86
- meta: Record<string, MetaProps>;
87
- link: Record<string, LinkProps>;
88
- style: Record<string, StyleProps>;
89
- script: Record<string, ScriptProps>;
90
- }
91
-
92
- export type UnsuspendEntry =
93
- | { status: 'pending'; promise: Promise<unknown> }
94
- | { status: 'fulfilled'; value: unknown }
95
- | { status: 'rejected'; reason: unknown };
96
-
97
- /** @internal Populated by the framework's render loop — use useServerData() instead. */
98
- export interface AppUnsuspend {
99
- cache: Map<string, UnsuspendEntry>;
100
- }
101
-
102
- export interface AppContext {
103
- path?: string;
104
- head: AppHead;
105
- /** @internal Framework use only — use the useServerData() hook instead. */
106
- _unsuspend?: AppUnsuspend;
107
- }
108
-
109
- export type HadarsEntryBase = {
110
- location: string;
111
- }
112
-
113
- export type HadarsProps<T extends {}> = T & HadarsEntryBase;
114
-
115
- export type MetaProps = MetaHTMLAttributes<HTMLMetaElement>;
116
- export type LinkProps = LinkHTMLAttributes<HTMLLinkElement>;
117
- export type StyleProps = StyleHTMLAttributes<HTMLStyleElement>;
118
- export type ScriptProps = ScriptHTMLAttributes<HTMLScriptElement>;
119
-
120
- export interface HadarsOptions {
121
- port?: number;
122
- entry: string;
123
- baseURL?: string;
124
- // Optional SWC plugins to be applied to the swc-loader during compilation
125
- swcPlugins?: SwcPluginList;
126
- proxy?: Record<string, string> | ((req: HadarsRequest) => Promise<Response | null> | Response | null);
127
- proxyCORS?: boolean;
128
- define?: Record<string, string>;
129
- /**
130
- * Bun WebSocket handler passed directly to `Bun.serve()`.
131
- * Ignored on Node.js and Deno — use `fetch` + a third-party WS library there.
132
- * Pass a `Bun.WebSocketHandler` instance here when running on Bun.
133
- */
134
- websocket?: unknown;
135
- fetch?: (req: HadarsRequest) => Promise<Response | undefined> | Response | undefined;
136
- wsPath?: string;
137
- // Port for the rspack HMR dev server. Defaults to port + 1.
138
- hmrPort?: number;
139
- /**
140
- * Parallelism level for `run()` mode (production server). Defaults to 1.
141
- * Has no effect in `dev()` mode.
142
- *
143
- * **Node.js** — forks this many worker processes via `node:cluster`, each
144
- * binding to the same port via OS round-robin. Set to `os.cpus().length`
145
- * to saturate all CPU cores.
146
- *
147
- * **Bun / Deno** — creates a `node:worker_threads` render pool of this size.
148
- * Each thread handles the synchronous `renderToString` step, freeing the
149
- * main event loop for I/O.
150
- */
151
- workers?: number;
152
- /**
153
- * Override or extend rspack's `optimization` config for production client builds.
154
- * Merged on top of hadars defaults (splitChunks vendor splitting, deterministic moduleIds).
155
- * Has no effect on the SSR bundle or dev mode.
156
- */
157
- optimization?: Record<string, unknown>;
158
- /**
159
- * PostCSS plugins to pass to `postcss-loader`.
160
- *
161
- * When set, replaces the default `builtin:lightningcss-loader` with `postcss-loader`
162
- * configured with these plugins, allowing full PostCSS transforms (e.g. Tailwind CSS v4).
163
- *
164
- * @example
165
- * ```ts
166
- * import tailwindcss from '@tailwindcss/postcss';
167
- * export default { postcssPlugins: [tailwindcss()] } satisfies HadarsOptions;
168
- * ```
169
- */
170
- postcssPlugins?: any[];
171
- /**
172
- * Path to a custom HTML template file (relative to the project root).
173
- * Replaces the built-in minimal template used to generate the HTML shell.
174
- *
175
- * The file must include two marker elements so hadars can inject the
176
- * per-request head tags and the server-rendered body:
177
- *
178
- * ```html
179
- * <meta name="HADARS_HEAD"> <!-- replaced with <title>, <meta>, <link>, <style> tags -->
180
- * <meta name="HADARS_BODY"> <!-- replaced with the SSR-rendered React tree -->
181
- * ```
182
- *
183
- * Any `<style>` blocks in the template are automatically processed through
184
- * PostCSS (using the project's `postcss.config.js`) at build/dev startup time,
185
- * so `@import "tailwindcss"` and other PostCSS directives work as expected.
186
- * Note: inline styles are processed once at startup and are not live-reloaded.
187
- */
188
- htmlTemplate?: string;
189
- /**
190
- * Force the React runtime mode independently of the build mode.
191
- * Useful when you need production build optimizations (minification, tree-shaking)
192
- * but want React's development build for debugging hydration mismatches or
193
- * component stack traces.
194
- *
195
- * - `'development'` — forces `process.env.NODE_ENV = "development"` and enables
196
- * JSX source info even in `hadars build`. React prints detailed hydration error
197
- * messages and component stacks.
198
- * - `'production'` — the default; React uses the optimised production bundle.
199
- *
200
- * Only affects the **client** bundle. The SSR bundle always uses slim-react.
201
- *
202
- * @example
203
- * // hadars.config.ts — debug hydration errors in a production build
204
- * reactMode: 'development'
205
- */
206
- reactMode?: 'development' | 'production';
207
- /**
208
- * Additional rspack module rules appended to the built-in rule set.
209
- * Applied to both the client and the SSR bundle.
210
- *
211
- * Useful for loaders not included by default, such as `@mdx-js/loader`,
212
- * `less-loader`, `yaml-loader`, etc.
213
- *
214
- * @example
215
- * moduleRules: [
216
- * {
217
- * test: /\.mdx?$/,
218
- * use: [{ loader: '@mdx-js/loader' }],
219
- * },
220
- * ]
221
- */
222
- moduleRules?: Record<string, any>[];
223
- /**
224
- * Additional rspack/webpack-compatible plugins applied to both the client
225
- * and SSR bundles. Any object that implements the `apply(compiler)` method
226
- * (the standard webpack/rspack plugin interface) is accepted.
227
- *
228
- * @example
229
- * import { SubresourceIntegrityPlugin } from 'webpack-subresource-integrity';
230
- * plugins: [new SubresourceIntegrityPlugin()]
231
- */
232
- plugins?: Array<{ apply(compiler: any): void }>;
233
- /**
234
- * SSR response cache for `run()` mode. Has no effect in `dev()` mode.
235
- *
236
- * Receives the incoming request and should return `{ key, ttl? }` to cache
237
- * the response, or `null`/`undefined` to skip caching for that request.
238
- * `ttl` is the time-to-live in milliseconds; omit for entries that never expire.
239
- * The function may be async.
240
- *
241
- * @example
242
- * // Cache every page by pathname (no per-user personalisation):
243
- * cache: (req) => ({ key: req.pathname })
244
- *
245
- * @example
246
- * // Cache with a per-route TTL, skip authenticated requests:
247
- * cache: (req) => req.cookies.session ? null : { key: req.pathname, ttl: 60_000 }
248
- */
249
- cache?: (req: HadarsRequest) => { key: string; ttl?: number } | null | undefined
250
- | Promise<{ key: string; ttl?: number } | null | undefined>;
251
- /**
252
- * Static export path list. Required for `hadars export static`.
253
- *
254
- * Return an array of URL paths (e.g. `['/', '/about', '/blog/hello']`) that
255
- * should be pre-rendered to HTML files. May be async.
256
- *
257
- * @example
258
- * paths: () => ['/', '/about', '/contact']
259
- *
260
- * @example
261
- * paths: async () => {
262
- * const posts = await fetchBlogPosts();
263
- * return ['/', ...posts.map(p => `/blog/${p.slug}`)];
264
- * }
265
- */
266
- /**
267
- * In-process GraphQL executor. Supply this to use the GraphQL data layer
268
- * in `paths` and `getInitProps` during `hadars export static`.
269
- * Has no effect in `dev` / `run` mode.
270
- */
271
- graphql?: GraphQLExecutor;
272
- paths?: (ctx: HadarsStaticContext) => Promise<string[]> | string[];
273
- /**
274
- * Gatsby-compatible source plugins to run before `hadars export static`.
275
- *
276
- * Each entry mirrors Gatsby's `gatsby-config.js` plugin object format:
277
- * `{ resolve: 'gatsby-source-contentful', options: { spaceId: '...', accessToken: '...' } }`
278
- *
279
- * The plugin must export a `sourceNodes` function with the standard Gatsby API.
280
- * Hadars provides a thin shim covering the most-used surface:
281
- * `actions.createNode`, `createNodeId`, `createContentDigest`, `cache`, `reporter`,
282
- * `getNode`, `getNodes`, `getNodesByType`.
283
- *
284
- * After all sources have run, hadars auto-generates a GraphQL schema from the
285
- * collected nodes and makes it available via `config.graphql` in `getInitProps`
286
- * and `paths`. Requires `graphql` to be installed in the project.
287
- *
288
- * @example
289
- * sources: [
290
- * {
291
- * resolve: 'gatsby-source-filesystem',
292
- * options: { name: 'posts', path: './content/posts' },
293
- * },
294
- * ]
295
- */
296
- sources?: HadarsSourceEntry[];
297
- /**
298
- * Called whenever an SSR render error is caught, in both `dev()` and `run()` mode
299
- * as well as the Lambda and Cloudflare adapters.
300
- *
301
- * Use this to forward errors to your monitoring service (Sentry, Datadog, etc.)
302
- * without affecting the response sent to the browser.
303
- * The handler may be async — hadars fires it and does not await the result,
304
- * so it never delays the error response.
305
- *
306
- * @example
307
- * import * as Sentry from '@sentry/node';
308
- * onError: (err, req) => Sentry.captureException(err, { extra: { url: req.url } })
309
- *
310
- * @example
311
- * onError: (err, req) => console.error('[myapp]', req.method, req.url, err)
312
- */
313
- onError?: (err: Error, req: Request) => void | Promise<void>;
314
- }
315
-
316
- /**
317
- * A Gatsby-compatible source plugin entry, matching the format used in
318
- * `gatsby-config.js` / `gatsby-config.ts`.
319
- */
320
- export interface HadarsSourceEntry {
321
- /**
322
- * Package name (e.g. `'gatsby-source-contentful'`) or a pre-imported module
323
- * object that exports `sourceNodes`. Using a module object lets you pass
324
- * local source plugins without publishing them to npm.
325
- */
326
- resolve: string | { sourceNodes?: (ctx: any, opts?: any) => Promise<void> | void };
327
- /** Plugin options forwarded as the second argument to `sourceNodes`. */
328
- options?: Record<string, unknown>;
329
- }
330
-
331
-
332
- // SWC plugin typing — a pragmatic, ergonomic union that matches common usages:
333
- // - a plugin package name (string)
334
- // - a tuple [pluginName, options]
335
- // - an object with a path and optional options
336
- // - a direct function (for programmatic plugin instances)
337
- export type SwcPluginItem =
338
- | string
339
- | [string, Record<string, unknown>]
340
- | { path: string; options?: Record<string, unknown> }
341
- | ((...args: any[]) => any);
342
-
343
- export type SwcPluginList = SwcPluginItem[];
344
-
345
- export interface HadarsRequest extends Request {
346
- pathname: string;
347
- search: string;
348
- location: string;
349
- cookies: Record<string, string>;
350
- }