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/README.md +177 -14
- package/cli-lib.ts +97 -5
- package/dist/{chunk-HWOLYLPF.js → chunk-H72BZXOA.js} +2 -2
- package/dist/cli.js +519 -16
- package/dist/cloudflare.cjs +2 -2
- package/dist/cloudflare.d.cts +1 -1
- package/dist/cloudflare.d.ts +1 -1
- package/dist/cloudflare.js +1 -1
- package/dist/{hadars-DEBSYAQl.d.cts → hadars-mKu5txjW.d.cts} +93 -2
- package/dist/{hadars-DEBSYAQl.d.ts → hadars-mKu5txjW.d.ts} +93 -2
- package/dist/index.cjs +11 -8
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -8
- package/dist/lambda.cjs +2 -2
- package/dist/lambda.d.cts +1 -1
- package/dist/lambda.d.ts +1 -1
- package/dist/lambda.js +1 -1
- package/dist/utils/Head.tsx +17 -8
- package/dist/utils/clientScript.tsx +9 -0
- package/index.ts +3 -0
- package/package.json +1 -1
- package/src/build.ts +29 -0
- package/src/index.tsx +3 -0
- package/src/source/context.ts +113 -0
- package/src/source/graphiql.ts +96 -0
- package/src/source/inference.ts +188 -0
- package/src/source/runner.ts +114 -0
- package/src/source/store.ts +48 -0
- package/src/static.ts +106 -0
- package/src/types/hadars.ts +91 -1
- package/src/utils/Head.tsx +17 -8
- package/src/utils/clientScript.tsx +9 -0
- package/src/utils/response.tsx +4 -3
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
|
|
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(
|
|
2518
|
-
const shimPath =
|
|
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
|
|
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(
|
|
2579
|
-
const shimPath =
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|
package/dist/cloudflare.cjs
CHANGED
|
@@ -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
|
|
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) {
|
package/dist/cloudflare.d.cts
CHANGED
package/dist/cloudflare.d.ts
CHANGED