hadars 0.2.0 → 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 +247 -15
- package/cli-lib.ts +184 -15
- package/dist/chunk-H72BZXOA.js +332 -0
- package/dist/cli.js +596 -23
- package/dist/cloudflare.cjs +1394 -0
- package/dist/cloudflare.d.cts +64 -0
- package/dist/cloudflare.d.ts +64 -0
- package/dist/cloudflare.js +68 -0
- package/dist/{hadars-Bh-V5YXg.d.cts → hadars-mKu5txjW.d.cts} +93 -37
- package/dist/{hadars-Bh-V5YXg.d.ts → hadars-mKu5txjW.d.ts} +93 -37
- package/dist/index.cjs +140 -164
- package/dist/index.d.cts +5 -11
- package/dist/index.d.ts +5 -11
- package/dist/index.js +140 -163
- package/dist/lambda.cjs +6 -2
- package/dist/lambda.d.cts +1 -2
- package/dist/lambda.d.ts +1 -2
- package/dist/lambda.js +10 -317
- package/dist/ssr-render-worker.js +3 -2
- package/dist/utils/Head.tsx +149 -195
- package/dist/utils/clientScript.tsx +9 -0
- package/index.ts +3 -0
- package/package.json +7 -2
- package/src/build.ts +29 -0
- package/src/cloudflare.ts +139 -0
- package/src/index.tsx +3 -3
- 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/ssr-render-worker.ts +4 -1
- package/src/static.ts +106 -0
- package/src/types/hadars.ts +91 -2
- package/src/utils/Head.tsx +149 -195
- package/src/utils/clientScript.tsx +9 -0
- package/src/utils/response.tsx +10 -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,29 +1174,33 @@ 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
|
};
|
|
1181
1181
|
const unsuspend = { cache: /* @__PURE__ */ new Map() };
|
|
1182
1182
|
globalThis.__hadarsUnsuspend = unsuspend;
|
|
1183
|
+
globalThis.__hadarsContext = context;
|
|
1183
1184
|
const element = createElement(App, props);
|
|
1184
1185
|
try {
|
|
1185
1186
|
await renderPreflight(element);
|
|
1186
1187
|
} finally {
|
|
1187
1188
|
globalThis.__hadarsUnsuspend = null;
|
|
1189
|
+
globalThis.__hadarsContext = null;
|
|
1188
1190
|
}
|
|
1189
1191
|
const status = context.head.status;
|
|
1190
1192
|
const getAppBody = async () => {
|
|
1191
1193
|
globalThis.__hadarsUnsuspend = unsuspend;
|
|
1194
|
+
globalThis.__hadarsContext = context;
|
|
1192
1195
|
try {
|
|
1193
1196
|
return await renderToString(element);
|
|
1194
1197
|
} finally {
|
|
1195
1198
|
globalThis.__hadarsUnsuspend = null;
|
|
1199
|
+
globalThis.__hadarsContext = null;
|
|
1196
1200
|
}
|
|
1197
1201
|
};
|
|
1198
1202
|
const finalize = async () => {
|
|
1199
|
-
const
|
|
1203
|
+
const restProps = getFinalProps ? await getFinalProps(props) : props;
|
|
1200
1204
|
const serverData = {};
|
|
1201
1205
|
let hasServerData = false;
|
|
1202
1206
|
for (const [key, entry] of unsuspend.cache) {
|
|
@@ -1792,6 +1796,11 @@ function buildSsrResponse(head, status, getAppBody, finalize, getPrecontentHtml)
|
|
|
1792
1796
|
status
|
|
1793
1797
|
});
|
|
1794
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
|
+
}
|
|
1795
1804
|
var makePrecontentHtmlGetter = (htmlFilePromise) => {
|
|
1796
1805
|
let preHead = null;
|
|
1797
1806
|
let postHead = null;
|
|
@@ -1887,6 +1896,357 @@ function createRenderCache(opts, handler) {
|
|
|
1887
1896
|
};
|
|
1888
1897
|
}
|
|
1889
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
|
+
|
|
1890
2250
|
// src/build.ts
|
|
1891
2251
|
async function processHtmlTemplate(templatePath) {
|
|
1892
2252
|
const html = await fs.readFile(templatePath, "utf-8");
|
|
@@ -2088,6 +2448,24 @@ var dev = async (options) => {
|
|
|
2088
2448
|
const handleProxy = createProxyHandler(options);
|
|
2089
2449
|
const handleWS = upgradeHandler(options);
|
|
2090
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
|
+
}
|
|
2091
2469
|
const entry = pathMod2.resolve(__dirname2, options.entry);
|
|
2092
2470
|
const hmrPort = options.hmrPort ?? port + 1;
|
|
2093
2471
|
const packageDir2 = pathMod2.dirname(fileURLToPath2(import.meta.url));
|
|
@@ -2260,6 +2638,10 @@ var dev = async (options) => {
|
|
|
2260
2638
|
if (res) return res;
|
|
2261
2639
|
}
|
|
2262
2640
|
if (handleWS && handleWS(request, ctx)) return void 0;
|
|
2641
|
+
if (handleGraphiql) {
|
|
2642
|
+
const graphiqlRes = await handleGraphiql(req);
|
|
2643
|
+
if (graphiqlRes) return graphiqlRes;
|
|
2644
|
+
}
|
|
2263
2645
|
const proxied = await handleProxy(request);
|
|
2264
2646
|
if (proxied) return proxied;
|
|
2265
2647
|
const url = new URL(request.url);
|
|
@@ -2283,7 +2665,8 @@ var dev = async (options) => {
|
|
|
2283
2665
|
lang: "en",
|
|
2284
2666
|
getInitProps,
|
|
2285
2667
|
getFinalProps
|
|
2286
|
-
}
|
|
2668
|
+
},
|
|
2669
|
+
staticCtx: devStaticCtx
|
|
2287
2670
|
});
|
|
2288
2671
|
if (request.headers.get("Accept") === "application/json") {
|
|
2289
2672
|
const { clientProps } = await finalize();
|
|
@@ -2464,6 +2847,63 @@ var run = async (options) => {
|
|
|
2464
2847
|
);
|
|
2465
2848
|
};
|
|
2466
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
|
+
|
|
2467
2907
|
// cli-lib.ts
|
|
2468
2908
|
var SUPPORTED = ["hadars.config.js", "hadars.config.mjs", "hadars.config.cjs", "hadars.config.ts"];
|
|
2469
2909
|
function findConfig(cwd) {
|
|
@@ -2497,6 +2937,130 @@ async function loadConfig(configPath) {
|
|
|
2497
2937
|
const mod = await import(url);
|
|
2498
2938
|
return mod && (mod.default ?? mod);
|
|
2499
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
|
+
}
|
|
3003
|
+
async function bundleCloudflare(config, configPath, outputFile, cwd) {
|
|
3004
|
+
console.log("Building hadars project...");
|
|
3005
|
+
await build({ ...config, mode: "production" });
|
|
3006
|
+
const ssrBundle = resolve(cwd, ".hadars", "index.ssr.js");
|
|
3007
|
+
const outHtml = resolve(cwd, ".hadars", "static", "out.html");
|
|
3008
|
+
if (!existsSync3(ssrBundle)) {
|
|
3009
|
+
console.error(`SSR bundle not found: ${ssrBundle}`);
|
|
3010
|
+
process.exit(1);
|
|
3011
|
+
}
|
|
3012
|
+
if (!existsSync3(outHtml)) {
|
|
3013
|
+
console.error(`HTML template not found: ${outHtml}`);
|
|
3014
|
+
process.exit(1);
|
|
3015
|
+
}
|
|
3016
|
+
const cloudflareModule = resolve(dirname2(fileURLToPath3(import.meta.url)), "cloudflare.js");
|
|
3017
|
+
const shimPath = join2(cwd, `.hadars-cloudflare-shim-${Date.now()}.ts`);
|
|
3018
|
+
const shim = [
|
|
3019
|
+
`import * as ssrModule from ${JSON.stringify(ssrBundle)};`,
|
|
3020
|
+
`import outHtml from ${JSON.stringify(outHtml)};`,
|
|
3021
|
+
`import { createCloudflareHandler } from ${JSON.stringify(cloudflareModule)};`,
|
|
3022
|
+
`import config from ${JSON.stringify(configPath)};`,
|
|
3023
|
+
`export default createCloudflareHandler(config as any, { ssrModule: ssrModule as any, outHtml });`
|
|
3024
|
+
].join("\n") + "\n";
|
|
3025
|
+
await writeFile2(shimPath, shim, "utf-8");
|
|
3026
|
+
try {
|
|
3027
|
+
const { build: esbuild } = await import("esbuild");
|
|
3028
|
+
console.log(`Bundling Cloudflare Worker \u2192 ${outputFile}`);
|
|
3029
|
+
await esbuild({
|
|
3030
|
+
entryPoints: [shimPath],
|
|
3031
|
+
bundle: true,
|
|
3032
|
+
// 'browser' avoids Node.js built-in shims; CF Workers uses Web APIs.
|
|
3033
|
+
// If you use node:* APIs in your app code, add nodejs_compat to wrangler.toml.
|
|
3034
|
+
platform: "browser",
|
|
3035
|
+
format: "esm",
|
|
3036
|
+
target: ["es2022"],
|
|
3037
|
+
outfile: outputFile,
|
|
3038
|
+
sourcemap: false,
|
|
3039
|
+
loader: { ".html": "text", ".tsx": "tsx", ".ts": "ts" },
|
|
3040
|
+
// @rspack/* is build-time only — never imported at Worker runtime.
|
|
3041
|
+
external: ["@rspack/*"],
|
|
3042
|
+
// Cloudflare Workers supports the Web Crypto API natively; suppress
|
|
3043
|
+
// esbuild's attempt to polyfill node:crypto.
|
|
3044
|
+
define: { "global": "globalThis" }
|
|
3045
|
+
});
|
|
3046
|
+
console.log(`Cloudflare Worker bundle written to ${outputFile}`);
|
|
3047
|
+
console.log(`
|
|
3048
|
+
Deploy instructions:`);
|
|
3049
|
+
console.log(` 1. Ensure wrangler.toml points to the output file:`);
|
|
3050
|
+
console.log(` name = "my-app"`);
|
|
3051
|
+
console.log(` main = "${outputFile}"`);
|
|
3052
|
+
console.log(` compatibility_date = "2024-09-23"`);
|
|
3053
|
+
console.log(` compatibility_flags = ["nodejs_compat"]`);
|
|
3054
|
+
console.log(` 2. Upload .hadars/static/ assets to R2 (or another CDN):`);
|
|
3055
|
+
console.log(` wrangler r2 object put my-bucket/assets/ --file .hadars/static/ --recursive`);
|
|
3056
|
+
console.log(` 3. Add a route rule in wrangler.toml to send *.js / *.css to R2`);
|
|
3057
|
+
console.log(` and all other requests to the Worker.`);
|
|
3058
|
+
console.log(` 4. Deploy: wrangler deploy`);
|
|
3059
|
+
} finally {
|
|
3060
|
+
await unlink(shimPath).catch(() => {
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
2500
3064
|
async function bundleLambda(config, configPath, outputFile, cwd) {
|
|
2501
3065
|
console.log("Building hadars project...");
|
|
2502
3066
|
await build({ ...config, mode: "production" });
|
|
@@ -2510,8 +3074,8 @@ async function bundleLambda(config, configPath, outputFile, cwd) {
|
|
|
2510
3074
|
console.error(`HTML template not found: ${outHtml}`);
|
|
2511
3075
|
process.exit(1);
|
|
2512
3076
|
}
|
|
2513
|
-
const lambdaModule = resolve(
|
|
2514
|
-
const shimPath =
|
|
3077
|
+
const lambdaModule = resolve(dirname2(fileURLToPath3(import.meta.url)), "lambda.js");
|
|
3078
|
+
const shimPath = join2(cwd, `.hadars-lambda-shim-${Date.now()}.ts`);
|
|
2515
3079
|
const shim = [
|
|
2516
3080
|
`import * as ssrModule from ${JSON.stringify(ssrBundle)};`,
|
|
2517
3081
|
`import outHtml from ${JSON.stringify(outHtml)};`,
|
|
@@ -2519,7 +3083,7 @@ async function bundleLambda(config, configPath, outputFile, cwd) {
|
|
|
2519
3083
|
`import config from ${JSON.stringify(configPath)};`,
|
|
2520
3084
|
`export const handler = createLambdaHandler(config as any, { ssrModule: ssrModule as any, outHtml });`
|
|
2521
3085
|
].join("\n") + "\n";
|
|
2522
|
-
await
|
|
3086
|
+
await writeFile2(shimPath, shim, "utf-8");
|
|
2523
3087
|
try {
|
|
2524
3088
|
const { build: esbuild } = await import("esbuild");
|
|
2525
3089
|
console.log(`Bundling Lambda handler \u2192 ${outputFile}`);
|
|
@@ -2598,7 +3162,7 @@ export default config;
|
|
|
2598
3162
|
dist/
|
|
2599
3163
|
`,
|
|
2600
3164
|
"src/App.tsx": () => `import React from 'react';
|
|
2601
|
-
import {
|
|
3165
|
+
import { HadarsHead, type HadarsApp } from 'hadars';
|
|
2602
3166
|
|
|
2603
3167
|
const css = \`
|
|
2604
3168
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
@@ -2710,15 +3274,15 @@ const css = \`
|
|
|
2710
3274
|
|
|
2711
3275
|
\`;
|
|
2712
3276
|
|
|
2713
|
-
const App: HadarsApp<{}> = (
|
|
3277
|
+
const App: HadarsApp<{}> = () => {
|
|
2714
3278
|
const [count, setCount] = React.useState(0);
|
|
2715
3279
|
|
|
2716
3280
|
return (
|
|
2717
|
-
|
|
3281
|
+
<>
|
|
2718
3282
|
<HadarsHead status={200}>
|
|
2719
3283
|
<title>My App</title>
|
|
2720
|
-
<meta
|
|
2721
|
-
<style id="app-styles" dangerouslySetInnerHTML={{ __html: css }} />
|
|
3284
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
3285
|
+
<style data-id="app-styles" dangerouslySetInnerHTML={{ __html: css }} />
|
|
2722
3286
|
</HadarsHead>
|
|
2723
3287
|
|
|
2724
3288
|
<nav className="nav">
|
|
@@ -2776,7 +3340,7 @@ const App: HadarsApp<{}> = ({ context }) => {
|
|
|
2776
3340
|
</div>
|
|
2777
3341
|
</div>
|
|
2778
3342
|
|
|
2779
|
-
|
|
3343
|
+
</>
|
|
2780
3344
|
);
|
|
2781
3345
|
};
|
|
2782
3346
|
|
|
@@ -2790,10 +3354,10 @@ async function createProject(name, cwd) {
|
|
|
2790
3354
|
process.exit(1);
|
|
2791
3355
|
}
|
|
2792
3356
|
console.log(`Creating hadars project in ${dir}`);
|
|
2793
|
-
await
|
|
3357
|
+
await mkdir2(join2(dir, "src"), { recursive: true });
|
|
2794
3358
|
for (const [file, template] of Object.entries(TEMPLATES)) {
|
|
2795
3359
|
const content = template(name);
|
|
2796
|
-
await
|
|
3360
|
+
await writeFile2(join2(dir, file), content, "utf-8");
|
|
2797
3361
|
console.log(` created ${file}`);
|
|
2798
3362
|
}
|
|
2799
3363
|
console.log(`
|
|
@@ -2805,7 +3369,7 @@ Done! Next steps:
|
|
|
2805
3369
|
`);
|
|
2806
3370
|
}
|
|
2807
3371
|
function usage() {
|
|
2808
|
-
console.log("Usage: hadars <new <name> | dev | build | run | export lambda [output.mjs]>");
|
|
3372
|
+
console.log("Usage: hadars <new <name> | dev | build | run | export lambda [output.mjs] | export cloudflare [output.mjs] | export static [outDir]>");
|
|
2809
3373
|
}
|
|
2810
3374
|
async function runCli(argv, cwd = process.cwd()) {
|
|
2811
3375
|
const cmd = argv[2];
|
|
@@ -2848,13 +3412,22 @@ async function runCli(argv, cwd = process.cwd()) {
|
|
|
2848
3412
|
process.exit(0);
|
|
2849
3413
|
case "export": {
|
|
2850
3414
|
const subCmd = argv[3];
|
|
2851
|
-
if (subCmd
|
|
2852
|
-
|
|
3415
|
+
if (subCmd === "lambda") {
|
|
3416
|
+
const outputFile = resolve(cwd, argv[4] ?? "lambda.mjs");
|
|
3417
|
+
await bundleLambda(cfg, configPath, outputFile, cwd);
|
|
3418
|
+
process.exit(0);
|
|
3419
|
+
} else if (subCmd === "cloudflare") {
|
|
3420
|
+
const outputFile = resolve(cwd, argv[4] ?? "cloudflare.mjs");
|
|
3421
|
+
await bundleCloudflare(cfg, configPath, outputFile, cwd);
|
|
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);
|
|
3427
|
+
} else {
|
|
3428
|
+
console.error(`Unknown export target: ${subCmd ?? "(none)"}. Supported: lambda, cloudflare, static`);
|
|
2853
3429
|
process.exit(1);
|
|
2854
3430
|
}
|
|
2855
|
-
const outputFile = resolve(cwd, argv[4] ?? "lambda.mjs");
|
|
2856
|
-
await bundleLambda(cfg, configPath, outputFile, cwd);
|
|
2857
|
-
process.exit(0);
|
|
2858
3431
|
}
|
|
2859
3432
|
case "run":
|
|
2860
3433
|
console.log("Running project...");
|