hadars 0.2.1 → 0.2.2-rc.0

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 CHANGED
@@ -5,9 +5,9 @@ import { spawn as spawn2 } from "node:child_process";
5
5
 
6
6
  // cli-lib.ts
7
7
  import { existsSync as existsSync3 } from "node:fs";
8
- import { mkdir, writeFile, unlink } from "node:fs/promises";
9
- import { resolve, join, dirname } from "node:path";
10
- import { fileURLToPath as fileURLToPath3 } from "node:url";
8
+ import { mkdir as mkdir2, writeFile as writeFile2, unlink, readFile as readFile2 } from "node:fs/promises";
9
+ import { resolve, join as join2, dirname as dirname2 } from "node:path";
10
+ import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
11
11
 
12
12
  // src/utils/proxyHandler.tsx
13
13
  var cloneHeaders = (headers) => {
@@ -1174,7 +1174,7 @@ var getReactResponse = async (req, opts) => {
1174
1174
  head: { title: "Hadars App", meta: {}, link: {}, style: {}, script: {}, status: 200 }
1175
1175
  };
1176
1176
  let props = {
1177
- ...getInitProps ? await getInitProps(req) : {},
1177
+ ...getInitProps ? await getInitProps(req, opts.staticCtx) : {},
1178
1178
  location: req.location,
1179
1179
  context
1180
1180
  };
@@ -1200,7 +1200,7 @@ var getReactResponse = async (req, opts) => {
1200
1200
  }
1201
1201
  };
1202
1202
  const finalize = async () => {
1203
- const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
1203
+ const restProps = getFinalProps ? await getFinalProps(props) : props;
1204
1204
  const serverData = {};
1205
1205
  let hasServerData = false;
1206
1206
  for (const [key, entry] of unsuspend.cache) {
@@ -1796,6 +1796,11 @@ function buildSsrResponse(head, status, getAppBody, finalize, getPrecontentHtml)
1796
1796
  status
1797
1797
  });
1798
1798
  }
1799
+ async function buildSsrHtml(bodyHtml, clientProps, headHtml, getPrecontentHtml) {
1800
+ const [precontentHtml, postContent] = await Promise.resolve(getPrecontentHtml(headHtml));
1801
+ const scriptContent = JSON.stringify({ hadars: { props: clientProps } }).replace(/</g, "\\u003c");
1802
+ return precontentHtml + `<div id="app">${bodyHtml}</div><script id="hadars" type="application/json">${scriptContent}</script>` + postContent;
1803
+ }
1799
1804
  var makePrecontentHtmlGetter = (htmlFilePromise) => {
1800
1805
  let preHead = null;
1801
1806
  let postHead = null;
@@ -1891,6 +1896,357 @@ function createRenderCache(opts, handler) {
1891
1896
  };
1892
1897
  }
1893
1898
 
