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/cli.js ADDED
@@ -0,0 +1,1441 @@
1
+ #!/usr/bin/env node
2
+
3
+ // cli-lib.ts
4
+ import { existsSync as existsSync3 } from "node:fs";
5
+ import { mkdir, writeFile } from "node:fs/promises";
6
+ import { resolve, join } from "node:path";
7
+
8
+ // src/utils/proxyHandler.tsx
9
+ var cloneHeaders = (headers) => {
10
+ return new Headers(headers);
11
+ };
12
+ var getCORSHeaders = (req) => {
13
+ const origin = req.headers.get("Origin") || "*";
14
+ return {
15
+ "Access-Control-Allow-Origin": origin,
16
+ "Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
17
+ "Access-Control-Allow-Headers": req.headers.get("Access-Control-Request-Headers") || "*",
18
+ "Access-Control-Allow-Credentials": "true"
19
+ };
20
+ };
21
+ var createProxyHandler = (options) => {
22
+ const { proxy, proxyCORS } = options;
23
+ if (!proxy) {
24
+ return () => void 0;
25
+ }
26
+ if (typeof proxy === "function") {
27
+ return async (req) => {
28
+ if (req.method === "OPTIONS" && options.proxyCORS) {
29
+ return new Response(null, {
30
+ status: 204,
31
+ headers: getCORSHeaders(req)
32
+ });
33
+ }
34
+ const res = await proxy(req);
35
+ if (res && proxyCORS) {
36
+ const modifiedHeaders = new Headers(res.headers);
37
+ Object.entries(getCORSHeaders(req)).forEach(([key, value]) => {
38
+ modifiedHeaders.set(key, value);
39
+ });
40
+ return new Response(res.body, {
41
+ status: res.status,
42
+ statusText: res.statusText,
43
+ headers: modifiedHeaders
44
+ });
45
+ }
46
+ return res || void 0;
47
+ };
48
+ }
49
+ const proxyRules = Object.entries(proxy).sort((a, b) => b[0].length - a[0].length);
50
+ return async (req) => {
51
+ if (req.method === "OPTIONS" && options.proxyCORS) {
52
+ return new Response(null, {
53
+ status: 204,
54
+ headers: getCORSHeaders(req)
55
+ });
56
+ }
57
+ for (const [path2, target] of proxyRules) {
58
+ if (req.pathname.startsWith(path2)) {
59
+ const targetURL = new URL(target);
60
+ targetURL.pathname = targetURL.pathname.replace(/\/$/, "") + req.pathname.slice(path2.length);
61
+ targetURL.search = req.search;
62
+ const sendHeaders = cloneHeaders(req.headers);
63
+ sendHeaders.set("Host", targetURL.host);
64
+ const proxyReq = new Request(targetURL.toString(), {
65
+ method: req.method,
66
+ headers: sendHeaders,
67
+ body: ["GET", "HEAD"].includes(req.method) ? void 0 : req.body,
68
+ redirect: "follow"
69
+ });
70
+ const res = await fetch(proxyReq);
71
+ if (proxyCORS) {
72
+ Object.entries(getCORSHeaders(req)).forEach(([key, value]) => {
73
+ res.headers.set(key, value);
74
+ });
75
+ }
76
+ const body = await res.arrayBuffer();
77
+ const clonedRes = new Headers(res.headers);
78
+ clonedRes.delete("content-length");
79
+ clonedRes.delete("content-encoding");
80
+ return new Response(body, {
81
+ status: res.status,
82
+ statusText: res.statusText,
83
+ headers: clonedRes
84
+ });
85
+ }
86
+ }
87
+ return void 0;
88
+ };
89
+ };
90
+
91
+ // src/utils/cookies.ts
92
+ var parseCookies = (cookieString) => {
93
+ const cookies = {};
94
+ if (!cookieString) {
95
+ return cookies;
96
+ }
97
+ const pairs = cookieString.split(";");
98
+ for (const pair of pairs) {
99
+ const index = pair.indexOf("=");
100
+ if (index > -1) {
101
+ const key = pair.slice(0, index).trim();
102
+ const value = pair.slice(index + 1).trim();
103
+ cookies[key] = decodeURIComponent(value);
104
+ }
105
+ }
106
+ return cookies;
107
+ };
108
+
109
+ // src/utils/request.tsx
110
+ var parseRequest = (request) => {
111
+ const url = new URL(request.url);
112
+ const cookies = request.headers.get("Cookie") || "";
113
+ const cookieRecord = parseCookies(cookies);
114
+ return Object.assign(request, { pathname: url.pathname, search: url.search, location: url.pathname + url.search, cookies: cookieRecord });
115
+ };
116
+
117
+ // src/utils/upgradeRequest.tsx
118
+ var upgradeHandler = (options) => {
119
+ const { wsPath = "/ws" } = options;
120
+ if (options.websocket) {
121
+ return (req, ctx) => {
122
+ if (req.pathname === wsPath) {
123
+ return ctx.upgrade(req);
124
+ }
125
+ return false;
126
+ };
127
+ }
128
+ return null;
129
+ };
130
+
131
+ // src/utils/response.tsx
132
+ import { createRequire } from "node:module";
133
+ import pathMod from "node:path";
134
+ import { pathToFileURL } from "node:url";
135
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
136
+ var _renderToStaticMarkup = null;
137
+ async function getStaticMarkupRenderer() {
138
+ if (!_renderToStaticMarkup) {
139
+ const req = createRequire(pathMod.resolve(process.cwd(), "__hadars_fake__.js"));
140
+ const resolved = req.resolve("react-dom/server");
141
+ const mod = await import(pathToFileURL(resolved).href);
142
+ _renderToStaticMarkup = mod.renderToStaticMarkup;
143
+ }
144
+ return _renderToStaticMarkup;
145
+ }
146
+ var getHeadHtml = (seoData, renderToStaticMarkup) => {
147
+ const metaEntries = Object.entries(seoData.meta);
148
+ const linkEntries = Object.entries(seoData.link);
149
+ const styleEntries = Object.entries(seoData.style);
150
+ const scriptEntries = Object.entries(seoData.script);
151
+ return renderToStaticMarkup(
152
+ /* @__PURE__ */ jsxs(Fragment, { children: [
153
+ /* @__PURE__ */ jsx("title", { children: seoData.title }),
154
+ metaEntries.map(([id, options]) => /* @__PURE__ */ jsx(
155
+ "meta",
156
+ {
157
+ id,
158
+ ...options
159
+ },
160
+ id
161
+ )),
162
+ linkEntries.map(([id, options]) => /* @__PURE__ */ jsx(
163
+ "link",
164
+ {
165
+ id,
166
+ ...options
167
+ },
168
+ id
169
+ )),
170
+ styleEntries.map(([id, options]) => /* @__PURE__ */ jsx(
171
+ "style",
172
+ {
173
+ id,
174
+ ...options
175
+ },
176
+ id
177
+ )),
178
+ scriptEntries.map(([id, options]) => /* @__PURE__ */ jsx(
179
+ "script",
180
+ {
181
+ id,
182
+ ...options
183
+ },
184
+ id
185
+ ))
186
+ ] })
187
+ );
188
+ };
189
+ var getReactResponse = async (req, opts) => {
190
+ const App = opts.document.body;
191
+ const { getInitProps, getAfterRenderProps, getFinalProps } = opts.document;
192
+ const renderToStaticMarkup = await getStaticMarkupRenderer();
193
+ const unsuspend = {
194
+ cache: /* @__PURE__ */ new Map(),
195
+ hasPending: false
196
+ };
197
+ const processUnsuspend = async () => {
198
+ const pending = [...unsuspend.cache.values()].filter((e) => e.status === "pending").map((e) => e.promise);
199
+ await Promise.all(pending);
200
+ };
201
+ const context = {
202
+ head: {
203
+ title: "Hadars App",
204
+ meta: {},
205
+ link: {},
206
+ style: {},
207
+ script: {},
208
+ status: 200
209
+ },
210
+ _unsuspend: unsuspend
211
+ };
212
+ let props = {
213
+ ...getInitProps ? await getInitProps(req) : {},
214
+ location: req.location,
215
+ context
216
+ };
217
+ let html = "";
218
+ let iters = 0;
219
+ do {
220
+ unsuspend.hasPending = false;
221
+ try {
222
+ globalThis.__hadarsUnsuspend = unsuspend;
223
+ html = renderToStaticMarkup(/* @__PURE__ */ jsx(App, { ...props }));
224
+ } finally {
225
+ globalThis.__hadarsUnsuspend = null;
226
+ }
227
+ if (unsuspend.hasPending)
228
+ await processUnsuspend();
229
+ } while (unsuspend.hasPending && ++iters < 25);
230
+ props = getAfterRenderProps ? await getAfterRenderProps(props, html) : props;
231
+ try {
232
+ globalThis.__hadarsUnsuspend = unsuspend;
233
+ renderToStaticMarkup(
234
+ /* @__PURE__ */ jsx(App, { ...{
235
+ ...props,
236
+ location: req.location,
237
+ context
238
+ } })
239
+ );
240
+ } finally {
241
+ globalThis.__hadarsUnsuspend = null;
242
+ }
243
+ const serverData = {};
244
+ for (const [k, v] of unsuspend.cache) {
245
+ if (v.status === "fulfilled")
246
+ serverData[k] = v.value;
247
+ }
248
+ const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
249
+ const clientProps = {
250
+ ...restProps,
251
+ location: req.location,
252
+ ...Object.keys(serverData).length > 0 ? { __serverData: serverData } : {}
253
+ };
254
+ const ReactPage = /* @__PURE__ */ jsxs(Fragment, { children: [
255
+ /* @__PURE__ */ jsx("div", { id: "app", children: /* @__PURE__ */ jsx(App, { ...{
256
+ ...props,
257
+ location: req.location,
258
+ context
259
+ } }) }),
260
+ /* @__PURE__ */ jsx("script", { id: "hadars", type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify({ hadars: { props: clientProps } }) } })
261
+ ] });
262
+ return {
263
+ ReactPage,
264
+ status: context.head.status,
265
+ headHtml: getHeadHtml(context.head, renderToStaticMarkup),
266
+ renderPayload: {
267
+ appProps: { ...props, location: req.location, context },
268
+ clientProps
269
+ }
270
+ };
271
+ };
272
+
273
+ // src/utils/rspack.ts
274
+ import rspack from "@rspack/core";
275
+ import ReactRefreshPlugin from "@rspack/plugin-react-refresh";
276
+ import path from "node:path";
277
+ import { fileURLToPath } from "node:url";
278
+ import pathMod2 from "node:path";
279
+ import { existsSync } from "node:fs";
280
+ var __dirname = process.cwd();
281
+ var packageDir = pathMod2.dirname(fileURLToPath(import.meta.url));
282
+ var clientScriptPath = pathMod2.resolve(packageDir, "template.html");
283
+ var loaderPath = existsSync(pathMod2.resolve(packageDir, "loader.cjs")) ? pathMod2.resolve(packageDir, "loader.cjs") : pathMod2.resolve(packageDir, "loader.ts");
284
+ var getConfigBase = (mode) => {
285
+ const isDev = mode === "development";
286
+ return {
287
+ experiments: {
288
+ css: true,
289
+ outputModule: true
290
+ },
291
+ resolve: {
292
+ modules: [
293
+ path.resolve(__dirname, "node_modules"),
294
+ // 'node_modules' (relative) enables the standard upward-traversal
295
+ // resolution so rspack can find transitive deps (e.g. webpack-dev-server)
296
+ // that live in a parent node_modules when running from a sub-project.
297
+ "node_modules"
298
+ ],
299
+ tsConfig: path.resolve(__dirname, "tsconfig.json"),
300
+ extensions: [".tsx", ".ts", ".js", ".jsx"]
301
+ },
302
+ module: {
303
+ rules: [
304
+ {
305
+ test: /\.mdx?$/,
306
+ use: [
307
+ {
308
+ loader: "@mdx-js/loader",
309
+ options: {}
310
+ }
311
+ ]
312
+ },
313
+ {
314
+ test: /\.css$/,
315
+ use: ["postcss-loader"],
316
+ type: "css"
317
+ },
318
+ {
319
+ test: /\.svg$/i,
320
+ issuer: /\.[jt]sx?$/,
321
+ use: ["@svgr/webpack"]
322
+ },
323
+ {
324
+ test: /\.m?jsx?$/,
325
+ resolve: {
326
+ fullySpecified: false
327
+ },
328
+ exclude: [loaderPath],
329
+ use: [
330
+ // Transforms loadModule('./path') based on build target.
331
+ // Runs before swc-loader (loaders execute right-to-left).
332
+ {
333
+ loader: loaderPath
334
+ },
335
+ {
336
+ loader: "builtin:swc-loader",
337
+ options: {
338
+ jsc: {
339
+ parser: {
340
+ syntax: "ecmascript",
341
+ jsx: true
342
+ },
343
+ transform: {
344
+ react: {
345
+ runtime: "automatic",
346
+ development: isDev,
347
+ refresh: isDev
348
+ }
349
+ }
350
+ }
351
+ }
352
+ }
353
+ ],
354
+ type: "javascript/auto"
355
+ },
356
+ {
357
+ test: /\.tsx?$/,
358
+ resolve: {
359
+ fullySpecified: false
360
+ },
361
+ exclude: [loaderPath],
362
+ use: [
363
+ {
364
+ loader: loaderPath
365
+ },
366
+ {
367
+ loader: "builtin:swc-loader",
368
+ options: {
369
+ jsc: {
370
+ parser: {
371
+ syntax: "typescript",
372
+ tsx: true
373
+ },
374
+ transform: {
375
+ react: {
376
+ runtime: "automatic",
377
+ development: isDev,
378
+ refresh: isDev
379
+ }
380
+ }
381
+ }
382
+ }
383
+ }
384
+ ],
385
+ type: "javascript/auto"
386
+ }
387
+ ]
388
+ }
389
+ };
390
+ };
391
+ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
392
+ const Config = getConfigBase(opts.mode);
393
+ const { base } = opts;
394
+ const isDev = opts.mode === "development";
395
+ const localConfig = {
396
+ ...Config,
397
+ module: {
398
+ ...Config.module,
399
+ rules: (Config.module && Array.isArray(Config.module.rules) ? Config.module.rules : []).map((r) => {
400
+ const nr = { ...r };
401
+ if (r && Array.isArray(r.use)) {
402
+ nr.use = r.use.map((u) => ({ ...typeof u === "object" ? u : { loader: u } }));
403
+ }
404
+ return nr;
405
+ })
406
+ }
407
+ };
408
+ if (opts.swcPlugins && Array.isArray(opts.swcPlugins) && opts.swcPlugins.length > 0) {
409
+ const rules = localConfig.module && localConfig.module.rules;
410
+ if (Array.isArray(rules)) {
411
+ for (const rule of rules) {
412
+ const ruleUse = rule;
413
+ if (ruleUse.use && Array.isArray(ruleUse.use)) {
414
+ for (const entry2 of ruleUse.use) {
415
+ const useEntry = entry2;
416
+ if (useEntry && useEntry.loader && typeof useEntry.loader === "string" && useEntry.loader.includes("swc-loader")) {
417
+ const options = useEntry.options || {};
418
+ useEntry.options = options;
419
+ useEntry.options.jsc = useEntry.options.jsc || {};
420
+ useEntry.options.jsc.experimental = useEntry.options.jsc.experimental || {};
421
+ useEntry.options.jsc.experimental.runPluginFirst = true;
422
+ const existingPlugins = Array.isArray(useEntry.options.jsc.experimental.plugins) ? useEntry.options.jsc.experimental.plugins : [];
423
+ const incomingPlugins = Array.isArray(opts.swcPlugins) ? opts.swcPlugins : [];
424
+ const seen = /* @__PURE__ */ new Set();
425
+ const merged = [];
426
+ for (const p of existingPlugins.concat(incomingPlugins)) {
427
+ const name = Array.isArray(p) && p.length > 0 ? String(p[0]) : String(p);
428
+ if (!seen.has(name)) {
429
+ seen.add(name);
430
+ merged.push(p);
431
+ }
432
+ }
433
+ useEntry.options.jsc.experimental.plugins = merged;
434
+ }
435
+ }
436
+ }
437
+ }
438
+ }
439
+ }
440
+ const isServerBuild = Boolean(
441
+ opts.output && typeof opts.output === "object" && (opts.output.library || String(opts.output.filename || "").includes("ssr"))
442
+ );
443
+ const resolveAliases = isServerBuild ? {
444
+ // force all react imports to resolve to this project's react
445
+ react: path.resolve(process.cwd(), "node_modules", "react"),
446
+ "react-dom": path.resolve(process.cwd(), "node_modules", "react-dom"),
447
+ // also map react/jsx-runtime to avoid duplicates when automatic runtime is used
448
+ "react/jsx-runtime": path.resolve(process.cwd(), "node_modules", "react", "jsx-runtime.js"),
449
+ "react/jsx-dev-runtime": path.resolve(process.cwd(), "node_modules", "react", "jsx-dev-runtime.js"),
450
+ // ensure emotion packages resolve to the project's node_modules so we don't pick up a browser-specific entry
451
+ "@emotion/react": path.resolve(process.cwd(), "node_modules", "@emotion", "react"),
452
+ "@emotion/server": path.resolve(process.cwd(), "node_modules", "@emotion", "server"),
453
+ "@emotion/cache": path.resolve(process.cwd(), "node_modules", "@emotion", "cache"),
454
+ "@emotion/styled": path.resolve(process.cwd(), "node_modules", "@emotion", "styled")
455
+ } : void 0;
456
+ const externals = isServerBuild ? [
457
+ "react",
458
+ "react-dom",
459
+ // keep common aliases external as well
460
+ "react/jsx-runtime",
461
+ "react/jsx-dev-runtime",
462
+ // emotion should be external on server builds to avoid client/browser code
463
+ "@emotion/react",
464
+ "@emotion/server",
465
+ "@emotion/cache",
466
+ "@emotion/styled"
467
+ ] : void 0;
468
+ const extraPlugins = [];
469
+ if (opts.define && typeof opts.define === "object") {
470
+ const DefinePlugin = rspack.DefinePlugin || rspack.plugins?.DefinePlugin;
471
+ if (DefinePlugin) {
472
+ extraPlugins.push(new DefinePlugin(opts.define));
473
+ } else {
474
+ extraPlugins.push({ name: "DefinePlugin", value: opts.define });
475
+ }
476
+ }
477
+ const resolveConfig = {
478
+ extensions: [".tsx", ".ts", ".js", ".jsx"],
479
+ alias: resolveAliases,
480
+ // for server builds prefer the package "main"/"module" fields and avoid "browser" so we don't pick browser-specific entrypoints
481
+ mainFields: isServerBuild ? ["main", "module"] : ["browser", "module", "main"]
482
+ };
483
+ return {
484
+ entry,
485
+ resolve: resolveConfig,
486
+ output: {
487
+ ...opts.output,
488
+ clean: false
489
+ },
490
+ mode: opts.mode,
491
+ externals,
492
+ plugins: [
493
+ new rspack.HtmlRspackPlugin({
494
+ publicPath: base || "/",
495
+ template: clientScriptPath,
496
+ scriptLoading: "module",
497
+ filename: "out.html",
498
+ inject: "body"
499
+ }),
500
+ isDev && new ReactRefreshPlugin(),
501
+ includeHotPlugin && isDev && new rspack.HotModuleReplacementPlugin(),
502
+ ...extraPlugins
503
+ ],
504
+ ...localConfig,
505
+ // HMR is not implemented for module chunk format, so disable outputModule
506
+ // for client builds. SSR builds still need it for dynamic import() of exports.
507
+ experiments: {
508
+ ...localConfig.experiments || {},
509
+ outputModule: isServerBuild
510
+ }
511
+ };
512
+ };
513
+ var createClientCompiler = (entry, opts) => {
514
+ return rspack(buildCompilerConfig(entry, opts, false));
515
+ };
516
+ var compileEntry = async (entry, opts) => {
517
+ const compiler = rspack(buildCompilerConfig(entry, opts, true));
518
+ if (opts.watch) {
519
+ await new Promise((resolve2, reject) => {
520
+ let first = true;
521
+ compiler.watch({}, (err, stats) => {
522
+ if (err) {
523
+ if (first) {
524
+ first = false;
525
+ reject(err);
526
+ } else {
527
+ console.error("rspack watch error", err);
528
+ }
529
+ return;
530
+ }
531
+ console.log(stats?.toString({
532
+ colors: true,
533
+ modules: true,
534
+ children: true,
535
+ chunks: true,
536
+ chunkModules: true
537
+ }));
538
+ if (first) {
539
+ first = false;
540
+ resolve2(stats);
541
+ } else {
542
+ try {
543
+ opts.onChange && opts.onChange(stats);
544
+ } catch (e) {
545
+ console.error("onChange handler error", e);
546
+ }
547
+ }
548
+ });
549
+ });
550
+ return;
551
+ }
552
+ await new Promise((resolve2, reject) => {
553
+ compiler.run((err, stats) => {
554
+ if (err) {
555
+ reject(err);
556
+ return;
557
+ }
558
+ console.log(stats?.toString({
559
+ colors: true,
560
+ modules: true,
561
+ children: true,
562
+ chunks: true,
563
+ chunkModules: true
564
+ }));
565
+ resolve2(stats);
566
+ });
567
+ });
568
+ };
569
+
570
+ // src/utils/runtime.ts
571
+ var isBun = typeof globalThis.Bun !== "undefined";
572
+ var isDeno = typeof globalThis.Deno !== "undefined";
573
+ var isNode = !isBun && !isDeno;
574
+
575
+ // src/utils/serve.ts
576
+ function nodeReadableToWebStream(readable) {
577
+ const enc = new TextEncoder();
578
+ return new ReadableStream({
579
+ start(controller) {
580
+ readable.on("data", (chunk) => {
581
+ controller.enqueue(
582
+ typeof chunk === "string" ? enc.encode(chunk) : new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)
583
+ );
584
+ });
585
+ readable.on("end", () => controller.close());
586
+ readable.on("error", (err) => controller.error(err));
587
+ },
588
+ cancel() {
589
+ readable.destroy?.();
590
+ }
591
+ });
592
+ }
593
+ var noopCtx = { upgrade: () => false };
594
+ async function serve(port, fetchHandler, websocket) {
595
+ if (isBun) {
596
+ globalThis.Bun.serve({
597
+ port,
598
+ websocket,
599
+ async fetch(req, server2) {
600
+ const ctx = { upgrade: (r) => server2.upgrade(r) };
601
+ return await fetchHandler(req, ctx) ?? void 0;
602
+ }
603
+ });
604
+ return;
605
+ }
606
+ if (isDeno) {
607
+ globalThis.Deno.serve({
608
+ port,
609
+ handler: async (req) => {
610
+ const res = await fetchHandler(req, noopCtx);
611
+ return res ?? new Response("Not Found", { status: 404 });
612
+ }
613
+ });
614
+ return;
615
+ }
616
+ const { createServer } = await import("node:http");
617
+ const server = createServer(async (nodeReq, nodeRes) => {
618
+ try {
619
+ const chunks = [];
620
+ if (!["GET", "HEAD"].includes(nodeReq.method ?? "GET")) {
621
+ for await (const chunk of nodeReq) {
622
+ chunks.push(chunk);
623
+ }
624
+ }
625
+ const body = chunks.length > 0 ? Buffer.concat(chunks) : void 0;
626
+ const url = `http://localhost:${port}${nodeReq.url ?? "/"}`;
627
+ const reqInit = {
628
+ method: nodeReq.method ?? "GET",
629
+ headers: new Headers(nodeReq.headers)
630
+ };
631
+ if (body) {
632
+ reqInit.body = body;
633
+ reqInit.duplex = "half";
634
+ }
635
+ const req = new Request(url, reqInit);
636
+ const res = await fetchHandler(req, noopCtx);
637
+ const response = res ?? new Response("Not Found", { status: 404 });
638
+ const headers = {};
639
+ response.headers.forEach((v, k) => {
640
+ headers[k] = v;
641
+ });
642
+ nodeRes.writeHead(response.status, headers);
643
+ if (response.body) {
644
+ const reader = response.body.getReader();
645
+ while (true) {
646
+ const { done, value } = await reader.read();
647
+ if (done)
648
+ break;
649
+ await new Promise(
650
+ (resolve2, reject) => nodeRes.write(value, (err) => err ? reject(err) : resolve2())
651
+ );
652
+ }
653
+ }
654
+ } catch (err) {
655
+ console.error("[hadars] request error", err);
656
+ if (!nodeRes.headersSent)
657
+ nodeRes.writeHead(500);
658
+ } finally {
659
+ nodeRes.end();
660
+ }
661
+ });
662
+ await new Promise((resolve2, reject) => {
663
+ server.listen(port, () => resolve2());
664
+ server.once("error", reject);
665
+ });
666
+ }
667
+
668
+ // src/utils/staticFile.ts
669
+ import { readFile, stat } from "node:fs/promises";
670
+ var MIME = {
671
+ html: "text/html; charset=utf-8",
672
+ htm: "text/html; charset=utf-8",
673
+ css: "text/css",
674
+ js: "application/javascript",
675
+ mjs: "application/javascript",
676
+ cjs: "application/javascript",
677
+ json: "application/json",
678
+ map: "application/json",
679
+ png: "image/png",
680
+ jpg: "image/jpeg",
681
+ jpeg: "image/jpeg",
682
+ gif: "image/gif",
683
+ webp: "image/webp",
684
+ svg: "image/svg+xml",
685
+ ico: "image/x-icon",
686
+ woff: "font/woff",
687
+ woff2: "font/woff2",
688
+ ttf: "font/ttf",
689
+ otf: "font/otf",
690
+ txt: "text/plain",
691
+ xml: "application/xml",
692
+ pdf: "application/pdf"
693
+ };
694
+ async function tryServeFile(filePath) {
695
+ try {
696
+ await stat(filePath);
697
+ } catch {
698
+ return null;
699
+ }
700
+ try {
701
+ const data = await readFile(filePath);
702
+ const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
703
+ const contentType = MIME[ext] ?? "application/octet-stream";
704
+ return new Response(data, { headers: { "Content-Type": contentType } });
705
+ } catch {
706
+ return null;
707
+ }
708
+ }
709
+
710
+ // src/build.ts
711
+ import { RspackDevServer } from "@rspack/dev-server";
712
+ import pathMod3 from "node:path";
713
+ import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
714
+ import { createRequire as createRequire2 } from "node:module";
715
+ import crypto from "node:crypto";
716
+ import fs from "node:fs/promises";
717
+ import { existsSync as existsSync2 } from "node:fs";
718
+ import os from "node:os";
719
+ import { spawn } from "node:child_process";
720
+ import cluster from "node:cluster";
721
+ var encoder = new TextEncoder();
722
+ var HEAD_MARKER = '<meta name="NINETY_HEAD">';
723
+ var BODY_MARKER = '<meta name="NINETY_BODY">';
724
+ var _renderToReadableStream = null;
725
+ async function getReadableStreamRenderer() {
726
+ if (!_renderToReadableStream) {
727
+ const req = createRequire2(pathMod3.resolve(process.cwd(), "__hadars_fake__.js"));
728
+ const resolved = req.resolve("react-dom/server.browser");
729
+ const mod = await import(pathToFileURL2(resolved).href);
730
+ _renderToReadableStream = mod.renderToReadableStream;
731
+ }
732
+ return _renderToReadableStream;
733
+ }
734
+ var _renderToString = null;
735
+ async function getRenderToString() {
736
+ if (!_renderToString) {
737
+ const req = createRequire2(pathMod3.resolve(process.cwd(), "__hadars_fake__.js"));
738
+ const resolved = req.resolve("react-dom/server");
739
+ const mod = await import(pathToFileURL2(resolved).href);
740
+ _renderToString = mod.renderToString;
741
+ }
742
+ return _renderToString;
743
+ }
744
+ var RenderWorkerPool = class {
745
+ workers = [];
746
+ pending = /* @__PURE__ */ new Map();
747
+ nextId = 0;
748
+ rrIndex = 0;
749
+ constructor(workerPath, size, ssrBundlePath) {
750
+ this._init(workerPath, size, ssrBundlePath);
751
+ }
752
+ _init(workerPath, size, ssrBundlePath) {
753
+ import("node:worker_threads").then(({ Worker }) => {
754
+ for (let i = 0; i < size; i++) {
755
+ const w = new Worker(workerPath, { workerData: { ssrBundlePath } });
756
+ w.on("message", (msg) => {
757
+ const { id, type, html, error, chunk } = msg;
758
+ const p = this.pending.get(id);
759
+ if (!p)
760
+ return;
761
+ if (p.kind === "renderStream") {
762
+ if (type === "chunk") {
763
+ p.controller.enqueue(chunk);
764
+ return;
765
+ }
766
+ this.pending.delete(id);
767
+ if (type === "done")
768
+ p.controller.close();
769
+ else
770
+ p.controller.error(new Error(error ?? "Stream error"));
771
+ return;
772
+ }
773
+ this.pending.delete(id);
774
+ if (error)
775
+ p.reject(new Error(error));
776
+ else
777
+ p.resolve(html);
778
+ });
779
+ w.on("error", (err) => {
780
+ console.error("[hadars] Render worker error:", err);
781
+ });
782
+ this.workers.push(w);
783
+ }
784
+ }).catch((err) => {
785
+ console.error("[hadars] Failed to initialise render worker pool:", err);
786
+ });
787
+ }
788
+ nextWorker() {
789
+ const w = this.workers[this.rrIndex % this.workers.length];
790
+ this.rrIndex++;
791
+ return w;
792
+ }
793
+ /** Offload a full renderToString call. Returns the HTML string. */
794
+ renderString(appProps, clientProps) {
795
+ return new Promise((resolve2, reject) => {
796
+ const id = this.nextId++;
797
+ this.pending.set(id, { kind: "renderString", resolve: resolve2, reject });
798
+ this.nextWorker().postMessage({ id, type: "renderString", appProps, clientProps });
799
+ });
800
+ }
801
+ /** Offload a renderToReadableStream call. Returns a ReadableStream fed by
802
+ * worker chunk messages. */
803
+ renderStream(appProps, clientProps) {
804
+ let controller;
805
+ const stream = new ReadableStream({
806
+ start: (ctrl) => {
807
+ controller = ctrl;
808
+ }
809
+ });
810
+ const id = this.nextId++;
811
+ this.pending.set(id, { kind: "renderStream", controller });
812
+ this.nextWorker().postMessage({ id, type: "renderStream", appProps, clientProps });
813
+ return stream;
814
+ }
815
+ async terminate() {
816
+ await Promise.all(this.workers.map((w) => w.terminate()));
817
+ }
818
+ };
819
+ async function buildSsrResponse(ReactPage, headHtml, status, getPrecontentHtml, streaming, renderPool, renderPayload) {
820
+ const renderToString = !streaming && !renderPool ? await getRenderToString() : null;
821
+ const renderReadableStream = streaming && !renderPool ? await getReadableStreamRenderer() : null;
822
+ const unsuspendForRender = renderPayload?.appProps?.context?._unsuspend ?? null;
823
+ if (!streaming) {
824
+ const [precontentHtml, postContent] = await getPrecontentHtml(headHtml);
825
+ let bodyHtml;
826
+ if (renderPool && renderPayload) {
827
+ bodyHtml = await renderPool.renderString(renderPayload.appProps, renderPayload.clientProps);
828
+ } else {
829
+ try {
830
+ globalThis.__hadarsUnsuspend = unsuspendForRender;
831
+ bodyHtml = renderToString(ReactPage);
832
+ } finally {
833
+ globalThis.__hadarsUnsuspend = null;
834
+ }
835
+ }
836
+ return new Response(precontentHtml + bodyHtml + postContent, {
837
+ headers: { "Content-Type": "text/html; charset=utf-8" },
838
+ status
839
+ });
840
+ }
841
+ const responseStream = new ReadableStream({
842
+ async start(controller) {
843
+ const [precontentHtml, postContent] = await getPrecontentHtml(headHtml);
844
+ controller.enqueue(encoder.encode(precontentHtml));
845
+ let bodyStream;
846
+ if (renderPool && renderPayload) {
847
+ bodyStream = renderPool.renderStream(renderPayload.appProps, renderPayload.clientProps);
848
+ } else {
849
+ let streamPromise;
850
+ try {
851
+ globalThis.__hadarsUnsuspend = unsuspendForRender;
852
+ streamPromise = renderReadableStream(ReactPage);
853
+ } finally {
854
+ globalThis.__hadarsUnsuspend = null;
855
+ }
856
+ bodyStream = await streamPromise;
857
+ }
858
+ const reader = bodyStream.getReader();
859
+ while (true) {
860
+ const { done, value } = await reader.read();
861
+ if (done)
862
+ break;
863
+ controller.enqueue(value);
864
+ }
865
+ controller.enqueue(encoder.encode(postContent));
866
+ controller.close();
867
+ }
868
+ });
869
+ return new Response(responseStream, {
870
+ headers: { "Content-Type": "text/html; charset=utf-8" },
871
+ status
872
+ });
873
+ }
874
+ var makePrecontentHtmlGetter = (htmlFilePromise) => {
875
+ let preHead = null;
876
+ let postHead = null;
877
+ let postContent = null;
878
+ return async (headHtml) => {
879
+ if (preHead === null || postHead === null || postContent === null) {
880
+ const html = await htmlFilePromise;
881
+ const headEnd = html.indexOf(HEAD_MARKER);
882
+ const contentStart = html.indexOf(BODY_MARKER);
883
+ preHead = html.slice(0, headEnd);
884
+ postHead = html.slice(headEnd + HEAD_MARKER.length, contentStart);
885
+ postContent = html.slice(contentStart + BODY_MARKER.length);
886
+ }
887
+ return [preHead + headHtml + postHead, postContent];
888
+ };
889
+ };
890
+ var SSR_FILENAME = "index.ssr.js";
891
+ var __dirname2 = process.cwd();
892
+ var getSuffix = (mode) => mode === "development" ? `?v=${Date.now()}` : "";
893
+ var HadarsFolder = "./.hadars";
894
+ var StaticPath = `${HadarsFolder}/static`;
895
+ var validateOptions = (options) => {
896
+ if (!options.entry) {
897
+ throw new Error("Entry file is required");
898
+ }
899
+ if (options.mode !== "development" && options.mode !== "production") {
900
+ throw new Error("Mode must be either 'development' or 'production'");
901
+ }
902
+ };
903
+ var resolveWorkerCmd = (packageDir2) => {
904
+ const tsPath = pathMod3.resolve(packageDir2, "ssr-watch.ts");
905
+ const jsPath = pathMod3.resolve(packageDir2, "ssr-watch.js");
906
+ if (isBun && existsSync2(tsPath)) {
907
+ return ["bun", tsPath];
908
+ }
909
+ if (isDeno && existsSync2(tsPath)) {
910
+ return ["deno", "run", "--allow-all", tsPath];
911
+ }
912
+ if (existsSync2(tsPath)) {
913
+ const allArgs = [...process.execArgv, process.argv[1] ?? ""];
914
+ const hasTsx = allArgs.some((a) => a.includes("tsx"));
915
+ const hasTsNode = allArgs.some((a) => a.includes("ts-node"));
916
+ if (hasTsx)
917
+ return ["tsx", tsPath];
918
+ if (hasTsNode)
919
+ return ["ts-node", tsPath];
920
+ }
921
+ if (existsSync2(jsPath)) {
922
+ return ["node", jsPath];
923
+ }
924
+ throw new Error(
925
+ `[hadars] SSR worker not found. Expected:
926
+ ${jsPath}
927
+ Run "npm run build:cli" to compile it, or launch hadars via a TypeScript runner:
928
+ npx tsx cli.ts dev`
929
+ );
930
+ };
931
+ var dev = async (options) => {
932
+ await fs.rm(HadarsFolder, { recursive: true, force: true });
933
+ let { port = 9090, baseURL = "" } = options;
934
+ console.log(`Starting Hadars on port ${port}`);
935
+ validateOptions(options);
936
+ const handleProxy = createProxyHandler(options);
937
+ const handleWS = upgradeHandler(options);
938
+ const handler = options.fetch;
939
+ const entry = pathMod3.resolve(__dirname2, options.entry);
940
+ const hmrPort = options.hmrPort ?? port + 1;
941
+ const packageDir2 = pathMod3.dirname(fileURLToPath2(import.meta.url));
942
+ const clientScriptPath2 = pathMod3.resolve(packageDir2, "utils", "clientScript.tsx");
943
+ const headPath = pathMod3.resolve(packageDir2, "utils", "Head");
944
+ let clientScript = "";
945
+ try {
946
+ clientScript = (await fs.readFile(clientScriptPath2, "utf-8")).replace("$_MOD_PATH$", entry + getSuffix(options.mode)).replace("$_HEAD_PATH$", headPath);
947
+ } catch (err) {
948
+ console.error("Failed to read client script from package dist, falling back to src", err);
949
+ throw err;
950
+ }
951
+ const tmpFilePath = pathMod3.join(os.tmpdir(), `hadars-client-${Date.now()}.tsx`);
952
+ await fs.writeFile(tmpFilePath, clientScript);
953
+ let ssrBuildId = Date.now();
954
+ const clientCompiler = createClientCompiler(tmpFilePath, {
955
+ target: "web",
956
+ output: {
957
+ filename: "index.js",
958
+ path: pathMod3.resolve(__dirname2, StaticPath)
959
+ },
960
+ base: baseURL,
961
+ mode: "development",
962
+ swcPlugins: options.swcPlugins,
963
+ define: options.define
964
+ });
965
+ const devServer = new RspackDevServer({
966
+ port: hmrPort,
967
+ hot: true,
968
+ liveReload: false,
969
+ client: {
970
+ webSocketURL: `ws://localhost:${hmrPort}/ws`
971
+ },
972
+ devMiddleware: {
973
+ writeToDisk: true
974
+ },
975
+ headers: { "Access-Control-Allow-Origin": "*" },
976
+ allowedHosts: "all"
977
+ }, clientCompiler);
978
+ console.log(`Starting HMR dev server on port ${hmrPort}`);
979
+ await new Promise((resolve2, reject) => {
980
+ let resolved = false;
981
+ clientCompiler.hooks.done.tap("initial-build", (stats) => {
982
+ if (!resolved) {
983
+ resolved = true;
984
+ console.log(stats.toString({ colors: true }));
985
+ resolve2();
986
+ }
987
+ });
988
+ devServer.start().catch(reject);
989
+ });
990
+ const workerCmd = resolveWorkerCmd(packageDir2);
991
+ console.log("Spawning SSR worker:", workerCmd.join(" "), "entry:", entry);
992
+ const child = spawn(workerCmd[0], [
993
+ ...workerCmd.slice(1),
994
+ `--entry=${entry}`,
995
+ `--outDir=${HadarsFolder}`,
996
+ `--outFile=${SSR_FILENAME}`,
997
+ `--base=${baseURL}`
998
+ ], { stdio: "pipe" });
999
+ child.stdin?.end();
1000
+ const stdoutWebStream = nodeReadableToWebStream(child.stdout);
1001
+ const stderrWebStream = nodeReadableToWebStream(child.stderr);
1002
+ const marker = "ssr-watch: initial-build-complete";
1003
+ const rebuildMarker = "ssr-watch: SSR rebuilt";
1004
+ const decoder = new TextDecoder();
1005
+ let gotMarker = false;
1006
+ let stdoutReader = null;
1007
+ try {
1008
+ stdoutReader = stdoutWebStream.getReader();
1009
+ let buf = "";
1010
+ const start = Date.now();
1011
+ const timeoutMs = 2e4;
1012
+ while (Date.now() - start < timeoutMs) {
1013
+ const { done, value } = await stdoutReader.read();
1014
+ if (done) {
1015
+ stdoutReader = null;
1016
+ break;
1017
+ }
1018
+ const chunk = decoder.decode(value, { stream: true });
1019
+ buf += chunk;
1020
+ try {
1021
+ process.stdout.write(chunk);
1022
+ } catch (e) {
1023
+ }
1024
+ if (buf.includes(marker)) {
1025
+ gotMarker = true;
1026
+ break;
1027
+ }
1028
+ }
1029
+ if (!gotMarker) {
1030
+ console.warn("SSR worker did not signal initial build completion within timeout");
1031
+ }
1032
+ } catch (err) {
1033
+ console.error("Error reading SSR worker output", err);
1034
+ stdoutReader = null;
1035
+ }
1036
+ if (stdoutReader) {
1037
+ const reader = stdoutReader;
1038
+ (async () => {
1039
+ try {
1040
+ while (true) {
1041
+ const { done, value } = await reader.read();
1042
+ if (done)
1043
+ break;
1044
+ const chunk = decoder.decode(value, { stream: true });
1045
+ try {
1046
+ process.stdout.write(chunk);
1047
+ } catch (e) {
1048
+ }
1049
+ if (chunk.includes(rebuildMarker)) {
1050
+ ssrBuildId = Date.now();
1051
+ console.log("[hadars] SSR bundle updated, build id:", ssrBuildId);
1052
+ }
1053
+ }
1054
+ } catch (e) {
1055
+ }
1056
+ })();
1057
+ }
1058
+ (async () => {
1059
+ try {
1060
+ const r = stderrWebStream.getReader();
1061
+ while (true) {
1062
+ const { done, value } = await r.read();
1063
+ if (done)
1064
+ break;
1065
+ try {
1066
+ process.stderr.write(decoder.decode(value));
1067
+ } catch (e) {
1068
+ }
1069
+ }
1070
+ } catch (e) {
1071
+ }
1072
+ })();
1073
+ const getPrecontentHtml = makePrecontentHtmlGetter(
1074
+ fs.readFile(pathMod3.join(__dirname2, StaticPath, "out.html"), "utf-8")
1075
+ );
1076
+ await serve(port, async (req, ctx) => {
1077
+ const request = parseRequest(req);
1078
+ if (handler) {
1079
+ const res = await handler(request);
1080
+ if (res)
1081
+ return res;
1082
+ }
1083
+ if (handleWS && handleWS(request, ctx))
1084
+ return void 0;
1085
+ const proxied = await handleProxy(request);
1086
+ if (proxied)
1087
+ return proxied;
1088
+ const url = new URL(request.url);
1089
+ const path2 = url.pathname;
1090
+ const staticRes = await tryServeFile(pathMod3.join(__dirname2, StaticPath, path2));
1091
+ if (staticRes)
1092
+ return staticRes;
1093
+ const projectStaticPath = pathMod3.resolve(process.cwd(), "static");
1094
+ if (path2 === "/" || path2 === "") {
1095
+ const indexRes = await tryServeFile(pathMod3.join(projectStaticPath, "index.html"));
1096
+ if (indexRes)
1097
+ return indexRes;
1098
+ }
1099
+ const projectRes = await tryServeFile(pathMod3.join(projectStaticPath, path2));
1100
+ if (projectRes)
1101
+ return projectRes;
1102
+ const ssrComponentPath = pathMod3.join(__dirname2, HadarsFolder, SSR_FILENAME);
1103
+ const importPath = pathToFileURL2(ssrComponentPath).href + `?t=${ssrBuildId}`;
1104
+ const {
1105
+ default: Component,
1106
+ getInitProps,
1107
+ getAfterRenderProps,
1108
+ getFinalProps
1109
+ } = await import(importPath);
1110
+ const { ReactPage, status, headHtml } = await getReactResponse(request, {
1111
+ document: {
1112
+ body: Component,
1113
+ lang: "en",
1114
+ getInitProps,
1115
+ getAfterRenderProps,
1116
+ getFinalProps
1117
+ }
1118
+ });
1119
+ return buildSsrResponse(ReactPage, headHtml, status, getPrecontentHtml, options.streaming === true);
1120
+ }, options.websocket);
1121
+ };
1122
+ var build = async (options) => {
1123
+ validateOptions(options);
1124
+ const entry = pathMod3.resolve(__dirname2, options.entry);
1125
+ const packageDir2 = pathMod3.dirname(fileURLToPath2(import.meta.url));
1126
+ const clientScriptPath2 = pathMod3.resolve(packageDir2, "utils", "clientScript.js");
1127
+ const headPath = pathMod3.resolve(packageDir2, "utils", "Head");
1128
+ let clientScript = "";
1129
+ try {
1130
+ clientScript = (await fs.readFile(clientScriptPath2, "utf-8")).replace("$_MOD_PATH$", entry + getSuffix(options.mode)).replace("$_HEAD_PATH$", headPath);
1131
+ } catch (err) {
1132
+ const srcClientPath = pathMod3.resolve(packageDir2, "utils", "clientScript.tsx");
1133
+ clientScript = (await fs.readFile(srcClientPath, "utf-8")).replace("$_MOD_PATH$", entry + `?v=${Date.now()}`).replace("$_HEAD_PATH$", pathMod3.resolve(packageDir2, "utils", "Head"));
1134
+ }
1135
+ const tmpFilePath = pathMod3.join(os.tmpdir(), `hadars-client-${Date.now()}.tsx`);
1136
+ await fs.writeFile(tmpFilePath, clientScript);
1137
+ const randomStr = crypto.randomBytes(6).toString("hex");
1138
+ console.log("Building client and server bundles in parallel...");
1139
+ await Promise.all([
1140
+ compileEntry(tmpFilePath, {
1141
+ target: "web",
1142
+ output: {
1143
+ filename: `index-${randomStr}.js`,
1144
+ path: pathMod3.resolve(__dirname2, StaticPath)
1145
+ },
1146
+ base: options.baseURL,
1147
+ mode: "production",
1148
+ swcPlugins: options.swcPlugins,
1149
+ define: options.define
1150
+ }),
1151
+ compileEntry(pathMod3.resolve(__dirname2, options.entry), {
1152
+ output: {
1153
+ iife: false,
1154
+ filename: SSR_FILENAME,
1155
+ path: pathMod3.resolve(__dirname2, HadarsFolder),
1156
+ publicPath: "",
1157
+ library: { type: "module" }
1158
+ },
1159
+ base: options.baseURL,
1160
+ target: "node",
1161
+ mode: "production",
1162
+ swcPlugins: options.swcPlugins,
1163
+ define: options.define
1164
+ })
1165
+ ]);
1166
+ await fs.rm(tmpFilePath);
1167
+ await fs.writeFile(
1168
+ pathMod3.join(__dirname2, HadarsFolder, "hadars.json"),
1169
+ JSON.stringify({ buildId: randomStr })
1170
+ );
1171
+ console.log("Build complete.");
1172
+ };
1173
+ var run = async (options) => {
1174
+ validateOptions(options);
1175
+ let { port = 9090, workers = 1 } = options;
1176
+ if (isNode && workers > 1 && cluster.isPrimary) {
1177
+ console.log(`[hadars] Starting ${workers} worker processes on port ${port}`);
1178
+ for (let i = 0; i < workers; i++) {
1179
+ cluster.fork();
1180
+ }
1181
+ cluster.on("exit", (worker, code, signal) => {
1182
+ console.warn(`[hadars] Worker ${worker.process.pid} exited (${signal ?? code}), restarting...`);
1183
+ cluster.fork();
1184
+ });
1185
+ await new Promise(() => {
1186
+ });
1187
+ return;
1188
+ }
1189
+ const handleProxy = createProxyHandler(options);
1190
+ const handleWS = upgradeHandler(options);
1191
+ const handler = options.fetch;
1192
+ console.log(`Starting Hadars (run) on port ${port}`);
1193
+ let renderPool;
1194
+ if (!isNode && workers > 1) {
1195
+ const packageDir2 = pathMod3.dirname(fileURLToPath2(import.meta.url));
1196
+ const workerJs = pathMod3.resolve(packageDir2, "ssr-render-worker.js");
1197
+ const workerTs = pathMod3.resolve(packageDir2, "src", "ssr-render-worker.ts");
1198
+ const workerFile = existsSync2(workerJs) ? workerJs : workerTs;
1199
+ const ssrBundlePath = pathMod3.resolve(__dirname2, HadarsFolder, SSR_FILENAME);
1200
+ renderPool = new RenderWorkerPool(workerFile, workers, ssrBundlePath);
1201
+ console.log(`[hadars] SSR render pool: ${workers} worker threads`);
1202
+ }
1203
+ const getPrecontentHtml = makePrecontentHtmlGetter(
1204
+ fs.readFile(pathMod3.join(__dirname2, StaticPath, "out.html"), "utf-8")
1205
+ );
1206
+ await serve(port, async (req, ctx) => {
1207
+ const request = parseRequest(req);
1208
+ if (handler) {
1209
+ const res = await handler(request);
1210
+ if (res)
1211
+ return res;
1212
+ }
1213
+ if (handleWS && handleWS(request, ctx))
1214
+ return void 0;
1215
+ const proxied = await handleProxy(request);
1216
+ if (proxied)
1217
+ return proxied;
1218
+ const url = new URL(request.url);
1219
+ const path2 = url.pathname;
1220
+ const staticRes = await tryServeFile(pathMod3.join(__dirname2, StaticPath, path2));
1221
+ if (staticRes)
1222
+ return staticRes;
1223
+ if (path2 === "/" || path2 === "") {
1224
+ const indexRes = await tryServeFile(pathMod3.join(__dirname2, StaticPath, "index.html"));
1225
+ if (indexRes)
1226
+ return indexRes;
1227
+ }
1228
+ const projectStaticPath = pathMod3.resolve(process.cwd(), "static");
1229
+ const projectRes = await tryServeFile(pathMod3.join(projectStaticPath, path2));
1230
+ if (projectRes)
1231
+ return projectRes;
1232
+ const routeClean = path2.replace(/(^\/|\/$)/g, "");
1233
+ if (routeClean) {
1234
+ const routeRes = await tryServeFile(
1235
+ pathMod3.join(__dirname2, StaticPath, routeClean, "index.html")
1236
+ );
1237
+ if (routeRes)
1238
+ return routeRes;
1239
+ }
1240
+ const componentPath = pathToFileURL2(
1241
+ pathMod3.resolve(__dirname2, HadarsFolder, SSR_FILENAME)
1242
+ ).href;
1243
+ const {
1244
+ default: Component,
1245
+ getInitProps,
1246
+ getAfterRenderProps,
1247
+ getFinalProps
1248
+ } = await import(componentPath);
1249
+ const { ReactPage, status, headHtml, renderPayload } = await getReactResponse(request, {
1250
+ document: {
1251
+ body: Component,
1252
+ lang: "en",
1253
+ getInitProps,
1254
+ getAfterRenderProps,
1255
+ getFinalProps
1256
+ }
1257
+ });
1258
+ return buildSsrResponse(ReactPage, headHtml, status, getPrecontentHtml, options.streaming === true, renderPool, renderPayload);
1259
+ }, options.websocket);
1260
+ };
1261
+
1262
+ // cli-lib.ts
1263
+ var SUPPORTED = ["hadars.config.js", "hadars.config.mjs", "hadars.config.cjs", "hadars.config.ts"];
1264
+ function findConfig(cwd) {
1265
+ for (const name of SUPPORTED) {
1266
+ const p = resolve(cwd, name);
1267
+ if (existsSync3(p))
1268
+ return p;
1269
+ }
1270
+ return null;
1271
+ }
1272
+ async function dev2(config) {
1273
+ await dev({
1274
+ ...config,
1275
+ baseURL: "",
1276
+ mode: "development"
1277
+ });
1278
+ }
1279
+ async function build2(config) {
1280
+ await build({
1281
+ ...config,
1282
+ mode: "production"
1283
+ });
1284
+ }
1285
+ async function run2(config) {
1286
+ await run({
1287
+ ...config,
1288
+ mode: "production"
1289
+ });
1290
+ }
1291
+ async function loadConfig(configPath) {
1292
+ const url = `file://${configPath}`;
1293
+ const mod = await import(url);
1294
+ return mod && (mod.default ?? mod);
1295
+ }
1296
+ var TEMPLATES = {
1297
+ "package.json": (name) => JSON.stringify({
1298
+ name,
1299
+ version: "0.1.0",
1300
+ type: "module",
1301
+ private: true,
1302
+ scripts: {
1303
+ dev: "hadars dev",
1304
+ build: "hadars build",
1305
+ start: "hadars run"
1306
+ },
1307
+ dependencies: {
1308
+ "hadars": "latest",
1309
+ react: "^19.0.0",
1310
+ "react-dom": "^19.0.0"
1311
+ }
1312
+ }, null, 2) + "\n",
1313
+ "hadars.config.ts": () => `import type { HadarsOptions } from 'hadars';
1314
+
1315
+ const config: HadarsOptions = {
1316
+ entry: 'src/App.tsx',
1317
+ port: 3000,
1318
+ };
1319
+
1320
+ export default config;
1321
+ `,
1322
+ "tsconfig.json": () => JSON.stringify({
1323
+ compilerOptions: {
1324
+ lib: ["ESNext", "DOM"],
1325
+ target: "ESNext",
1326
+ module: "Preserve",
1327
+ moduleDetection: "force",
1328
+ jsx: "react-jsx",
1329
+ moduleResolution: "bundler",
1330
+ allowImportingTsExtensions: true,
1331
+ verbatimModuleSyntax: true,
1332
+ noEmit: true,
1333
+ strict: true,
1334
+ skipLibCheck: true
1335
+ }
1336
+ }, null, 2) + "\n",
1337
+ ".gitignore": () => `node_modules/
1338
+ .hadars/
1339
+ dist/
1340
+ `,
1341
+ "src/App.tsx": () => `import React from 'react';
1342
+ import { HadarsContext, HadarsHead, type HadarsApp } from 'hadars';
1343
+
1344
+ const App: HadarsApp<{}> = ({ context }) => (
1345
+ <HadarsContext context={context}>
1346
+ <HadarsHead status={200}>
1347
+ <title>My App</title>
1348
+ </HadarsHead>
1349
+ <main>
1350
+ <h1>Hello from hadars!</h1>
1351
+ <p>Edit <code>src/App.tsx</code> to get started.</p>
1352
+ </main>
1353
+ </HadarsContext>
1354
+ );
1355
+
1356
+ export default App;
1357
+ `
1358
+ };
1359
+ async function createProject(name, cwd) {
1360
+ const dir = resolve(cwd, name);
1361
+ if (existsSync3(dir)) {
1362
+ console.error(`Directory already exists: ${dir}`);
1363
+ process.exit(1);
1364
+ }
1365
+ console.log(`Creating hadars project in ${dir}`);
1366
+ await mkdir(join(dir, "src"), { recursive: true });
1367
+ for (const [file, template] of Object.entries(TEMPLATES)) {
1368
+ const content = template(name);
1369
+ await writeFile(join(dir, file), content, "utf-8");
1370
+ console.log(` created ${file}`);
1371
+ }
1372
+ console.log(`
1373
+ Done! Next steps:
1374
+
1375
+ cd ${name}
1376
+ npm install # or: bun install / pnpm install
1377
+ npm run dev # or: bun run dev
1378
+ `);
1379
+ }
1380
+ function usage() {
1381
+ console.log("Usage: hadars <new <name> | dev | build | run>");
1382
+ }
1383
+ async function runCli(argv, cwd = process.cwd()) {
1384
+ const cmd = argv[2];
1385
+ if (cmd === "new") {
1386
+ const name = argv[3];
1387
+ if (!name) {
1388
+ console.error("Usage: hadars new <project-name>");
1389
+ process.exit(1);
1390
+ }
1391
+ try {
1392
+ await createProject(name, cwd);
1393
+ } catch (err) {
1394
+ console.error("Failed to create project:", err?.message ?? err);
1395
+ process.exit(2);
1396
+ }
1397
+ return;
1398
+ }
1399
+ if (!cmd || !["dev", "build", "run"].includes(cmd)) {
1400
+ usage();
1401
+ process.exit(1);
1402
+ }
1403
+ const configPath = findConfig(cwd);
1404
+ if (!configPath) {
1405
+ console.log(`No hadars.config.* found in ${cwd}`);
1406
+ console.log("Proceeding with default behavior (no config)");
1407
+ return;
1408
+ }
1409
+ try {
1410
+ const cfg = await loadConfig(configPath);
1411
+ console.log(`Loaded config from ${configPath}`);
1412
+ switch (cmd) {
1413
+ case "dev":
1414
+ console.log("Starting development server...");
1415
+ await dev2(cfg);
1416
+ break;
1417
+ case "build":
1418
+ console.log("Building project...");
1419
+ await build2(cfg);
1420
+ console.log("Build complete");
1421
+ process.exit(0);
1422
+ case "run":
1423
+ console.log("Running project...");
1424
+ await run2(cfg);
1425
+ break;
1426
+ }
1427
+ } catch (err) {
1428
+ console.error("Failed to load config:", err?.message ?? err);
1429
+ process.exit(2);
1430
+ }
1431
+ }
1432
+
1433
+ // cli.ts
1434
+ runCli(process.argv).catch((err) => {
1435
+ console.error(err);
1436
+ try {
1437
+ process.exit(1);
1438
+ } catch (_) {
1439
+ throw err;
1440
+ }
1441
+ });