@vercel/backends 0.7.1 → 0.8.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/dev/cron-host.d.mts +1 -0
- package/dist/dev/cron-host.mjs +51 -0
- package/dist/index.mjs +635 -631
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { builtinModules, createRequire } from "node:module";
|
|
2
|
-
import { createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
2
|
import { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
4
3
|
import { delimiter as delimiter$1, dirname as dirname$1, join as join$1 } from "path";
|
|
5
4
|
import { FileBlob, FileFsRef, NodejsLambda, Span, createDiagnostics, debug, defaultCachePathGlob, download, execCommand, generateProjectManifest, getEnvForPackageManager, getInternalServiceCronPath, getLambdaOptionsFromFunction, getNodeBinPaths, getNodeVersion, getReportedServiceType, glob, isBackendFramework, isBunVersion, isExperimentalBackendsWithoutIntrospectionEnabled, isScheduleTriggeredService, runNpmInstall, runPackageJsonScript, scanParentDirs } from "@vercel/build-utils";
|
|
5
|
+
import { createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { lstat, readFile, rm, stat } from "node:fs/promises";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
7
8
|
import { build as build$2 } from "rolldown";
|
|
8
9
|
import { exports } from "resolve.exports";
|
|
9
10
|
import { isNativeError } from "node:util/types";
|
|
@@ -11,7 +12,6 @@ import { nodeFileTrace as nodeFileTrace$1, resolve as resolve$1 } from "@vercel/
|
|
|
11
12
|
import { transform } from "oxc-transform";
|
|
12
13
|
import execa from "execa";
|
|
13
14
|
import { readFile as readFile$1, writeFile } from "fs/promises";
|
|
14
|
-
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { spawn } from "node:child_process";
|
|
16
16
|
import { tmpdir } from "node:os";
|
|
17
17
|
import { z } from "zod";
|
|
@@ -70,222 +70,104 @@ async function maybeExecBuildCommand(args, { spawnEnv }) {
|
|
|
70
70
|
const diagnostics = createDiagnostics("node");
|
|
71
71
|
|
|
72
72
|
//#endregion
|
|
73
|
-
//#region src/
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
73
|
+
//#region src/find-entrypoint.ts
|
|
74
|
+
const frameworks = [
|
|
75
|
+
"express",
|
|
76
|
+
"hono",
|
|
77
|
+
"elysia",
|
|
78
|
+
"fastify",
|
|
79
|
+
"@nestjs/core",
|
|
80
|
+
"h3"
|
|
81
|
+
];
|
|
82
|
+
const entrypointFilenames = [
|
|
83
|
+
"app",
|
|
84
|
+
"index",
|
|
85
|
+
"server",
|
|
86
|
+
"main",
|
|
87
|
+
"src/app",
|
|
88
|
+
"src/index",
|
|
89
|
+
"src/server",
|
|
90
|
+
"src/main"
|
|
91
|
+
];
|
|
92
|
+
const entrypointExtensions = [
|
|
93
|
+
"js",
|
|
94
|
+
"cjs",
|
|
95
|
+
"mjs",
|
|
96
|
+
"ts",
|
|
97
|
+
"cts",
|
|
98
|
+
"mts"
|
|
99
|
+
];
|
|
100
|
+
const entrypoints = entrypointFilenames.flatMap((filename) => entrypointExtensions.map((extension) => `${filename}.${extension}`));
|
|
101
|
+
const createFrameworkRegex = (framework) => new RegExp(`(?:from|require|import)\\s*(?:\\(\\s*)?["']${framework}["']\\s*(?:\\))?`, "g");
|
|
102
|
+
const findEntrypoint = async (cwd) => {
|
|
103
|
+
let packageJsonObject = null;
|
|
104
|
+
try {
|
|
105
|
+
const packageJson = await readFile(join(cwd, "package.json"), "utf-8");
|
|
106
|
+
packageJsonObject = JSON.parse(packageJson);
|
|
107
|
+
} catch (_) {}
|
|
108
|
+
if (packageJsonObject) {
|
|
109
|
+
const main = typeof packageJsonObject.main === "string" ? packageJsonObject.main.trim() : "";
|
|
110
|
+
if (main) {
|
|
111
|
+
const abs = resolve(cwd, main);
|
|
112
|
+
const rel = relative(cwd, abs);
|
|
113
|
+
if (!rel.startsWith("..") && rel !== "") try {
|
|
114
|
+
await readFile(abs, "utf-8");
|
|
115
|
+
return rel.split(sep).join("/");
|
|
116
|
+
} catch {}
|
|
95
117
|
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
const subpath = bareImport.startsWith(pkgName) ? `.${bareImport.slice(pkgName.length)}` || "." : ".";
|
|
114
|
-
try {
|
|
115
|
-
if (exports(pkgJson, subpath, {
|
|
116
|
-
require: false,
|
|
117
|
-
conditions: ["node", "import"]
|
|
118
|
-
})?.some((p) => p === relativePath || p === `./${relativePath}`)) return false;
|
|
119
|
-
if (exports(pkgJson, subpath, {
|
|
120
|
-
require: true,
|
|
121
|
-
conditions: ["node", "require"]
|
|
122
|
-
})?.some((p) => p === relativePath || p === `./${relativePath}`)) return true;
|
|
123
|
-
} catch (err) {
|
|
124
|
-
console.warn("Export resolution failed::", err);
|
|
118
|
+
}
|
|
119
|
+
let framework;
|
|
120
|
+
if (packageJsonObject) framework = frameworks.find((framework$1) => packageJsonObject.dependencies?.[framework$1]);
|
|
121
|
+
if (!framework) for (const entrypoint of entrypoints) {
|
|
122
|
+
const entrypointPath = join(cwd, entrypoint);
|
|
123
|
+
try {
|
|
124
|
+
await readFile(entrypointPath, "utf-8");
|
|
125
|
+
return entrypoint;
|
|
126
|
+
} catch (_) {}
|
|
127
|
+
}
|
|
128
|
+
const regex = framework ? createFrameworkRegex(framework) : void 0;
|
|
129
|
+
for (const entrypoint of entrypoints) {
|
|
130
|
+
const entrypointPath = join(cwd, entrypoint);
|
|
131
|
+
try {
|
|
132
|
+
const content = await readFile(entrypointPath, "utf-8");
|
|
133
|
+
if (regex) {
|
|
134
|
+
if (regex.test(content)) return entrypoint;
|
|
125
135
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
} catch (_) {}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const findEntrypointOrThrow = async (cwd) => {
|
|
140
|
+
const entrypoint = await findEntrypoint(cwd);
|
|
141
|
+
if (!entrypoint) throw new Error(`No entrypoint found in "${cwd}". Set package.json "main" to a server file, or add one of: ${entrypoints.join(", ")}`);
|
|
142
|
+
return entrypoint;
|
|
143
|
+
};
|
|
144
|
+
const findEntrypointWithHintOrThrow = async (workPath, configured) => {
|
|
145
|
+
const explicit = configured && configured !== "package.json" ? configured : null;
|
|
146
|
+
if (explicit && existsSync(join(workPath, explicit))) return explicit;
|
|
147
|
+
return findEntrypointOrThrow(workPath);
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Normalized entrypoint detector for Node services. Wraps {@link findEntrypoint}
|
|
151
|
+
* and returns the result in the shared {@link DetectedEntrypoint} shape consumed
|
|
152
|
+
* by services auto-detection.
|
|
153
|
+
*/
|
|
154
|
+
const detectEntrypoint = async ({ workPath }) => {
|
|
155
|
+
const file = await findEntrypoint(workPath);
|
|
156
|
+
if (!file) return null;
|
|
136
157
|
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
order: "pre",
|
|
140
|
-
async handler(id, importer, rOpts) {
|
|
141
|
-
if (id.startsWith(CJS_SHIM_PREFIX$1)) return {
|
|
142
|
-
id,
|
|
143
|
-
external: false
|
|
144
|
-
};
|
|
145
|
-
const resolved = await this.resolve(id, importer, rOpts);
|
|
146
|
-
if (builtinModules.includes(id)) return {
|
|
147
|
-
id: `node:${id}`,
|
|
148
|
-
external: true
|
|
149
|
-
};
|
|
150
|
-
if (resolved?.id && isLocalImport(resolved.id)) tracedPaths.add(resolved.id);
|
|
151
|
-
if (importer?.startsWith(CJS_SHIM_PREFIX$1) && isBareImport(id)) return {
|
|
152
|
-
id,
|
|
153
|
-
external: true
|
|
154
|
-
};
|
|
155
|
-
if (importer && isBareImport(id) && resolved?.id?.includes("node_modules")) {
|
|
156
|
-
if (args.shimBareImports) {
|
|
157
|
-
if (await isCommonJS(id, resolved.id, resolved)) {
|
|
158
|
-
const importerPkgJsonPath = (await this.resolve(importer))?.packageJsonPath;
|
|
159
|
-
if (importerPkgJsonPath) {
|
|
160
|
-
const importerPkgDir = relative(args.repoRootPath, dirname(importerPkgJsonPath));
|
|
161
|
-
const shimId$1 = `${CJS_SHIM_PREFIX$1}${importerPkgDir.replace(/\//g, "_")}_${id.replace(/\//g, "_")}`;
|
|
162
|
-
shimMeta.set(shimId$1, {
|
|
163
|
-
pkgDir: importerPkgDir,
|
|
164
|
-
pkgName: id
|
|
165
|
-
});
|
|
166
|
-
return {
|
|
167
|
-
id: shimId$1,
|
|
168
|
-
external: false
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
const shimId = `${CJS_SHIM_PREFIX$1}${id.replace(/\//g, "_")}`;
|
|
172
|
-
shimMeta.set(shimId, {
|
|
173
|
-
pkgDir: "",
|
|
174
|
-
pkgName: id
|
|
175
|
-
});
|
|
176
|
-
return {
|
|
177
|
-
id: shimId,
|
|
178
|
-
external: false
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return {
|
|
183
|
-
external: true,
|
|
184
|
-
id
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
if (importer && isBareImport(id)) return resolved;
|
|
188
|
-
return {
|
|
189
|
-
external: true,
|
|
190
|
-
...resolved,
|
|
191
|
-
id: resolved?.id || id
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
load: { async handler(id) {
|
|
196
|
-
if (id.startsWith(CJS_SHIM_PREFIX$1)) {
|
|
197
|
-
const meta = shimMeta.get(id);
|
|
198
|
-
if (!meta) return { code: `module.exports = require('${id.slice(10)}');` };
|
|
199
|
-
const { pkgDir, pkgName } = meta;
|
|
200
|
-
if (pkgDir) return { code: `
|
|
201
|
-
import { createRequire } from 'node:module';
|
|
202
|
-
import { fileURLToPath } from 'node:url';
|
|
203
|
-
import { dirname, join } from 'node:path';
|
|
204
|
-
|
|
205
|
-
const requireFromContext = createRequire(join(dirname(fileURLToPath(import.meta.url)), '${join("..", pkgDir, "package.json")}'));
|
|
206
|
-
module.exports = requireFromContext('${pkgName}');
|
|
207
|
-
`.trim() };
|
|
208
|
-
return { code: `module.exports = require('${pkgName}');` };
|
|
209
|
-
}
|
|
210
|
-
return null;
|
|
211
|
-
} }
|
|
158
|
+
kind: "file",
|
|
159
|
+
entrypoint: file
|
|
212
160
|
};
|
|
213
161
|
};
|
|
214
162
|
|
|
215
163
|
//#endregion
|
|
216
|
-
//#region src/
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
follow: true,
|
|
224
|
-
includeDirectories: true
|
|
225
|
-
});
|
|
226
|
-
for (const file of Object.keys(compiledSourceFiles)) files[file] = compiledSourceFiles[file];
|
|
227
|
-
/**
|
|
228
|
-
* While we're not using NFT to process source code, we are using it
|
|
229
|
-
* to tree shake node deps, and include any fs reads for files that are
|
|
230
|
-
* not part of the traced paths or compiled source files.
|
|
231
|
-
* Most of this is identical to the `@vercel/node` implementation
|
|
232
|
-
*/
|
|
233
|
-
const runNft = () => nodeFileTrace$1(Array.from(tracedPaths), {
|
|
234
|
-
base: args.repoRootPath,
|
|
235
|
-
processCwd: args.workPath,
|
|
236
|
-
ts: true,
|
|
237
|
-
mixedModules: true,
|
|
238
|
-
async resolve(id, parent, job, cjsResolve) {
|
|
239
|
-
return resolve$1(id, parent, job, cjsResolve);
|
|
240
|
-
},
|
|
241
|
-
async readFile(fsPath) {
|
|
242
|
-
try {
|
|
243
|
-
let source = await readFile(fsPath);
|
|
244
|
-
if (fsPath.endsWith(".ts") && !fsPath.endsWith(".d.ts") || fsPath.endsWith(".tsx") || fsPath.endsWith(".mts") || fsPath.endsWith(".cts")) source = (await transform(fsPath, source.toString())).code;
|
|
245
|
-
return source;
|
|
246
|
-
} catch (error) {
|
|
247
|
-
if (isNativeError(error) && "code" in error && (error.code === "ENOENT" || error.code === "EISDIR")) return null;
|
|
248
|
-
throw error;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
const result = await span.child("vc.builder.backends.nft").trace(runNft);
|
|
253
|
-
if (!args.keepTracedPaths) for (const file of tracedPaths) {
|
|
254
|
-
const relativeFile = relative(args.repoRootPath, file);
|
|
255
|
-
result.fileList.delete(relativeFile);
|
|
256
|
-
}
|
|
257
|
-
debug("NFT traced files count:", result.fileList.size);
|
|
258
|
-
for (const file of result.fileList) {
|
|
259
|
-
const absolutePath = join(args.repoRootPath, file);
|
|
260
|
-
const stats = await lstat(absolutePath);
|
|
261
|
-
const outputPath = file;
|
|
262
|
-
if (stats.isSymbolicLink() || stats.isFile()) files[outputPath] = new FileFsRef({
|
|
263
|
-
fsPath: absolutePath,
|
|
264
|
-
mode: stats.mode
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
debug("Total files in context:", Object.keys(files).length);
|
|
268
|
-
return files;
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
//#endregion
|
|
272
|
-
//#region src/cervel/rolldown.ts
|
|
273
|
-
const __dirname__filenameShim = `
|
|
274
|
-
import { createRequire as __createRequire } from 'node:module';
|
|
275
|
-
import { fileURLToPath as __fileURLToPath } from 'node:url';
|
|
276
|
-
import { dirname as __dirname_ } from 'node:path';
|
|
277
|
-
var require = typeof require !== 'undefined' ? require : __createRequire(import.meta.url);
|
|
278
|
-
var __filename = typeof __filename !== 'undefined' ? __filename : __fileURLToPath(import.meta.url);
|
|
279
|
-
var __dirname = typeof __dirname !== 'undefined' ? __dirname : __dirname_(__filename);
|
|
280
|
-
`.trim();
|
|
281
|
-
const rolldown$1 = async (args) => {
|
|
282
|
-
const entrypointPath = join(args.workPath, args.entrypoint);
|
|
283
|
-
const outputDir = join(args.workPath, args.out);
|
|
284
|
-
const extension = extname(args.entrypoint);
|
|
285
|
-
const extensionMap = {
|
|
286
|
-
".ts": {
|
|
287
|
-
format: "auto",
|
|
288
|
-
extension: "js"
|
|
164
|
+
//#region src/rolldown/resolve-format.ts
|
|
165
|
+
const resolveEntrypointAndFormat = async (args) => {
|
|
166
|
+
const extension = extname(args.entrypoint);
|
|
167
|
+
const extensionMap = {
|
|
168
|
+
".ts": {
|
|
169
|
+
format: "auto",
|
|
170
|
+
extension: "js"
|
|
289
171
|
},
|
|
290
172
|
".mts": {
|
|
291
173
|
format: "esm",
|
|
@@ -309,9 +191,8 @@ const rolldown$1 = async (args) => {
|
|
|
309
191
|
}
|
|
310
192
|
};
|
|
311
193
|
const extensionInfo = extensionMap[extension] || extensionMap[".js"];
|
|
312
|
-
let resolvedFormat = extensionInfo.format === "auto" ?
|
|
194
|
+
let resolvedFormat = extensionInfo.format === "auto" ? args.defaultFormat : extensionInfo.format;
|
|
313
195
|
const packageJsonPath = join(args.workPath, "package.json");
|
|
314
|
-
const external = [];
|
|
315
196
|
let pkg = {};
|
|
316
197
|
if (existsSync(packageJsonPath)) {
|
|
317
198
|
const source = await readFile(packageJsonPath, "utf8");
|
|
@@ -322,431 +203,46 @@ const rolldown$1 = async (args) => {
|
|
|
322
203
|
}
|
|
323
204
|
if (extensionInfo.format === "auto") if (pkg.type === "module") resolvedFormat = "esm";
|
|
324
205
|
else resolvedFormat = "cjs";
|
|
325
|
-
for (const dependency of Object.keys(pkg.dependencies || {})) external.push(dependency);
|
|
326
|
-
for (const dependency of Object.keys(pkg.devDependencies || {})) external.push(dependency);
|
|
327
|
-
for (const dependency of Object.keys(pkg.peerDependencies || {})) external.push(dependency);
|
|
328
|
-
for (const dependency of Object.keys(pkg.optionalDependencies || {})) external.push(dependency);
|
|
329
|
-
}
|
|
330
|
-
const resolvedExtension = resolvedFormat === "esm" ? "mjs" : "cjs";
|
|
331
|
-
const context = { tracedPaths: /* @__PURE__ */ new Set() };
|
|
332
|
-
const runRolldown = () => build$2({
|
|
333
|
-
input: entrypointPath,
|
|
334
|
-
cwd: args.workPath,
|
|
335
|
-
platform: "node",
|
|
336
|
-
tsconfig: true,
|
|
337
|
-
plugins: [plugin({
|
|
338
|
-
repoRootPath: args.repoRootPath,
|
|
339
|
-
outDir: outputDir,
|
|
340
|
-
workPath: args.workPath,
|
|
341
|
-
shimBareImports: resolvedFormat === "esm",
|
|
342
|
-
context
|
|
343
|
-
})],
|
|
344
|
-
output: {
|
|
345
|
-
cleanDir: true,
|
|
346
|
-
dir: outputDir,
|
|
347
|
-
format: resolvedFormat,
|
|
348
|
-
entryFileNames: `[name].${resolvedExtension}`,
|
|
349
|
-
preserveModules: true,
|
|
350
|
-
preserveModulesRoot: args.repoRootPath,
|
|
351
|
-
sourcemap: false,
|
|
352
|
-
banner: resolvedFormat === "esm" ? __dirname__filenameShim : void 0
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
const out = await args.span.child("vc.builder.backends.rolldown").trace(runRolldown);
|
|
356
|
-
let handler = null;
|
|
357
|
-
for (const entry of out.output) if (entry.type === "chunk") {
|
|
358
|
-
if (entry.isEntry) handler = entry.fileName;
|
|
359
206
|
}
|
|
360
|
-
if (
|
|
361
|
-
const outputFiles = await nodeFileTrace({
|
|
362
|
-
outDir: outputDir,
|
|
363
|
-
tracedPaths: Array.from(context.tracedPaths),
|
|
364
|
-
repoRootPath: args.repoRootPath,
|
|
365
|
-
workPath: args.workPath,
|
|
366
|
-
keepTracedPaths: false,
|
|
367
|
-
span: args.span
|
|
368
|
-
});
|
|
369
|
-
const cleanup = async () => {
|
|
370
|
-
await rm(outputDir, {
|
|
371
|
-
recursive: true,
|
|
372
|
-
force: true
|
|
373
|
-
});
|
|
374
|
-
};
|
|
207
|
+
if (!resolvedFormat) throw new Error(`Unable to resolve format for ${args.entrypoint}`);
|
|
375
208
|
return {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
outputDir,
|
|
379
|
-
outputFiles
|
|
380
|
-
},
|
|
381
|
-
cleanup
|
|
209
|
+
format: resolvedFormat,
|
|
210
|
+
extension: resolvedFormat === "esm" ? "mjs" : "cjs"
|
|
382
211
|
};
|
|
383
212
|
};
|
|
384
213
|
|
|
385
214
|
//#endregion
|
|
386
|
-
//#region src/
|
|
387
|
-
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
const Colors = {
|
|
403
|
-
bold: _c(1),
|
|
404
|
-
red: _c(31),
|
|
405
|
-
green: _c(32),
|
|
406
|
-
yellow: _c(33),
|
|
407
|
-
blue: _c(34),
|
|
408
|
-
magenta: _c(35),
|
|
409
|
-
cyan: _c(36),
|
|
410
|
-
gray: _c(90),
|
|
411
|
-
url: (title, url) => noColor ? `[${title}](${url})` : `\u001B]8;;${url}\u001B\\${title}\u001B]8;;\u001B\\`
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
//#endregion
|
|
415
|
-
//#region src/typescript.ts
|
|
416
|
-
/**
|
|
417
|
-
* Typecheck via the TypeScript compiler API (`createProgram`, `getPreEmitDiagnostics`),
|
|
418
|
-
* not by spawning the `tsc` binary.
|
|
419
|
-
*
|
|
420
|
-
* We only want to validate the deployment entrypoint and its import graph, not every
|
|
421
|
-
* file matched by `tsconfig` `include`. The CLI cannot combine `--project` with explicit
|
|
422
|
-
* root files (TS5042), so expressing 'project options + entry-only roots' in one `tsc`
|
|
423
|
-
* call requires a generated tsconfig on disk. Writing beside the user's config is
|
|
424
|
-
* invasive; a temp config elsewhere often breaks `node_modules` / `@types` resolution
|
|
425
|
-
* relative to the real project. The API lets us reuse `parseJsonConfigFileContent` (same
|
|
426
|
-
* options as `-p`) with explicit `rootNames`, no files written, and a compiler host whose
|
|
427
|
-
* current directory stays `workPath`.
|
|
428
|
-
*
|
|
429
|
-
* The `typescript` package is resolved with `require` from the user's app (peer dependency), not bundled.
|
|
430
|
-
*/
|
|
431
|
-
const require_ = createRequire(import.meta.url);
|
|
432
|
-
const typescript = (args) => {
|
|
433
|
-
const { span } = args;
|
|
434
|
-
return span.child("vc.builder.backends.tsCompile").trace(async () => {
|
|
435
|
-
const extension = extname(args.entrypoint);
|
|
436
|
-
if (![
|
|
437
|
-
".ts",
|
|
438
|
-
".mts",
|
|
439
|
-
".cts"
|
|
440
|
-
].includes(extension)) return;
|
|
441
|
-
const ts = resolveTypeScriptModule(args.workPath);
|
|
442
|
-
if (!ts) {
|
|
443
|
-
console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck skipped ${Colors.gray("(TypeScript not found)")}`));
|
|
444
|
-
return null;
|
|
445
|
-
}
|
|
446
|
-
return doTypeCheck(args, ts);
|
|
447
|
-
});
|
|
448
|
-
};
|
|
449
|
-
async function doTypeCheck(args, ts) {
|
|
450
|
-
const entryAbsolute = resolve(args.workPath, args.entrypoint);
|
|
451
|
-
const tsconfig = await findNearestTsconfig(args.workPath);
|
|
452
|
-
const formatDiagnostics = process.stdout.isTTY ? ts.formatDiagnosticsWithColorAndContext : ts.formatDiagnostics;
|
|
453
|
-
const diagnosticHost = {
|
|
454
|
-
getNewLine: () => ts.sys.newLine,
|
|
455
|
-
getCanonicalFileName: (fileName) => ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(),
|
|
456
|
-
getCurrentDirectory: () => args.workPath
|
|
457
|
-
};
|
|
458
|
-
let options;
|
|
459
|
-
let parseDiagnostics = [];
|
|
460
|
-
if (tsconfig) {
|
|
461
|
-
const configRead = ts.readConfigFile(tsconfig, ts.sys.readFile);
|
|
462
|
-
if (configRead.error) {
|
|
463
|
-
const message = formatDiagnostics([configRead.error], diagnosticHost);
|
|
464
|
-
console.error("\nTypeScript type check failed:\n");
|
|
465
|
-
console.error(message);
|
|
466
|
-
throw new Error("TypeScript type check failed");
|
|
215
|
+
//#region src/service-vc-init.ts
|
|
216
|
+
async function applyServiceVcInit(args) {
|
|
217
|
+
const { format, extension } = await resolveShimFormat$1(args);
|
|
218
|
+
const handlerDir = dirname(args.handler);
|
|
219
|
+
const vcInitName = `${basename(args.handler, extname(args.handler))}.__vc_service_vc_init${extension}`;
|
|
220
|
+
const vcInitHandler = handlerDir === "." ? vcInitName : join(handlerDir, vcInitName);
|
|
221
|
+
const handlerImportPath = `./${basename(args.handler)}`;
|
|
222
|
+
const vcInitSource = format === "esm" ? createEsmServiceVcInit(handlerImportPath) : createCjsServiceVcInit(handlerImportPath);
|
|
223
|
+
return {
|
|
224
|
+
handler: vcInitHandler,
|
|
225
|
+
files: {
|
|
226
|
+
...args.files,
|
|
227
|
+
[vcInitHandler]: new FileBlob({
|
|
228
|
+
data: vcInitSource,
|
|
229
|
+
mode: 420
|
|
230
|
+
})
|
|
467
231
|
}
|
|
468
|
-
const parsed = ts.parseJsonConfigFileContent(configRead.config, ts.sys, dirname(tsconfig), void 0, tsconfig);
|
|
469
|
-
parseDiagnostics = parsed.errors;
|
|
470
|
-
options = {
|
|
471
|
-
...parsed.options,
|
|
472
|
-
noEmit: true,
|
|
473
|
-
skipLibCheck: true,
|
|
474
|
-
allowJs: true,
|
|
475
|
-
esModuleInterop: true
|
|
476
|
-
};
|
|
477
|
-
} else options = {
|
|
478
|
-
noEmit: true,
|
|
479
|
-
skipLibCheck: true,
|
|
480
|
-
allowJs: true,
|
|
481
|
-
esModuleInterop: true,
|
|
482
|
-
target: ts.ScriptTarget.ES2022,
|
|
483
|
-
module: ts.ModuleKind.NodeNext,
|
|
484
|
-
moduleResolution: ts.ModuleResolutionKind.NodeNext
|
|
485
232
|
};
|
|
486
|
-
const compilerHost = ts.createCompilerHost(options);
|
|
487
|
-
compilerHost.getCurrentDirectory = () => args.workPath;
|
|
488
|
-
const program = ts.createProgram([entryAbsolute], options, compilerHost);
|
|
489
|
-
const errors = [...parseDiagnostics, ...ts.getPreEmitDiagnostics(program)].filter((d) => d.category === ts.DiagnosticCategory.Error);
|
|
490
|
-
if (errors.length === 0) {
|
|
491
|
-
console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck complete`));
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
const output = formatDiagnostics(errors, diagnosticHost);
|
|
495
|
-
console.error("\nTypeScript type check failed:\n");
|
|
496
|
-
console.error(output);
|
|
497
|
-
throw new Error("TypeScript type check failed");
|
|
498
233
|
}
|
|
499
|
-
function
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
234
|
+
async function resolveShimFormat$1(args) {
|
|
235
|
+
const { format } = await resolveEntrypointAndFormat({
|
|
236
|
+
entrypoint: args.handler,
|
|
237
|
+
workPath: args.workPath
|
|
238
|
+
});
|
|
239
|
+
return {
|
|
240
|
+
format,
|
|
241
|
+
extension: extname(args.handler) || (format === "esm" ? ".mjs" : ".cjs")
|
|
242
|
+
};
|
|
505
243
|
}
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
if (existsSync(tsconfigPath)) return tsconfigPath;
|
|
509
|
-
if (workPath === "/") return;
|
|
510
|
-
return findNearestTsconfig(join(workPath, ".."));
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
//#endregion
|
|
514
|
-
//#region src/find-entrypoint.ts
|
|
515
|
-
const frameworks = [
|
|
516
|
-
"express",
|
|
517
|
-
"hono",
|
|
518
|
-
"elysia",
|
|
519
|
-
"fastify",
|
|
520
|
-
"@nestjs/core",
|
|
521
|
-
"h3"
|
|
522
|
-
];
|
|
523
|
-
const entrypointFilenames = [
|
|
524
|
-
"app",
|
|
525
|
-
"index",
|
|
526
|
-
"server",
|
|
527
|
-
"main",
|
|
528
|
-
"src/app",
|
|
529
|
-
"src/index",
|
|
530
|
-
"src/server",
|
|
531
|
-
"src/main"
|
|
532
|
-
];
|
|
533
|
-
const entrypointExtensions = [
|
|
534
|
-
"js",
|
|
535
|
-
"cjs",
|
|
536
|
-
"mjs",
|
|
537
|
-
"ts",
|
|
538
|
-
"cts",
|
|
539
|
-
"mts"
|
|
540
|
-
];
|
|
541
|
-
const entrypoints = entrypointFilenames.flatMap((filename) => entrypointExtensions.map((extension) => `${filename}.${extension}`));
|
|
542
|
-
const createFrameworkRegex = (framework) => new RegExp(`(?:from|require|import)\\s*(?:\\(\\s*)?["']${framework}["']\\s*(?:\\))?`, "g");
|
|
543
|
-
const findEntrypoint = async (cwd) => {
|
|
544
|
-
let packageJsonObject = null;
|
|
545
|
-
try {
|
|
546
|
-
const packageJson = await readFile(join(cwd, "package.json"), "utf-8");
|
|
547
|
-
packageJsonObject = JSON.parse(packageJson);
|
|
548
|
-
} catch (_) {}
|
|
549
|
-
if (packageJsonObject) {
|
|
550
|
-
const main = typeof packageJsonObject.main === "string" ? packageJsonObject.main.trim() : "";
|
|
551
|
-
if (main) {
|
|
552
|
-
const abs = resolve(cwd, main);
|
|
553
|
-
const rel = relative(cwd, abs);
|
|
554
|
-
if (!rel.startsWith("..") && rel !== "") try {
|
|
555
|
-
await readFile(abs, "utf-8");
|
|
556
|
-
return rel.split(sep).join("/");
|
|
557
|
-
} catch {}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
let framework;
|
|
561
|
-
if (packageJsonObject) framework = frameworks.find((framework$1) => packageJsonObject.dependencies?.[framework$1]);
|
|
562
|
-
if (!framework) for (const entrypoint of entrypoints) {
|
|
563
|
-
const entrypointPath = join(cwd, entrypoint);
|
|
564
|
-
try {
|
|
565
|
-
await readFile(entrypointPath, "utf-8");
|
|
566
|
-
return entrypoint;
|
|
567
|
-
} catch (_) {}
|
|
568
|
-
}
|
|
569
|
-
const regex = framework ? createFrameworkRegex(framework) : void 0;
|
|
570
|
-
for (const entrypoint of entrypoints) {
|
|
571
|
-
const entrypointPath = join(cwd, entrypoint);
|
|
572
|
-
try {
|
|
573
|
-
const content = await readFile(entrypointPath, "utf-8");
|
|
574
|
-
if (regex) {
|
|
575
|
-
if (regex.test(content)) return entrypoint;
|
|
576
|
-
}
|
|
577
|
-
} catch (_) {}
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
const findEntrypointOrThrow = async (cwd) => {
|
|
581
|
-
const entrypoint = await findEntrypoint(cwd);
|
|
582
|
-
if (!entrypoint) throw new Error(`No entrypoint found in "${cwd}". Set package.json "main" to a server file, or add one of: ${entrypoints.join(", ")}`);
|
|
583
|
-
return entrypoint;
|
|
584
|
-
};
|
|
585
|
-
/**
|
|
586
|
-
* Normalized entrypoint detector for Node services. Wraps {@link findEntrypoint}
|
|
587
|
-
* and returns the result in the shared {@link DetectedEntrypoint} shape consumed
|
|
588
|
-
* by services auto-detection.
|
|
589
|
-
*/
|
|
590
|
-
const detectEntrypoint = async ({ workPath }) => {
|
|
591
|
-
const file = await findEntrypoint(workPath);
|
|
592
|
-
if (!file) return null;
|
|
593
|
-
return {
|
|
594
|
-
kind: "file",
|
|
595
|
-
entrypoint: file
|
|
596
|
-
};
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
//#endregion
|
|
600
|
-
//#region src/cervel/index.ts
|
|
601
|
-
const require$2 = createRequire(import.meta.url);
|
|
602
|
-
const getBuildSummary = async (outputDir) => {
|
|
603
|
-
const buildSummary = await readFile$1(join(outputDir, ".cervel.json"), "utf-8");
|
|
604
|
-
return JSON.parse(buildSummary);
|
|
605
|
-
};
|
|
606
|
-
const build$1 = async (args) => {
|
|
607
|
-
const entrypoint = args.entrypoint || await findEntrypointOrThrow(args.workPath);
|
|
608
|
-
const span = args.span ?? new Span({ name: "cervel-build" });
|
|
609
|
-
const [, rolldownResult] = await Promise.all([typescript({
|
|
610
|
-
entrypoint,
|
|
611
|
-
workPath: args.workPath,
|
|
612
|
-
span
|
|
613
|
-
}), rolldown$1({
|
|
614
|
-
entrypoint,
|
|
615
|
-
workPath: args.workPath,
|
|
616
|
-
repoRootPath: args.repoRootPath,
|
|
617
|
-
out: args.out,
|
|
618
|
-
span
|
|
619
|
-
})]);
|
|
620
|
-
await writeFile(join(args.workPath, args.out, ".cervel.json"), JSON.stringify({ handler: rolldownResult.result.handler }, null, 2));
|
|
621
|
-
console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Build complete — Using ${Colors.bold(entrypoint)} as the root entrypoint.`));
|
|
622
|
-
return { rolldownResult: rolldownResult.result };
|
|
623
|
-
};
|
|
624
|
-
const serve = async (args) => {
|
|
625
|
-
const entrypoint = await findEntrypointOrThrow(args.workPath);
|
|
626
|
-
const srvxBin = join(require$2.resolve("srvx"), "..", "..", "..", "bin", "srvx.mjs");
|
|
627
|
-
const tsxBin = require$2.resolve("tsx");
|
|
628
|
-
const restArgs = Object.entries(args.rest).filter(([, value]) => value !== void 0 && value !== false).map(([key, value]) => typeof value === "boolean" ? `--${key}` : `--${key}=${value}`);
|
|
629
|
-
if (!args.rest.import) restArgs.push("--import", tsxBin);
|
|
630
|
-
await execa("npx", [
|
|
631
|
-
srvxBin,
|
|
632
|
-
...restArgs,
|
|
633
|
-
entrypoint
|
|
634
|
-
], {
|
|
635
|
-
cwd: args.workPath,
|
|
636
|
-
stdio: "inherit"
|
|
637
|
-
});
|
|
638
|
-
};
|
|
639
|
-
const srvxOptions = {
|
|
640
|
-
help: {
|
|
641
|
-
type: "boolean",
|
|
642
|
-
short: "h"
|
|
643
|
-
},
|
|
644
|
-
version: {
|
|
645
|
-
type: "boolean",
|
|
646
|
-
short: "v"
|
|
647
|
-
},
|
|
648
|
-
prod: { type: "boolean" },
|
|
649
|
-
port: {
|
|
650
|
-
type: "string",
|
|
651
|
-
short: "p"
|
|
652
|
-
},
|
|
653
|
-
host: {
|
|
654
|
-
type: "string",
|
|
655
|
-
short: "H"
|
|
656
|
-
},
|
|
657
|
-
static: {
|
|
658
|
-
type: "string",
|
|
659
|
-
short: "s"
|
|
660
|
-
},
|
|
661
|
-
import: { type: "string" },
|
|
662
|
-
tls: { type: "boolean" },
|
|
663
|
-
cert: { type: "string" },
|
|
664
|
-
key: { type: "string" }
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
//#endregion
|
|
668
|
-
//#region src/rolldown/resolve-format.ts
|
|
669
|
-
const resolveEntrypointAndFormat = async (args) => {
|
|
670
|
-
const extension = extname(args.entrypoint);
|
|
671
|
-
const extensionMap = {
|
|
672
|
-
".ts": {
|
|
673
|
-
format: "auto",
|
|
674
|
-
extension: "js"
|
|
675
|
-
},
|
|
676
|
-
".mts": {
|
|
677
|
-
format: "esm",
|
|
678
|
-
extension: "mjs"
|
|
679
|
-
},
|
|
680
|
-
".cts": {
|
|
681
|
-
format: "cjs",
|
|
682
|
-
extension: "cjs"
|
|
683
|
-
},
|
|
684
|
-
".cjs": {
|
|
685
|
-
format: "cjs",
|
|
686
|
-
extension: "cjs"
|
|
687
|
-
},
|
|
688
|
-
".js": {
|
|
689
|
-
format: "auto",
|
|
690
|
-
extension: "js"
|
|
691
|
-
},
|
|
692
|
-
".mjs": {
|
|
693
|
-
format: "esm",
|
|
694
|
-
extension: "mjs"
|
|
695
|
-
}
|
|
696
|
-
};
|
|
697
|
-
const extensionInfo = extensionMap[extension] || extensionMap[".js"];
|
|
698
|
-
let resolvedFormat = extensionInfo.format === "auto" ? args.defaultFormat : extensionInfo.format;
|
|
699
|
-
const packageJsonPath = join(args.workPath, "package.json");
|
|
700
|
-
let pkg = {};
|
|
701
|
-
if (existsSync(packageJsonPath)) {
|
|
702
|
-
const source = await readFile(packageJsonPath, "utf8");
|
|
703
|
-
try {
|
|
704
|
-
pkg = JSON.parse(source.toString());
|
|
705
|
-
} catch (_e) {
|
|
706
|
-
pkg = {};
|
|
707
|
-
}
|
|
708
|
-
if (extensionInfo.format === "auto") if (pkg.type === "module") resolvedFormat = "esm";
|
|
709
|
-
else resolvedFormat = "cjs";
|
|
710
|
-
}
|
|
711
|
-
if (!resolvedFormat) throw new Error(`Unable to resolve format for ${args.entrypoint}`);
|
|
712
|
-
return {
|
|
713
|
-
format: resolvedFormat,
|
|
714
|
-
extension: resolvedFormat === "esm" ? "mjs" : "cjs"
|
|
715
|
-
};
|
|
716
|
-
};
|
|
717
|
-
|
|
718
|
-
//#endregion
|
|
719
|
-
//#region src/service-vc-init.ts
|
|
720
|
-
async function applyServiceVcInit(args) {
|
|
721
|
-
const { format, extension } = await resolveShimFormat$1(args);
|
|
722
|
-
const handlerDir = dirname(args.handler);
|
|
723
|
-
const vcInitName = `${basename(args.handler, extname(args.handler))}.__vc_service_vc_init${extension}`;
|
|
724
|
-
const vcInitHandler = handlerDir === "." ? vcInitName : join(handlerDir, vcInitName);
|
|
725
|
-
const handlerImportPath = `./${basename(args.handler)}`;
|
|
726
|
-
const vcInitSource = format === "esm" ? createEsmServiceVcInit(handlerImportPath) : createCjsServiceVcInit(handlerImportPath);
|
|
727
|
-
return {
|
|
728
|
-
handler: vcInitHandler,
|
|
729
|
-
files: {
|
|
730
|
-
...args.files,
|
|
731
|
-
[vcInitHandler]: new FileBlob({
|
|
732
|
-
data: vcInitSource,
|
|
733
|
-
mode: 420
|
|
734
|
-
})
|
|
735
|
-
}
|
|
736
|
-
};
|
|
737
|
-
}
|
|
738
|
-
async function resolveShimFormat$1(args) {
|
|
739
|
-
const { format } = await resolveEntrypointAndFormat({
|
|
740
|
-
entrypoint: args.handler,
|
|
741
|
-
workPath: args.workPath
|
|
742
|
-
});
|
|
743
|
-
return {
|
|
744
|
-
format,
|
|
745
|
-
extension: extname(args.handler) || (format === "esm" ? ".mjs" : ".cjs")
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
const sharedShimPrelude = String.raw`
|
|
749
|
-
const PATCH_SYMBOL = Symbol.for('vc.service.route-prefix-strip.patch')
|
|
244
|
+
const sharedShimPrelude = String.raw`
|
|
245
|
+
const PATCH_SYMBOL = Symbol.for('vc.service.route-prefix-strip.patch')
|
|
750
246
|
|
|
751
247
|
function normalizeServiceRoutePrefix(rawPrefix) {
|
|
752
248
|
if (!rawPrefix) {
|
|
@@ -906,7 +402,7 @@ async function applyCronDispatch(args) {
|
|
|
906
402
|
const handlerDir = posix.dirname(args.handler);
|
|
907
403
|
const dispatchName = `${posix.basename(args.handler, extname(args.handler))}.__vc_cron_dispatch${extension}`;
|
|
908
404
|
const dispatchHandler = handlerDir === "." ? dispatchName : posix.join(handlerDir, dispatchName);
|
|
909
|
-
const handlerImportPath = `./${posix.basename(args.handler)}`;
|
|
405
|
+
const handlerImportPath = args.modulePathOverride ?? `./${posix.basename(args.handler)}`;
|
|
910
406
|
const routesJson = JSON.stringify(args.routes);
|
|
911
407
|
if (routesJson.includes("\\") || routesJson.includes("'")) throw new Error(`cron route table contains characters that need JS-string escaping: ${routesJson}`);
|
|
912
408
|
const dispatchSource = (format === "esm" ? ESM_TEMPLATE : CJS_TEMPLATE).replace(USER_MODULE_PLACEHOLDER, JSON.stringify(handlerImportPath)).replace(ROUTES_PLACEHOLDER, `'${routesJson}'`);
|
|
@@ -962,6 +458,515 @@ async function getServiceCrons(opts) {
|
|
|
962
458
|
}];
|
|
963
459
|
}
|
|
964
460
|
|
|
461
|
+
//#endregion
|
|
462
|
+
//#region src/cervel/plugin.ts
|
|
463
|
+
const CJS_SHIM_PREFIX$1 = "\0cjs-shim:";
|
|
464
|
+
const plugin = (args) => {
|
|
465
|
+
const packageJsonCache = /* @__PURE__ */ new Map();
|
|
466
|
+
const shimMeta = /* @__PURE__ */ new Map();
|
|
467
|
+
const { tracedPaths } = args.context;
|
|
468
|
+
const isBareImport = (id) => {
|
|
469
|
+
return !id.startsWith(".") && !id.startsWith("/") && !/^[a-z][a-z0-9+.-]*:/i.test(id);
|
|
470
|
+
};
|
|
471
|
+
/**
|
|
472
|
+
* Read and cache package.json contents
|
|
473
|
+
*/
|
|
474
|
+
const getPackageJson = async (pkgPath) => {
|
|
475
|
+
if (packageJsonCache.has(pkgPath)) return packageJsonCache.get(pkgPath);
|
|
476
|
+
try {
|
|
477
|
+
const contents = await readFile(pkgPath, "utf-8");
|
|
478
|
+
const parsed = JSON.parse(contents);
|
|
479
|
+
packageJsonCache.set(pkgPath, parsed);
|
|
480
|
+
return parsed;
|
|
481
|
+
} catch {
|
|
482
|
+
packageJsonCache.set(pkgPath, null);
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
/**
|
|
487
|
+
* Determine if a resolved module is CommonJS based on package.json exports
|
|
488
|
+
*/
|
|
489
|
+
const isCommonJS = async (bareImport, resolvedPath, resolvedInfo) => {
|
|
490
|
+
const ext = extname(resolvedPath);
|
|
491
|
+
if (ext === ".cjs") return true;
|
|
492
|
+
if (ext === ".mjs") return false;
|
|
493
|
+
if (ext === ".js" || ext === ".ts") {
|
|
494
|
+
const pkgJsonPath = resolvedInfo.packageJsonPath;
|
|
495
|
+
if (!pkgJsonPath) return true;
|
|
496
|
+
const pkgJson = await getPackageJson(pkgJsonPath);
|
|
497
|
+
if (!pkgJson) return true;
|
|
498
|
+
const pkgDir = dirname(pkgJsonPath);
|
|
499
|
+
const relativePath = resolvedPath.startsWith(pkgDir) ? resolvedPath.slice(pkgDir.length + 1).replace(/\\/g, "/") : null;
|
|
500
|
+
if (!relativePath) return pkgJson.type !== "module";
|
|
501
|
+
const pkgName = pkgJson.name || "";
|
|
502
|
+
const subpath = bareImport.startsWith(pkgName) ? `.${bareImport.slice(pkgName.length)}` || "." : ".";
|
|
503
|
+
try {
|
|
504
|
+
if (exports(pkgJson, subpath, {
|
|
505
|
+
require: false,
|
|
506
|
+
conditions: ["node", "import"]
|
|
507
|
+
})?.some((p) => p === relativePath || p === `./${relativePath}`)) return false;
|
|
508
|
+
if (exports(pkgJson, subpath, {
|
|
509
|
+
require: true,
|
|
510
|
+
conditions: ["node", "require"]
|
|
511
|
+
})?.some((p) => p === relativePath || p === `./${relativePath}`)) return true;
|
|
512
|
+
} catch (err) {
|
|
513
|
+
console.warn("Export resolution failed::", err);
|
|
514
|
+
}
|
|
515
|
+
if (pkgJson.module) return false;
|
|
516
|
+
return pkgJson.type !== "module";
|
|
517
|
+
}
|
|
518
|
+
return true;
|
|
519
|
+
};
|
|
520
|
+
const isLocalImport = (id) => {
|
|
521
|
+
if (id.startsWith("node:")) return false;
|
|
522
|
+
if (id.includes("node_modules")) return false;
|
|
523
|
+
return true;
|
|
524
|
+
};
|
|
525
|
+
return {
|
|
526
|
+
name: "cervel",
|
|
527
|
+
resolveId: {
|
|
528
|
+
order: "pre",
|
|
529
|
+
async handler(id, importer, rOpts) {
|
|
530
|
+
if (id.startsWith(CJS_SHIM_PREFIX$1)) return {
|
|
531
|
+
id,
|
|
532
|
+
external: false
|
|
533
|
+
};
|
|
534
|
+
const resolved = await this.resolve(id, importer, rOpts);
|
|
535
|
+
if (builtinModules.includes(id)) return {
|
|
536
|
+
id: `node:${id}`,
|
|
537
|
+
external: true
|
|
538
|
+
};
|
|
539
|
+
if (resolved?.id && isLocalImport(resolved.id)) tracedPaths.add(resolved.id);
|
|
540
|
+
if (importer?.startsWith(CJS_SHIM_PREFIX$1) && isBareImport(id)) return {
|
|
541
|
+
id,
|
|
542
|
+
external: true
|
|
543
|
+
};
|
|
544
|
+
if (importer && isBareImport(id) && resolved?.id?.includes("node_modules")) {
|
|
545
|
+
if (args.shimBareImports) {
|
|
546
|
+
if (await isCommonJS(id, resolved.id, resolved)) {
|
|
547
|
+
const importerPkgJsonPath = (await this.resolve(importer))?.packageJsonPath;
|
|
548
|
+
if (importerPkgJsonPath) {
|
|
549
|
+
const importerPkgDir = relative(args.repoRootPath, dirname(importerPkgJsonPath));
|
|
550
|
+
const shimId$1 = `${CJS_SHIM_PREFIX$1}${importerPkgDir.replace(/\//g, "_")}_${id.replace(/\//g, "_")}`;
|
|
551
|
+
shimMeta.set(shimId$1, {
|
|
552
|
+
pkgDir: importerPkgDir,
|
|
553
|
+
pkgName: id
|
|
554
|
+
});
|
|
555
|
+
return {
|
|
556
|
+
id: shimId$1,
|
|
557
|
+
external: false
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
const shimId = `${CJS_SHIM_PREFIX$1}${id.replace(/\//g, "_")}`;
|
|
561
|
+
shimMeta.set(shimId, {
|
|
562
|
+
pkgDir: "",
|
|
563
|
+
pkgName: id
|
|
564
|
+
});
|
|
565
|
+
return {
|
|
566
|
+
id: shimId,
|
|
567
|
+
external: false
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
external: true,
|
|
573
|
+
id
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
if (importer && isBareImport(id)) return resolved;
|
|
577
|
+
return {
|
|
578
|
+
external: true,
|
|
579
|
+
...resolved,
|
|
580
|
+
id: resolved?.id || id
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
load: { async handler(id) {
|
|
585
|
+
if (id.startsWith(CJS_SHIM_PREFIX$1)) {
|
|
586
|
+
const meta = shimMeta.get(id);
|
|
587
|
+
if (!meta) return { code: `module.exports = require('${id.slice(10)}');` };
|
|
588
|
+
const { pkgDir, pkgName } = meta;
|
|
589
|
+
if (pkgDir) return { code: `
|
|
590
|
+
import { createRequire } from 'node:module';
|
|
591
|
+
import { fileURLToPath } from 'node:url';
|
|
592
|
+
import { dirname, join } from 'node:path';
|
|
593
|
+
|
|
594
|
+
const requireFromContext = createRequire(join(dirname(fileURLToPath(import.meta.url)), '${join("..", pkgDir, "package.json")}'));
|
|
595
|
+
module.exports = requireFromContext('${pkgName}');
|
|
596
|
+
`.trim() };
|
|
597
|
+
return { code: `module.exports = require('${pkgName}');` };
|
|
598
|
+
}
|
|
599
|
+
return null;
|
|
600
|
+
} }
|
|
601
|
+
};
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
//#endregion
|
|
605
|
+
//#region src/cervel/node-file-trace.ts
|
|
606
|
+
const nodeFileTrace = async (args) => {
|
|
607
|
+
const { span } = args;
|
|
608
|
+
const files = {};
|
|
609
|
+
const { tracedPaths } = args;
|
|
610
|
+
const compiledSourceFiles = await glob("**/*", {
|
|
611
|
+
cwd: args.outDir,
|
|
612
|
+
follow: true,
|
|
613
|
+
includeDirectories: true
|
|
614
|
+
});
|
|
615
|
+
for (const file of Object.keys(compiledSourceFiles)) files[file] = compiledSourceFiles[file];
|
|
616
|
+
/**
|
|
617
|
+
* While we're not using NFT to process source code, we are using it
|
|
618
|
+
* to tree shake node deps, and include any fs reads for files that are
|
|
619
|
+
* not part of the traced paths or compiled source files.
|
|
620
|
+
* Most of this is identical to the `@vercel/node` implementation
|
|
621
|
+
*/
|
|
622
|
+
const runNft = () => nodeFileTrace$1(Array.from(tracedPaths), {
|
|
623
|
+
base: args.repoRootPath,
|
|
624
|
+
processCwd: args.workPath,
|
|
625
|
+
ts: true,
|
|
626
|
+
mixedModules: true,
|
|
627
|
+
async resolve(id, parent, job, cjsResolve) {
|
|
628
|
+
return resolve$1(id, parent, job, cjsResolve);
|
|
629
|
+
},
|
|
630
|
+
async readFile(fsPath) {
|
|
631
|
+
try {
|
|
632
|
+
let source = await readFile(fsPath);
|
|
633
|
+
if (fsPath.endsWith(".ts") && !fsPath.endsWith(".d.ts") || fsPath.endsWith(".tsx") || fsPath.endsWith(".mts") || fsPath.endsWith(".cts")) source = (await transform(fsPath, source.toString())).code;
|
|
634
|
+
return source;
|
|
635
|
+
} catch (error) {
|
|
636
|
+
if (isNativeError(error) && "code" in error && (error.code === "ENOENT" || error.code === "EISDIR")) return null;
|
|
637
|
+
throw error;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
const result = await span.child("vc.builder.backends.nft").trace(runNft);
|
|
642
|
+
if (!args.keepTracedPaths) for (const file of tracedPaths) {
|
|
643
|
+
const relativeFile = relative(args.repoRootPath, file);
|
|
644
|
+
result.fileList.delete(relativeFile);
|
|
645
|
+
}
|
|
646
|
+
debug("NFT traced files count:", result.fileList.size);
|
|
647
|
+
for (const file of result.fileList) {
|
|
648
|
+
const absolutePath = join(args.repoRootPath, file);
|
|
649
|
+
const stats = await lstat(absolutePath);
|
|
650
|
+
const outputPath = file;
|
|
651
|
+
if (stats.isSymbolicLink() || stats.isFile()) files[outputPath] = new FileFsRef({
|
|
652
|
+
fsPath: absolutePath,
|
|
653
|
+
mode: stats.mode
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
debug("Total files in context:", Object.keys(files).length);
|
|
657
|
+
return files;
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
//#endregion
|
|
661
|
+
//#region src/cervel/rolldown.ts
|
|
662
|
+
const __dirname__filenameShim = `
|
|
663
|
+
import { createRequire as __createRequire } from 'node:module';
|
|
664
|
+
import { fileURLToPath as __fileURLToPath } from 'node:url';
|
|
665
|
+
import { dirname as __dirname_ } from 'node:path';
|
|
666
|
+
var require = typeof require !== 'undefined' ? require : __createRequire(import.meta.url);
|
|
667
|
+
var __filename = typeof __filename !== 'undefined' ? __filename : __fileURLToPath(import.meta.url);
|
|
668
|
+
var __dirname = typeof __dirname !== 'undefined' ? __dirname : __dirname_(__filename);
|
|
669
|
+
`.trim();
|
|
670
|
+
const rolldown$1 = async (args) => {
|
|
671
|
+
const entrypointPath = join(args.workPath, args.entrypoint);
|
|
672
|
+
const outputDir = join(args.workPath, args.out);
|
|
673
|
+
const extension = extname(args.entrypoint);
|
|
674
|
+
const extensionMap = {
|
|
675
|
+
".ts": {
|
|
676
|
+
format: "auto",
|
|
677
|
+
extension: "js"
|
|
678
|
+
},
|
|
679
|
+
".mts": {
|
|
680
|
+
format: "esm",
|
|
681
|
+
extension: "mjs"
|
|
682
|
+
},
|
|
683
|
+
".cts": {
|
|
684
|
+
format: "cjs",
|
|
685
|
+
extension: "cjs"
|
|
686
|
+
},
|
|
687
|
+
".cjs": {
|
|
688
|
+
format: "cjs",
|
|
689
|
+
extension: "cjs"
|
|
690
|
+
},
|
|
691
|
+
".js": {
|
|
692
|
+
format: "auto",
|
|
693
|
+
extension: "js"
|
|
694
|
+
},
|
|
695
|
+
".mjs": {
|
|
696
|
+
format: "esm",
|
|
697
|
+
extension: "mjs"
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
const extensionInfo = extensionMap[extension] || extensionMap[".js"];
|
|
701
|
+
let resolvedFormat = extensionInfo.format === "auto" ? void 0 : extensionInfo.format;
|
|
702
|
+
const packageJsonPath = join(args.workPath, "package.json");
|
|
703
|
+
const external = [];
|
|
704
|
+
let pkg = {};
|
|
705
|
+
if (existsSync(packageJsonPath)) {
|
|
706
|
+
const source = await readFile(packageJsonPath, "utf8");
|
|
707
|
+
try {
|
|
708
|
+
pkg = JSON.parse(source.toString());
|
|
709
|
+
} catch (_e) {
|
|
710
|
+
pkg = {};
|
|
711
|
+
}
|
|
712
|
+
if (extensionInfo.format === "auto") if (pkg.type === "module") resolvedFormat = "esm";
|
|
713
|
+
else resolvedFormat = "cjs";
|
|
714
|
+
for (const dependency of Object.keys(pkg.dependencies || {})) external.push(dependency);
|
|
715
|
+
for (const dependency of Object.keys(pkg.devDependencies || {})) external.push(dependency);
|
|
716
|
+
for (const dependency of Object.keys(pkg.peerDependencies || {})) external.push(dependency);
|
|
717
|
+
for (const dependency of Object.keys(pkg.optionalDependencies || {})) external.push(dependency);
|
|
718
|
+
}
|
|
719
|
+
const resolvedExtension = resolvedFormat === "esm" ? "mjs" : "cjs";
|
|
720
|
+
const context = { tracedPaths: /* @__PURE__ */ new Set() };
|
|
721
|
+
const runRolldown = () => build$2({
|
|
722
|
+
input: entrypointPath,
|
|
723
|
+
cwd: args.workPath,
|
|
724
|
+
platform: "node",
|
|
725
|
+
tsconfig: true,
|
|
726
|
+
plugins: [plugin({
|
|
727
|
+
repoRootPath: args.repoRootPath,
|
|
728
|
+
outDir: outputDir,
|
|
729
|
+
workPath: args.workPath,
|
|
730
|
+
shimBareImports: resolvedFormat === "esm",
|
|
731
|
+
context
|
|
732
|
+
})],
|
|
733
|
+
output: {
|
|
734
|
+
cleanDir: true,
|
|
735
|
+
dir: outputDir,
|
|
736
|
+
format: resolvedFormat,
|
|
737
|
+
entryFileNames: `[name].${resolvedExtension}`,
|
|
738
|
+
preserveModules: true,
|
|
739
|
+
preserveModulesRoot: args.repoRootPath,
|
|
740
|
+
sourcemap: false,
|
|
741
|
+
banner: resolvedFormat === "esm" ? __dirname__filenameShim : void 0
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
const out = await args.span.child("vc.builder.backends.rolldown").trace(runRolldown);
|
|
745
|
+
let handler = null;
|
|
746
|
+
for (const entry of out.output) if (entry.type === "chunk") {
|
|
747
|
+
if (entry.isEntry) handler = entry.fileName;
|
|
748
|
+
}
|
|
749
|
+
if (typeof handler !== "string") throw new Error(`Unable to resolve module for ${args.entrypoint}`);
|
|
750
|
+
const outputFiles = await nodeFileTrace({
|
|
751
|
+
outDir: outputDir,
|
|
752
|
+
tracedPaths: Array.from(context.tracedPaths),
|
|
753
|
+
repoRootPath: args.repoRootPath,
|
|
754
|
+
workPath: args.workPath,
|
|
755
|
+
keepTracedPaths: false,
|
|
756
|
+
span: args.span
|
|
757
|
+
});
|
|
758
|
+
const cleanup = async () => {
|
|
759
|
+
await rm(outputDir, {
|
|
760
|
+
recursive: true,
|
|
761
|
+
force: true
|
|
762
|
+
});
|
|
763
|
+
};
|
|
764
|
+
return {
|
|
765
|
+
result: {
|
|
766
|
+
handler,
|
|
767
|
+
outputDir,
|
|
768
|
+
outputFiles
|
|
769
|
+
},
|
|
770
|
+
cleanup
|
|
771
|
+
};
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
//#endregion
|
|
775
|
+
//#region src/cervel/utils.ts
|
|
776
|
+
const noColor = globalThis.process?.env?.NO_COLOR === "1" || globalThis.process?.env?.TERM === "dumb";
|
|
777
|
+
const resets = {
|
|
778
|
+
1: 22,
|
|
779
|
+
31: 39,
|
|
780
|
+
32: 39,
|
|
781
|
+
33: 39,
|
|
782
|
+
34: 39,
|
|
783
|
+
35: 39,
|
|
784
|
+
36: 39,
|
|
785
|
+
90: 39
|
|
786
|
+
};
|
|
787
|
+
const _c = (c) => (text) => {
|
|
788
|
+
if (noColor) return text;
|
|
789
|
+
return `\u001B[${c}m${text}\u001B[${resets[c] ?? 0}m`;
|
|
790
|
+
};
|
|
791
|
+
const Colors = {
|
|
792
|
+
bold: _c(1),
|
|
793
|
+
red: _c(31),
|
|
794
|
+
green: _c(32),
|
|
795
|
+
yellow: _c(33),
|
|
796
|
+
blue: _c(34),
|
|
797
|
+
magenta: _c(35),
|
|
798
|
+
cyan: _c(36),
|
|
799
|
+
gray: _c(90),
|
|
800
|
+
url: (title, url) => noColor ? `[${title}](${url})` : `\u001B]8;;${url}\u001B\\${title}\u001B]8;;\u001B\\`
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
//#endregion
|
|
804
|
+
//#region src/typescript.ts
|
|
805
|
+
/**
|
|
806
|
+
* Typecheck via the TypeScript compiler API (`createProgram`, `getPreEmitDiagnostics`),
|
|
807
|
+
* not by spawning the `tsc` binary.
|
|
808
|
+
*
|
|
809
|
+
* We only want to validate the deployment entrypoint and its import graph, not every
|
|
810
|
+
* file matched by `tsconfig` `include`. The CLI cannot combine `--project` with explicit
|
|
811
|
+
* root files (TS5042), so expressing 'project options + entry-only roots' in one `tsc`
|
|
812
|
+
* call requires a generated tsconfig on disk. Writing beside the user's config is
|
|
813
|
+
* invasive; a temp config elsewhere often breaks `node_modules` / `@types` resolution
|
|
814
|
+
* relative to the real project. The API lets us reuse `parseJsonConfigFileContent` (same
|
|
815
|
+
* options as `-p`) with explicit `rootNames`, no files written, and a compiler host whose
|
|
816
|
+
* current directory stays `workPath`.
|
|
817
|
+
*
|
|
818
|
+
* The `typescript` package is resolved with `require` from the user's app (peer dependency), not bundled.
|
|
819
|
+
*/
|
|
820
|
+
const require_ = createRequire(import.meta.url);
|
|
821
|
+
const typescript = (args) => {
|
|
822
|
+
const { span } = args;
|
|
823
|
+
return span.child("vc.builder.backends.tsCompile").trace(async () => {
|
|
824
|
+
const extension = extname(args.entrypoint);
|
|
825
|
+
if (![
|
|
826
|
+
".ts",
|
|
827
|
+
".mts",
|
|
828
|
+
".cts"
|
|
829
|
+
].includes(extension)) return;
|
|
830
|
+
const ts = resolveTypeScriptModule(args.workPath);
|
|
831
|
+
if (!ts) {
|
|
832
|
+
console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck skipped ${Colors.gray("(TypeScript not found)")}`));
|
|
833
|
+
return null;
|
|
834
|
+
}
|
|
835
|
+
return doTypeCheck(args, ts);
|
|
836
|
+
});
|
|
837
|
+
};
|
|
838
|
+
async function doTypeCheck(args, ts) {
|
|
839
|
+
const entryAbsolute = resolve(args.workPath, args.entrypoint);
|
|
840
|
+
const tsconfig = await findNearestTsconfig(args.workPath);
|
|
841
|
+
const formatDiagnostics = process.stdout.isTTY ? ts.formatDiagnosticsWithColorAndContext : ts.formatDiagnostics;
|
|
842
|
+
const diagnosticHost = {
|
|
843
|
+
getNewLine: () => ts.sys.newLine,
|
|
844
|
+
getCanonicalFileName: (fileName) => ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(),
|
|
845
|
+
getCurrentDirectory: () => args.workPath
|
|
846
|
+
};
|
|
847
|
+
let options;
|
|
848
|
+
let parseDiagnostics = [];
|
|
849
|
+
if (tsconfig) {
|
|
850
|
+
const configRead = ts.readConfigFile(tsconfig, ts.sys.readFile);
|
|
851
|
+
if (configRead.error) {
|
|
852
|
+
const message = formatDiagnostics([configRead.error], diagnosticHost);
|
|
853
|
+
console.error("\nTypeScript type check failed:\n");
|
|
854
|
+
console.error(message);
|
|
855
|
+
throw new Error("TypeScript type check failed");
|
|
856
|
+
}
|
|
857
|
+
const parsed = ts.parseJsonConfigFileContent(configRead.config, ts.sys, dirname(tsconfig), void 0, tsconfig);
|
|
858
|
+
parseDiagnostics = parsed.errors;
|
|
859
|
+
options = {
|
|
860
|
+
...parsed.options,
|
|
861
|
+
noEmit: true,
|
|
862
|
+
skipLibCheck: true,
|
|
863
|
+
allowJs: true,
|
|
864
|
+
esModuleInterop: true
|
|
865
|
+
};
|
|
866
|
+
} else options = {
|
|
867
|
+
noEmit: true,
|
|
868
|
+
skipLibCheck: true,
|
|
869
|
+
allowJs: true,
|
|
870
|
+
esModuleInterop: true,
|
|
871
|
+
target: ts.ScriptTarget.ES2022,
|
|
872
|
+
module: ts.ModuleKind.NodeNext,
|
|
873
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext
|
|
874
|
+
};
|
|
875
|
+
const compilerHost = ts.createCompilerHost(options);
|
|
876
|
+
compilerHost.getCurrentDirectory = () => args.workPath;
|
|
877
|
+
const program = ts.createProgram([entryAbsolute], options, compilerHost);
|
|
878
|
+
const errors = [...parseDiagnostics, ...ts.getPreEmitDiagnostics(program)].filter((d) => d.category === ts.DiagnosticCategory.Error);
|
|
879
|
+
if (errors.length === 0) {
|
|
880
|
+
console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck complete`));
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const output = formatDiagnostics(errors, diagnosticHost);
|
|
884
|
+
console.error("\nTypeScript type check failed:\n");
|
|
885
|
+
console.error(output);
|
|
886
|
+
throw new Error("TypeScript type check failed");
|
|
887
|
+
}
|
|
888
|
+
function resolveTypeScriptModule(workPath) {
|
|
889
|
+
try {
|
|
890
|
+
return require_(require_.resolve("typescript", { paths: [workPath] }));
|
|
891
|
+
} catch (_e) {
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
const findNearestTsconfig = async (workPath) => {
|
|
896
|
+
const tsconfigPath = join(workPath, "tsconfig.json");
|
|
897
|
+
if (existsSync(tsconfigPath)) return tsconfigPath;
|
|
898
|
+
if (workPath === "/") return;
|
|
899
|
+
return findNearestTsconfig(join(workPath, ".."));
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
//#endregion
|
|
903
|
+
//#region src/cervel/index.ts
|
|
904
|
+
const require$2 = createRequire(import.meta.url);
|
|
905
|
+
const getBuildSummary = async (outputDir) => {
|
|
906
|
+
const buildSummary = await readFile$1(join(outputDir, ".cervel.json"), "utf-8");
|
|
907
|
+
return JSON.parse(buildSummary);
|
|
908
|
+
};
|
|
909
|
+
const build$1 = async (args) => {
|
|
910
|
+
const entrypoint = args.entrypoint || await findEntrypointOrThrow(args.workPath);
|
|
911
|
+
const span = args.span ?? new Span({ name: "cervel-build" });
|
|
912
|
+
const [, rolldownResult] = await Promise.all([typescript({
|
|
913
|
+
entrypoint,
|
|
914
|
+
workPath: args.workPath,
|
|
915
|
+
span
|
|
916
|
+
}), rolldown$1({
|
|
917
|
+
entrypoint,
|
|
918
|
+
workPath: args.workPath,
|
|
919
|
+
repoRootPath: args.repoRootPath,
|
|
920
|
+
out: args.out,
|
|
921
|
+
span
|
|
922
|
+
})]);
|
|
923
|
+
await writeFile(join(args.workPath, args.out, ".cervel.json"), JSON.stringify({ handler: rolldownResult.result.handler }, null, 2));
|
|
924
|
+
console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Build complete — Using ${Colors.bold(entrypoint)} as the root entrypoint.`));
|
|
925
|
+
return { rolldownResult: rolldownResult.result };
|
|
926
|
+
};
|
|
927
|
+
const serve = async (args) => {
|
|
928
|
+
const entrypoint = await findEntrypointOrThrow(args.workPath);
|
|
929
|
+
const srvxBin = join(require$2.resolve("srvx"), "..", "..", "..", "bin", "srvx.mjs");
|
|
930
|
+
const tsxBin = require$2.resolve("tsx");
|
|
931
|
+
const restArgs = Object.entries(args.rest).filter(([, value]) => value !== void 0 && value !== false).map(([key, value]) => typeof value === "boolean" ? `--${key}` : `--${key}=${value}`);
|
|
932
|
+
if (!args.rest.import) restArgs.push("--import", tsxBin);
|
|
933
|
+
await execa("npx", [
|
|
934
|
+
srvxBin,
|
|
935
|
+
...restArgs,
|
|
936
|
+
entrypoint
|
|
937
|
+
], {
|
|
938
|
+
cwd: args.workPath,
|
|
939
|
+
stdio: "inherit"
|
|
940
|
+
});
|
|
941
|
+
};
|
|
942
|
+
const srvxOptions = {
|
|
943
|
+
help: {
|
|
944
|
+
type: "boolean",
|
|
945
|
+
short: "h"
|
|
946
|
+
},
|
|
947
|
+
version: {
|
|
948
|
+
type: "boolean",
|
|
949
|
+
short: "v"
|
|
950
|
+
},
|
|
951
|
+
prod: { type: "boolean" },
|
|
952
|
+
port: {
|
|
953
|
+
type: "string",
|
|
954
|
+
short: "p"
|
|
955
|
+
},
|
|
956
|
+
host: {
|
|
957
|
+
type: "string",
|
|
958
|
+
short: "H"
|
|
959
|
+
},
|
|
960
|
+
static: {
|
|
961
|
+
type: "string",
|
|
962
|
+
short: "s"
|
|
963
|
+
},
|
|
964
|
+
import: { type: "string" },
|
|
965
|
+
tls: { type: "boolean" },
|
|
966
|
+
cert: { type: "string" },
|
|
967
|
+
key: { type: "string" }
|
|
968
|
+
};
|
|
969
|
+
|
|
965
970
|
//#endregion
|
|
966
971
|
//#region src/rolldown/nft.ts
|
|
967
972
|
const nft = async (args) => {
|
|
@@ -1669,8 +1674,7 @@ const build = async (args) => {
|
|
|
1669
1674
|
span.setAttributes({ "builder.name": builderName });
|
|
1670
1675
|
const buildSpan = span.child("vc.builder.backends.build");
|
|
1671
1676
|
return buildSpan.trace(async () => {
|
|
1672
|
-
const
|
|
1673
|
-
const entrypoint = explicit && existsSync(join(args.workPath, explicit)) ? explicit : await findEntrypointOrThrow(args.workPath);
|
|
1677
|
+
const entrypoint = await findEntrypointWithHintOrThrow(args.workPath, args.entrypoint);
|
|
1674
1678
|
debug("Entrypoint", entrypoint);
|
|
1675
1679
|
args.entrypoint = entrypoint;
|
|
1676
1680
|
const cronEntries = await getServiceCrons({
|