1899
+ // src/source/runner.ts
1900
+ import { EventEmitter as EventEmitter2 } from "node:events";
1901
+ import { createRequire } from "node:module";
1902
+
1903
+ // src/source/store.ts
1904
+ var NodeStore = class {
1905
+ byId = /* @__PURE__ */ new Map();
1906
+ byType = /* @__PURE__ */ new Map();
1907
+ createNode(node) {
1908
+ this.byId.set(node.id, node);
1909
+ const list = this.byType.get(node.internal.type) ?? [];
1910
+ const idx = list.findIndex((n) => n.id === node.id);
1911
+ if (idx >= 0) list[idx] = node;
1912
+ else list.push(node);
1913
+ this.byType.set(node.internal.type, list);
1914
+ }
1915
+ getNode(id) {
1916
+ return this.byId.get(id);
1917
+ }
1918
+ getNodes() {
1919
+ return Array.from(this.byId.values());
1920
+ }
1921
+ getNodesByType(type) {
1922
+ return this.byType.get(type) ?? [];
1923
+ }
1924
+ getTypes() {
1925
+ return Array.from(this.byType.keys());
1926
+ }
1927
+ };
1928
+
1929
+ // src/source/context.ts
1930
+ import { createHash } from "node:crypto";
1931
+ import { EventEmitter } from "node:events";
1932
+ function makeGatsbyContext(store, pluginName, pluginOptions = {}, emitter = new EventEmitter()) {
1933
+ const cacheMap = /* @__PURE__ */ new Map();
1934
+ const cache = {
1935
+ get: (key) => Promise.resolve(cacheMap.get(key)),
1936
+ set: (key, value) => {
1937
+ cacheMap.set(key, value);
1938
+ return Promise.resolve();
1939
+ }
1940
+ };
1941
+ const reporter = {
1942
+ info: (msg) => console.log(`[${pluginName}] ${msg}`),
1943
+ warn: (msg) => console.warn(`[${pluginName}] WARN: ${msg}`),
1944
+ error: (msg, err) => console.error(`[${pluginName}] ERROR: ${msg}`, err ?? ""),
1945
+ panic: (msg, err) => {
1946
+ console.error(`[${pluginName}] PANIC: ${msg}`, err ?? "");
1947
+ process.exit(1);
1948
+ },
1949
+ verbose: (msg) => {
1950
+ if (process.env.HADARS_VERBOSE) console.log(`[${pluginName}] ${msg}`);
1951
+ },
1952
+ activityTimer: (name) => ({
1953
+ start: () => {
1954
+ if (process.env.HADARS_VERBOSE) console.log(`[${pluginName}] \u25B6 ${name}`);
1955
+ },
1956
+ end: () => {
1957
+ if (process.env.HADARS_VERBOSE) console.log(`[${pluginName}] \u25A0 ${name}`);
1958
+ }
1959
+ }),
1960
+ // Gatsby 4+ reporter extras — used by some plugins but safe to no-op
1961
+ setErrorMap: () => {
1962
+ },
1963
+ log: (msg) => console.log(`[${pluginName}] ${msg}`),
1964
+ success: (msg) => console.log(`[${pluginName}] \u2713 ${msg}`)
1965
+ };
1966
+ const actions = {
1967
+ createNode: (node) => store.createNode(node),
1968
+ deleteNode: ({ id }) => {
1969
+ },
1970
+ touchNode: () => {
1971
+ }
1972
+ };
1973
+ return {
1974
+ actions,
1975
+ createNodeId: (input) => createHash("sha256").update(pluginName + input).digest("hex"),
1976
+ createContentDigest: (content) => {
1977
+ const str = typeof content === "string" ? content : JSON.stringify(content);
1978
+ return createHash("md5").update(str).digest("hex");
1979
+ },
1980
+ getNode: (id) => store.getNode(id),
1981
+ getNodes: () => store.getNodes(),
1982
+ getNodesByType: (t) => store.getNodesByType(t),
1983
+ cache,
1984
+ reporter,
1985
+ // Stubs for rarely-used Gatsby internals that plugins may destructure
1986
+ store: {},
1987
+ emitter,
1988
+ tracing: { startSpan: () => ({ finish: () => {
1989
+ } }) },
1990
+ schema: {},
1991
+ parentSpan: null
1992
+ };
1993
+ }
1994
+
1995
+ // src/source/runner.ts
1996
+ var SETTLE_IDLE_MS = 300;
1997
+ var SETTLE_TIMEOUT_MS = 1e4;
1998
+ function waitForSettle(getLastNodeTime) {
1999
+ return new Promise((resolve2) => {
2000
+ const deadline = Date.now() + SETTLE_TIMEOUT_MS;
2001
+ const check = () => {
2002
+ const idle = Date.now() - getLastNodeTime();
2003
+ if (idle >= SETTLE_IDLE_MS || Date.now() >= deadline) {
2004
+ resolve2();
2005
+ } else {
2006
+ setTimeout(check, 50);
2007
+ }
2008
+ };
2009
+ setTimeout(check, SETTLE_IDLE_MS);
2010
+ });
2011
+ }
2012
+ async function runSources(sources) {
2013
+ const store = new NodeStore();
2014
+ for (const entry of sources) {
2015
+ const { resolve: resolve2, options = {} } = entry;
2016
+ let mod;
2017
+ if (typeof resolve2 === "string") {
2018
+ const projectRequire = createRequire(process.cwd() + "/package.json");
2019
+ let pkgPath;
2020
+ try {
2021
+ pkgPath = projectRequire.resolve(`${resolve2}/gatsby-node`);
2022
+ } catch {
2023
+ pkgPath = projectRequire.resolve(resolve2);
2024
+ }
2025
+ mod = await import(pkgPath);
2026
+ } else {
2027
+ mod = resolve2;
2028
+ }
2029
+ if (typeof mod.sourceNodes !== "function") {
2030
+ const name = typeof resolve2 === "string" ? resolve2 : "(module)";
2031
+ console.warn(`[hadars] source plugin ${name} does not export sourceNodes \u2014 skipping`);
2032
+ continue;
2033
+ }
2034
+ const pluginName = typeof resolve2 === "string" ? resolve2 : "hadars-source";
2035
+ let lastNodeTime = Date.now();
2036
+ const trackingStore = new Proxy(store, {
2037
+ get(target, prop) {
2038
+ if (prop === "createNode") {
2039
+ return (node) => {
2040
+ lastNodeTime = Date.now();
2041
+ return target.createNode(node);
2042
+ };
2043
+ }
2044
+ return target[prop];
2045
+ }
2046
+ });
2047
+ const emitter = new EventEmitter2();
2048
+ const ctx = makeGatsbyContext(trackingStore, pluginName, options, emitter);
2049
+ try {
2050
+ await mod.sourceNodes(ctx, options);
2051
+ } catch (err) {
2052
+ throw new Error(
2053
+ `[hadars] source plugin "${pluginName}" threw during sourceNodes: ${err.message}`
2054
+ );
2055
+ }
2056
+ await waitForSettle(() => lastNodeTime);
2057
+ emitter.emit("BOOTSTRAP_FINISHED");
2058
+ await waitForSettle(() => lastNodeTime);
2059
+ }
2060
+ return store;
2061
+ }
2062
+
2063
+ // src/source/inference.ts
2064
+ function inferScalar(value) {
2065
+ if (typeof value === "boolean") return "Boolean";
2066
+ if (typeof value === "number") return Number.isInteger(value) ? "Int" : "Float";
2067
+ return "String";
2068
+ }
2069
+ function inferFieldShape(value, seenTypes) {
2070
+ if (value === null || value === void 0) {
2071
+ return { type: "String", nullable: true };
2072
+ }
2073
+ if (Array.isArray(value)) {
2074
+ const inner = value.length > 0 ? inferFieldShape(value[0], seenTypes) : { type: "String", nullable: true };
2075
+ return { type: `[${inner.type}]`, nullable: true };
2076
+ }
2077
+ if (typeof value === "object") {
2078
+ return { type: "String", nullable: true };
2079
+ }
2080
+ return { type: inferScalar(value), nullable: true };
2081
+ }
2082
+ var INTERNAL_FIELDS = /* @__PURE__ */ new Set(["id", "internal", "__typename", "parent", "children"]);
2083
+ var FILTERABLE_SCALARS = /* @__PURE__ */ new Set(["String", "Int", "Float", "Boolean", "ID"]);
2084
+ function buildTypeFields(nodes) {
2085
+ const fieldMap = /* @__PURE__ */ new Map();
2086
+ for (const node of nodes) {
2087
+ for (const [key, val] of Object.entries(node)) {
2088
+ if (INTERNAL_FIELDS.has(key)) continue;
2089
+ if (fieldMap.has(key)) continue;
2090
+ const { type } = inferFieldShape(val, /* @__PURE__ */ new Set());
2091
+ fieldMap.set(key, {
2092
+ name: key,
2093
+ type,
2094
+ filterable: FILTERABLE_SCALARS.has(type)
2095
+ });
2096
+ }
2097
+ }
2098
+ return Array.from(fieldMap.values());
2099
+ }
2100
+ function buildTypeSDL(typeName, fields) {
2101
+ const lines = [
2102
+ " id: ID!",
2103
+ ...fields.map((f) => ` ${f.name}: ${f.type}`)
2104
+ ];
2105
+ return `type ${typeName} {
2106
+ ${lines.join("\n")}
2107
+ }`;
2108
+ }
2109
+ function queryNames(typeName) {
2110
+ const lower = typeName.charAt(0).toLowerCase() + typeName.slice(1);
2111
+ return { single: lower, all: `all${typeName}` };
2112
+ }
2113
+ function buildSingleArgs(fields) {
2114
+ const args = [
2115
+ "id: ID",
2116
+ ...fields.filter((f) => f.filterable && f.name !== "id").map((f) => `${f.name}: ${f.type}`)
2117
+ ];
2118
+ return args.join(", ");
2119
+ }
2120
+ async function buildSchemaExecutor(store) {
2121
+ let gql;
2122
+ try {
2123
+ const { createRequire: createRequire2 } = await import("node:module");
2124
+ const projectRequire = createRequire2(process.cwd() + "/package.json");
2125
+ const graphqlPath = projectRequire.resolve("graphql");
2126
+ gql = await import(graphqlPath);
2127
+ } catch {
2128
+ return null;
2129
+ }
2130
+ const { buildSchema, graphql } = gql;
2131
+ const types = store.getTypes();
2132
+ if (types.length === 0) {
2133
+ const schema2 = buildSchema("type Query { _empty: String }");
2134
+ return (query, variables) => graphql({ schema: schema2, source: query, variableValues: variables });
2135
+ }
2136
+ const typeFields = new Map(
2137
+ types.map((typeName) => {
2138
+ const nodes = store.getNodesByType(typeName);
2139
+ return [typeName, buildTypeFields(nodes)];
2140
+ })
2141
+ );
2142
+ const typeSDLs = types.map(
2143
+ (typeName) => buildTypeSDL(typeName, typeFields.get(typeName))
2144
+ );
2145
+ const queryFields = types.map((typeName) => {
2146
+ const { single, all } = queryNames(typeName);
2147
+ const args = buildSingleArgs(typeFields.get(typeName));
2148
+ return [
2149
+ ` ${single}(${args}): ${typeName}`,
2150
+ ` ${all}: [${typeName}!]!`
2151
+ ].join("\n");
2152
+ });
2153
+ const sdl = [
2154
+ ...typeSDLs,
2155
+ `type Query {
2156
+ ${queryFields.join("\n")}
2157
+ }`
2158
+ ].join("\n\n");
2159
+ let schema;
2160
+ try {
2161
+ schema = buildSchema(sdl);
2162
+ } catch (err) {
2163
+ throw new Error(`[hadars] Failed to build GraphQL schema from node store: ${err.message}`);
2164
+ }
2165
+ const rootValue = {};
2166
+ for (const typeName of types) {
2167
+ const { single, all } = queryNames(typeName);
2168
+ rootValue[all] = () => store.getNodesByType(typeName);
2169
+ rootValue[single] = (args) => {
2170
+ const nodes = store.getNodesByType(typeName);
2171
+ return nodes.find(
2172
+ (node) => Object.entries(args).every(([k, v]) => v === void 0 || node[k] === v)
2173
+ ) ?? null;
2174
+ };
2175
+ }
2176
+ return (query, variables) => graphql({ schema, rootValue, source: query, variableValues: variables });
2177
+ }
2178
+
2179
+ // src/source/graphiql.ts
2180
+ var GRAPHQL_PATH = "/__hadars/graphql";
2181
+ var GRAPHIQL_HTML = `<!doctype html>
2182
+ <html lang="en">
2183
+ <head>
2184
+ <meta charset="utf-8">
2185
+ <meta name="viewport" content="width=device-width, initial-scale=1">
2186
+ <title>GraphiQL \u2014 hadars</title>
2187
+ <style>
2188
+ * { box-sizing: border-box; margin: 0; padding: 0; }
2189
+ body { height: 100vh; overflow: hidden; }
2190
+ #graphiql { height: 100vh; }
2191
+ </style>
2192
+ <link rel="stylesheet" href="https://unpkg.com/graphiql@3/graphiql.min.css" />
2193
+ </head>
2194
+ <body>
2195
+ <div id="graphiql"></div>
2196
+ <script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
2197
+ <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
2198
+ <script src="https://unpkg.com/graphiql@3/graphiql.min.js" crossorigin></script>
2199
+ <script>
2200
+ const root = ReactDOM.createRoot(document.getElementById('graphiql'));
2201
+ root.render(
2202
+ React.createElement(GraphiQL, {
2203
+ fetcher: GraphiQL.createFetcher({ url: '${GRAPHQL_PATH}' }),
2204
+ })
2205
+ );
2206
+ </script>
2207
+ </body>
2208
+ </html>`;
2209
+ function createGraphiqlHandler(executor) {
2210
+ return async (req) => {
2211
+ const url = new URL(req.url);
2212
+ if (url.pathname !== GRAPHQL_PATH) return void 0;
2213
+ if (req.method === "GET") {
2214
+ return new Response(GRAPHIQL_HTML, {
2215
+ headers: { "Content-Type": "text/html; charset=utf-8" }
2216
+ });
2217
+ }
2218
+ if (req.method === "POST") {
2219
+ let body;
2220
+ try {
2221
+ body = await req.json();
2222
+ } catch {
2223
+ return new Response(JSON.stringify({ errors: [{ message: "Invalid JSON body" }] }), {
2224
+ status: 400,
2225
+ headers: { "Content-Type": "application/json" }
2226
+ });
2227
+ }
2228
+ if (!body.query) {
2229
+ return new Response(JSON.stringify({ errors: [{ message: 'Missing "query" field' }] }), {
2230
+ status: 400,
2231
+ headers: { "Content-Type": "application/json" }
2232
+ });
2233
+ }
2234
+ try {
2235
+ const result = await executor(body.query, body.variables);
2236
+ return new Response(JSON.stringify(result), {
2237
+ headers: { "Content-Type": "application/json" }
2238
+ });
2239
+ } catch (err) {
2240
+ return new Response(JSON.stringify({ errors: [{ message: err.message }] }), {
2241
+ status: 500,
2242
+ headers: { "Content-Type": "application/json" }
2243
+ });
2244
+ }
2245
+ }
2246
+ return new Response("Method Not Allowed", { status: 405 });
2247
+ };
2248
+ }
2249
+
1894
2250
  // src/build.ts
1895
2251
  async function processHtmlTemplate(templatePath) {
1896
2252
  const html = await fs.readFile(templatePath, "utf-8");
@@ -2092,6 +2448,24 @@ var dev = async (options) => {
2092
2448
  const handleProxy = createProxyHandler(options);
2093
2449
  const handleWS = upgradeHandler(options);
2094
2450
  const handler = options.fetch;
2451
+ let handleGraphiql = null;
2452
+ let devStaticCtx;
2453
+ if (options.sources && options.sources.length > 0) {
2454
+ console.log(`[hadars] Running ${options.sources.length} source plugin(s)\u2026`);
2455
+ try {
2456
+ const store = await runSources(options.sources);
2457
+ const executor = await buildSchemaExecutor(store);
2458
+ if (executor) {
2459
+ devStaticCtx = { graphql: executor };
2460
+ handleGraphiql = createGraphiqlHandler(executor);
2461
+ console.log(`[hadars] GraphiQL available at http://localhost:${port}${GRAPHQL_PATH}`);
2462
+ } else {
2463
+ console.warn("[hadars] `graphql` package not found \u2014 GraphiQL disabled. Run: npm install graphql");
2464
+ }
2465
+ } catch (err) {
2466
+ console.error("[hadars] Source plugin error:", err);
2467
+ }
2468
+ }
2095
2469
  const entry = pathMod2.resolve(__dirname2, options.entry);
2096
2470
  const hmrPort = options.hmrPort ?? port + 1;
2097
2471
  const packageDir2 = pathMod2.dirname(fileURLToPath2(import.meta.url));
@@ -2264,6 +2638,10 @@ var dev = async (options) => {
2264
2638
  if (res) return res;
2265
2639
  }
2266
2640
  if (handleWS && handleWS(request, ctx)) return void 0;
2641
+ if (handleGraphiql) {
2642
+ const graphiqlRes = await handleGraphiql(req);
2643
+ if (graphiqlRes) return graphiqlRes;
2644
+ }
2267
2645
  const proxied = await handleProxy(request);
2268
2646
  if (proxied) return proxied;
2269
2647
  const url = new URL(request.url);
@@ -2287,7 +2665,8 @@ var dev = async (options) => {
2287
2665
  lang: "en",
2288
2666
  getInitProps,
2289
2667
  getFinalProps
2290
- }
2668
+ },
2669
+ staticCtx: devStaticCtx
2291
2670
  });
2292
2671
  if (request.headers.get("Accept") === "application/json") {
2293
2672
  const { clientProps } = await finalize();
@@ -2468,6 +2847,63 @@ var run = async (options) => {
2468
2847
  );
2469
2848
  };
2470
2849
 
2850
+ // src/static.ts
2851
+ import { cp, mkdir, writeFile } from "node:fs/promises";
2852
+ import { join, basename } from "node:path";
2853
+ async function renderStaticSite(opts) {
2854
+ const { ssrModule, htmlSource, staticSrc, paths, outputDir } = opts;
2855
+ const staticCtx = {
2856
+ graphql: opts.graphql ?? (() => Promise.reject(
2857
+ new Error("[hadars] No graphql executor configured. Add a `graphql` function to your hadars.config.")
2858
+ ))
2859
+ };
2860
+ const getPrecontentHtml = makePrecontentHtmlGetter(Promise.resolve(htmlSource));
2861
+ await mkdir(outputDir, { recursive: true });
2862
+ const rendered = [];
2863
+ const errors = [];
2864
+ for (const urlPath of paths) {
2865
+ try {
2866
+ const req = parseRequest(new Request("http://localhost" + urlPath));
2867
+ const { head, getAppBody, finalize } = await getReactResponse(req, {
2868
+ document: {
2869
+ body: ssrModule.default,
2870
+ getInitProps: ssrModule.getInitProps,
2871
+ getFinalProps: ssrModule.getFinalProps
2872
+ },
2873
+ staticCtx
2874
+ });
2875
+ const bodyHtml = await getAppBody();
2876
+ const { clientProps } = await finalize();
2877
+ const headHtml = buildHeadHtml(head);
2878
+ const staticClientProps = { ...clientProps, __hadarsStatic: true };
2879
+ const html = await buildSsrHtml(bodyHtml, staticClientProps, headHtml, getPrecontentHtml);
2880
+ const cleanPath = urlPath.replace(/\/$/, "");
2881
+ const pageDir = cleanPath === "" ? outputDir : join(outputDir, cleanPath);
2882
+ await mkdir(pageDir, { recursive: true });
2883
+ await writeFile(join(pageDir, "index.html"), html, "utf-8");
2884
+ const serverData = staticClientProps.__serverData ?? {};
2885
+ await writeFile(
2886
+ join(pageDir, "index.json"),
2887
+ JSON.stringify({ serverData }),
2888
+ "utf-8"
2889
+ );
2890
+ rendered.push(urlPath);
2891
+ } catch (err) {
2892
+ errors.push({
2893
+ path: urlPath,
2894
+ error: err instanceof Error ? err : new Error(String(err))
2895
+ });
2896
+ }
2897
+ }
2898
+ const staticDest = join(outputDir, "static");
2899
+ await mkdir(staticDest, { recursive: true });
2900
+ await cp(staticSrc, staticDest, {
2901
+ recursive: true,
2902
+ filter: (src) => basename(src) !== "out.html"
2903
+ });
2904
+ return { rendered, errors };
2905
+ }
2906
+
2471
2907
  // cli-lib.ts
2472
2908
  var SUPPORTED = ["hadars.config.js", "hadars.config.mjs", "hadars.config.cjs", "hadars.config.ts"];
2473
2909
  function findConfig(cwd) {
@@ -2501,6 +2937,69 @@ async function loadConfig(configPath) {
2501
2937
  const mod = await import(url);
2502
2938
  return mod && (mod.default ?? mod);
2503
2939
  }
2940
+ async function exportStatic(config, outputDir, cwd) {
2941
+ if (!config.paths) {
2942
+ console.error(
2943
+ "Error: `paths` is not defined in your hadars config.\nAdd a `paths` function that returns the list of URLs to pre-render:\n\n paths: () => ['/', '/about', '/contact']\n"
2944
+ );
2945
+ process.exit(1);
2946
+ }
2947
+ console.log("Building hadars project...");
2948
+ await build({ ...config, mode: "production" });
2949
+ const ssrBundle = resolve(cwd, ".hadars", "index.ssr.js");
2950
+ const outHtml = resolve(cwd, ".hadars", "static", "out.html");
2951
+ const staticSrc = resolve(cwd, ".hadars", "static");
2952
+ if (!existsSync3(ssrBundle)) {
2953
+ console.error(`SSR bundle not found: ${ssrBundle}`);
2954
+ process.exit(1);
2955
+ }
2956
+ if (!existsSync3(outHtml)) {
2957
+ console.error(`HTML template not found: ${outHtml}`);
2958
+ process.exit(1);
2959
+ }
2960
+ const ssrModule = await import(pathToFileURL2(ssrBundle).href);
2961
+ const htmlSource = await readFile2(outHtml, "utf-8");
2962
+ const outDir = resolve(cwd, outputDir);
2963
+ let graphql = config.graphql;
2964
+ if (config.sources && config.sources.length > 0) {
2965
+ console.log(`Running ${config.sources.length} source plugin(s)...`);
2966
+ const store = await runSources(config.sources);
2967
+ if (!graphql) {
2968
+ const inferred = await buildSchemaExecutor(store);
2969
+ if (!inferred) {
2970
+ console.error(
2971
+ "Error: `graphql` package not found.\nSource plugins require graphql-js to be installed:\n\n npm install graphql\n"
2972
+ );
2973
+ process.exit(1);
2974
+ }
2975
+ graphql = inferred;
2976
+ console.log(`Schema inferred for types: ${store.getTypes().join(", ") || "(none)"}`);
2977
+ }
2978
+ }
2979
+ const staticCtx = {
2980
+ graphql: graphql ?? (() => Promise.reject(
2981
+ new Error("[hadars] No graphql executor configured. Add a `graphql` function to your hadars.config.")
2982
+ ))
2983
+ };
2984
+ const paths = await config.paths(staticCtx);
2985
+ console.log(`Pre-rendering ${paths.length} page(s)...`);
2986
+ const { rendered, errors } = await renderStaticSite({
2987
+ ssrModule,
2988
+ htmlSource,
2989
+ staticSrc,
2990
+ paths,
2991
+ outputDir: outDir,
2992
+ graphql
2993
+ });
2994
+ for (const p of rendered) console.log(` [200] ${p}`);
2995
+ for (const { path: path2, error } of errors) console.error(` [ERR] ${path2}: ${error.message}`);
2996
+ console.log(`
2997
+ Exported to ${outputDir}/`);
2998
+ if (errors.length > 0) console.log(` ${errors.length} page(s) failed`);
2999
+ console.log(`
3000
+ Serve locally:`);
3001
+ console.log(` npx serve ${outputDir}`);
3002
+ }
2504
3003
  async function bundleCloudflare(config, configPath, outputFile, cwd) {
2505
3004
  console.log("Building hadars project...");
2506
3005
  await build({ ...config, mode: "production" });
@@ -2514,8 +3013,8 @@ async function bundleCloudflare(config, configPath, outputFile, cwd) {
2514
3013
  console.error(`HTML template not found: ${outHtml}`);
2515
3014
  process.exit(1);
2516
3015
  }
2517
- const cloudflareModule = resolve(dirname(fileURLToPath3(import.meta.url)), "cloudflare.js");
2518
- const shimPath = join(cwd, `.hadars-cloudflare-shim-${Date.now()}.ts`);
3016
+ const cloudflareModule = resolve(dirname2(fileURLToPath3(import.meta.url)), "cloudflare.js");
3017
+ const shimPath = join2(cwd, `.hadars-cloudflare-shim-${Date.now()}.ts`);
2519
3018
  const shim = [
2520
3019
  `import * as ssrModule from ${JSON.stringify(ssrBundle)};`,
2521
3020
  `import outHtml from ${JSON.stringify(outHtml)};`,
@@ -2523,7 +3022,7 @@ async function bundleCloudflare(config, configPath, outputFile, cwd) {
2523
3022
  `import config from ${JSON.stringify(configPath)};`,
2524
3023
  `export default createCloudflareHandler(config as any, { ssrModule: ssrModule as any, outHtml });`
2525
3024
  ].join("\n") + "\n";
2526
- await writeFile(shimPath, shim, "utf-8");
3025
+ await writeFile2(shimPath, shim, "utf-8");
2527
3026
  try {
2528
3027
  const { build: esbuild } = await import("esbuild");
2529
3028
  console.log(`Bundling Cloudflare Worker \u2192 ${outputFile}`);
@@ -2575,8 +3074,8 @@ async function bundleLambda(config, configPath, outputFile, cwd) {
2575
3074
  console.error(`HTML template not found: ${outHtml}`);
2576
3075
  process.exit(1);
2577
3076
  }
2578
- const lambdaModule = resolve(dirname(fileURLToPath3(import.meta.url)), "lambda.js");
2579
- const shimPath = join(cwd, `.hadars-lambda-shim-${Date.now()}.ts`);
3077
+ const lambdaModule = resolve(dirname2(fileURLToPath3(import.meta.url)), "lambda.js");
3078
+ const shimPath = join2(cwd, `.hadars-lambda-shim-${Date.now()}.ts`);
2580
3079
  const shim = [
2581
3080
  `import * as ssrModule from ${JSON.stringify(ssrBundle)};`,
2582
3081
  `import outHtml from ${JSON.stringify(outHtml)};`,
@@ -2584,7 +3083,7 @@ async function bundleLambda(config, configPath, outputFile, cwd) {
2584
3083
  `import config from ${JSON.stringify(configPath)};`,
2585
3084
  `export const handler = createLambdaHandler(config as any, { ssrModule: ssrModule as any, outHtml });`
2586
3085
  ].join("\n") + "\n";
2587
- await writeFile(shimPath, shim, "utf-8");
3086
+ await writeFile2(shimPath, shim, "utf-8");
2588
3087
  try {
2589
3088
  const { build: esbuild } = await import("esbuild");
2590
3089
  console.log(`Bundling Lambda handler \u2192 ${outputFile}`);
@@ -2855,10 +3354,10 @@ async function createProject(name, cwd) {
2855
3354
  process.exit(1);
2856
3355
  }
2857
3356
  console.log(`Creating hadars project in ${dir}`);
2858
- await mkdir(join(dir, "src"), { recursive: true });
3357
+ await mkdir2(join2(dir, "src"), { recursive: true });
2859
3358
  for (const [file, template] of Object.entries(TEMPLATES)) {
2860
3359
  const content = template(name);
2861
- await writeFile(join(dir, file), content, "utf-8");
3360
+ await writeFile2(join2(dir, file), content, "utf-8");
2862
3361
  console.log(` created ${file}`);
2863
3362
  }
2864
3363
  console.log(`
@@ -2870,7 +3369,7 @@ Done! Next steps:
2870
3369
  `);
2871
3370
  }
2872
3371
  function usage() {
2873
- console.log("Usage: hadars <new <name> | dev | build | run | export lambda [output.mjs] | export cloudflare [output.mjs]>");
3372
+ console.log("Usage: hadars <new <name> | dev | build | run | export lambda [output.mjs] | export cloudflare [output.mjs] | export static [outDir]>");
2874
3373
  }
2875
3374
  async function runCli(argv, cwd = process.cwd()) {
2876
3375
  const cmd = argv[2];
@@ -2921,8 +3420,12 @@ async function runCli(argv, cwd = process.cwd()) {
2921
3420
  const outputFile = resolve(cwd, argv[4] ?? "cloudflare.mjs");
2922
3421
  await bundleCloudflare(cfg, configPath, outputFile, cwd);
2923
3422
  process.exit(0);
3423
+ } else if (subCmd === "static") {
3424
+ const outDirArg = argv[4] ?? "out";
3425
+ await exportStatic(cfg, outDirArg, cwd);
3426
+ process.exit(0);
2924
3427
  } else {
2925
- console.error(`Unknown export target: ${subCmd ?? "(none)"}. Supported: lambda, cloudflare`);
3428
+ console.error(`Unknown export target: ${subCmd ?? "(none)"}. Supported: lambda, cloudflare, static`);
2926
3429
  process.exit(1);
2927
3430
  }
2928
3431
  }
@@ -1186,7 +1186,7 @@ var getReactResponse = async (req, opts) => {
1186
1186
  head: { title: "Hadars App", meta: {}, link: {}, style: {}, script: {}, status: 200 }
1187
1187
  };
1188
1188
  let props = {
1189
- ...getInitProps ? await getInitProps(req) : {},
1189
+ ...getInitProps ? await getInitProps(req, opts.staticCtx) : {},
1190
1190
  location: req.location,
1191
1191
  context
1192
1192
  };
@@ -1212,7 +1212,7 @@ var getReactResponse = async (req, opts) => {
1212
1212
  }
1213
1213
  };
1214
1214
  const finalize = async () => {
1215
- const { context: _, ...restProps } = getFinalProps ? await getFinalProps(props) : props;
1215
+ const restProps = getFinalProps ? await getFinalProps(props) : props;
1216
1216
  const serverData = {};
1217
1217
  let hasServerData = false;
1218
1218
  for (const [key, entry] of unsuspend.cache) {
@@ -1,4 +1,4 @@
1
- import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-DEBSYAQl.cjs';
1
+ import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-mKu5txjW.cjs';
2
2
 
3
3
  /**
4
4
  * Cloudflare Workers adapter for hadars.
@@ -1,4 +1,4 @@
1
- import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-DEBSYAQl.js';
1
+ import { H as HadarsEntryModule, a as HadarsOptions } from './hadars-mKu5txjW.js';
2
2
 
3
3
  /**
4
4
  * Cloudflare Workers adapter for hadars.
@@ -6,7 +6,7 @@ import {
6
6
  getReactResponse,
7
7
  makePrecontentHtmlGetter,
8
8
  parseRequest
9
- } from "./chunk-HWOLYLPF.js";
9
+ } from "./chunk-H72BZXOA.js";
10
10
  import "./chunk-LY5MTHFV.js";
11
11
  import "./chunk-OS3V4CPN.js";
12
12