@prisma/cli 3.0.0-dev.88.1 → 3.0.0-dev.90.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/app/index.js +2 -2
- package/dist/controllers/app-env.js +1 -1
- package/dist/controllers/app.js +25 -24
- package/dist/controllers/project.js +3 -3
- package/dist/lib/app/app-interaction.js +5 -0
- package/dist/lib/app/{preview-provider.js → app-provider.js} +9 -9
- package/dist/lib/app/{preview-branch-database.js → branch-database-api.js} +1 -1
- package/dist/lib/app/build-settings.js +93 -0
- package/dist/lib/app/build.js +80 -0
- package/dist/lib/app/bun-project.js +2 -3
- package/dist/lib/app/{preview-progress.js → deploy-progress.js} +5 -5
- package/package.json +2 -2
- package/dist/lib/app/preview-build-settings.js +0 -376
- package/dist/lib/app/preview-build.js +0 -501
- package/dist/lib/app/preview-interaction.js +0 -5
|
@@ -1,501 +0,0 @@
|
|
|
1
|
-
import { readBunPackageJson, resolveBunEntrypoint } from "./bun-project.js";
|
|
2
|
-
import { hasAnyPackageDependency, hasPackageDependency, hasRootFile, joinPosix, nextOutputRootFromStandaloneDirectory, resolvePreviewBuildSettings, runResolvedBuildCommand } from "./preview-build-settings.js";
|
|
3
|
-
import { resolveSourceRoot } from "@prisma/compute-sdk/config";
|
|
4
|
-
import { chmod, copyFile, cp, lstat, mkdir, mkdtemp, readdir, readlink, rm, stat, writeFile } from "node:fs/promises";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import os from "node:os";
|
|
7
|
-
import { AstroBuild, BunBuild, NuxtBuild } from "@prisma/compute-sdk";
|
|
8
|
-
//#region src/lib/app/preview-build.ts
|
|
9
|
-
const PREVIEW_BUILD_TYPES = [
|
|
10
|
-
"auto",
|
|
11
|
-
"bun",
|
|
12
|
-
"nextjs",
|
|
13
|
-
"nuxt",
|
|
14
|
-
"astro",
|
|
15
|
-
"tanstack-start"
|
|
16
|
-
];
|
|
17
|
-
const RESOLVED_PREVIEW_BUILD_TYPES = PREVIEW_BUILD_TYPES.filter((buildType) => buildType !== "auto");
|
|
18
|
-
var PreviewBuildStrategy = class {
|
|
19
|
-
#appPath;
|
|
20
|
-
#entrypoint;
|
|
21
|
-
#buildType;
|
|
22
|
-
#signal;
|
|
23
|
-
#buildSettings;
|
|
24
|
-
constructor(options) {
|
|
25
|
-
this.#appPath = options.appPath;
|
|
26
|
-
this.#entrypoint = options.entrypoint;
|
|
27
|
-
this.#buildType = options.buildType ?? "auto";
|
|
28
|
-
this.#signal = options.signal;
|
|
29
|
-
this.#buildSettings = options.buildSettings;
|
|
30
|
-
}
|
|
31
|
-
async canBuild(signal = this.#signal) {
|
|
32
|
-
const { strategy } = await resolvePreviewBuildStrategy({
|
|
33
|
-
appPath: this.#appPath,
|
|
34
|
-
entrypoint: this.#entrypoint,
|
|
35
|
-
buildType: this.#buildType,
|
|
36
|
-
signal,
|
|
37
|
-
buildSettings: this.#buildSettings
|
|
38
|
-
});
|
|
39
|
-
return strategy.canBuild(signal);
|
|
40
|
-
}
|
|
41
|
-
async execute(signal = this.#signal) {
|
|
42
|
-
const { artifact } = await executePreviewBuild({
|
|
43
|
-
appPath: this.#appPath,
|
|
44
|
-
entrypoint: this.#entrypoint,
|
|
45
|
-
buildType: this.#buildType,
|
|
46
|
-
signal,
|
|
47
|
-
buildSettings: this.#buildSettings
|
|
48
|
-
});
|
|
49
|
-
return artifact;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
async function executePreviewBuild(options) {
|
|
53
|
-
const { strategy, buildType } = await resolvePreviewBuildStrategy({
|
|
54
|
-
appPath: options.appPath,
|
|
55
|
-
entrypoint: options.entrypoint,
|
|
56
|
-
buildType: options.buildType ?? "auto",
|
|
57
|
-
signal: options.signal,
|
|
58
|
-
buildSettings: options.buildSettings
|
|
59
|
-
});
|
|
60
|
-
const artifact = await strategy.execute(options.signal);
|
|
61
|
-
try {
|
|
62
|
-
await normalizeArtifactSymlinks(artifact.directory, options.appPath, options.signal);
|
|
63
|
-
return {
|
|
64
|
-
artifact,
|
|
65
|
-
buildType
|
|
66
|
-
};
|
|
67
|
-
} catch (error) {
|
|
68
|
-
await artifact.cleanup?.().catch(() => void 0);
|
|
69
|
-
throw error;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
async function resolvePreviewBuildStrategy(options) {
|
|
73
|
-
if (options.buildType !== "auto") {
|
|
74
|
-
const strategy = await createPreviewBuildStrategy({
|
|
75
|
-
appPath: options.appPath,
|
|
76
|
-
entrypoint: options.entrypoint,
|
|
77
|
-
buildType: options.buildType,
|
|
78
|
-
signal: options.signal,
|
|
79
|
-
buildSettings: options.buildSettings
|
|
80
|
-
});
|
|
81
|
-
return {
|
|
82
|
-
buildType: options.buildType,
|
|
83
|
-
strategy
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
for (const buildType of RESOLVED_PREVIEW_BUILD_TYPES) {
|
|
87
|
-
if (buildType === "bun") continue;
|
|
88
|
-
const strategy = await createPreviewBuildStrategy({
|
|
89
|
-
appPath: options.appPath,
|
|
90
|
-
entrypoint: options.entrypoint,
|
|
91
|
-
buildType,
|
|
92
|
-
signal: options.signal,
|
|
93
|
-
buildSettings: options.buildSettings
|
|
94
|
-
});
|
|
95
|
-
if (await strategy.canBuild(options.signal)) return {
|
|
96
|
-
buildType,
|
|
97
|
-
strategy
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
buildType: "bun",
|
|
102
|
-
strategy: await createPreviewBuildStrategy({
|
|
103
|
-
appPath: options.appPath,
|
|
104
|
-
entrypoint: options.entrypoint,
|
|
105
|
-
buildType: "bun",
|
|
106
|
-
signal: options.signal,
|
|
107
|
-
buildSettings: options.buildSettings
|
|
108
|
-
})
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
async function createPreviewBuildStrategy(options) {
|
|
112
|
-
switch (options.buildType) {
|
|
113
|
-
case "nextjs": return new PreviewNextjsBuild({
|
|
114
|
-
appPath: options.appPath,
|
|
115
|
-
buildSettings: options.buildSettings
|
|
116
|
-
});
|
|
117
|
-
case "nuxt": return new NuxtBuild({ appPath: options.appPath });
|
|
118
|
-
case "astro": return new AstroBuild({ appPath: options.appPath });
|
|
119
|
-
case "tanstack-start": return new PreviewTanstackStartBuild({
|
|
120
|
-
appPath: options.appPath,
|
|
121
|
-
buildSettings: options.buildSettings
|
|
122
|
-
});
|
|
123
|
-
case "bun": {
|
|
124
|
-
const entrypoint = await resolveBunEntrypoint(options.appPath, options.entrypoint, options.signal);
|
|
125
|
-
return new PreviewBunBuild({
|
|
126
|
-
appPath: options.appPath,
|
|
127
|
-
strategy: new BunBuild({
|
|
128
|
-
appPath: options.appPath,
|
|
129
|
-
entrypoint
|
|
130
|
-
}),
|
|
131
|
-
buildSettings: options.buildSettings
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
var PreviewNextjsBuild = class {
|
|
137
|
-
#appPath;
|
|
138
|
-
#buildSettings;
|
|
139
|
-
constructor(options) {
|
|
140
|
-
this.#appPath = options.appPath;
|
|
141
|
-
this.#buildSettings = options.buildSettings;
|
|
142
|
-
}
|
|
143
|
-
async canBuild(signal) {
|
|
144
|
-
const packageJson = await readBunPackageJson(this.#appPath, signal);
|
|
145
|
-
return await hasRootFile(this.#appPath, NEXT_CONFIG_FILENAMES, signal) || hasPackageDependency(packageJson, "next");
|
|
146
|
-
}
|
|
147
|
-
async execute(signal) {
|
|
148
|
-
const settings = this.#buildSettings ?? await resolvePreviewBuildSettings({
|
|
149
|
-
appPath: this.#appPath,
|
|
150
|
-
buildType: "nextjs",
|
|
151
|
-
signal
|
|
152
|
-
});
|
|
153
|
-
await runResolvedBuildCommand(this.#appPath, settings, "Next.js", signal);
|
|
154
|
-
const standaloneDir = path.join(this.#appPath, settings.outputDirectory);
|
|
155
|
-
if (!await directoryExists(standaloneDir, signal)) return stageNextjsFullTreeFallbackArtifact(this.#appPath, signal);
|
|
156
|
-
const outDir = await unsupportedFilesystemBoundary(signal, () => mkdtemp(path.join(os.tmpdir(), "compute-build-")));
|
|
157
|
-
try {
|
|
158
|
-
const artifactDir = path.join(outDir, "app");
|
|
159
|
-
await stageNextjsStandaloneArtifact({
|
|
160
|
-
standaloneDir,
|
|
161
|
-
artifactDir,
|
|
162
|
-
appPath: this.#appPath,
|
|
163
|
-
signal
|
|
164
|
-
});
|
|
165
|
-
const entrypoint = await findNextStandaloneEntrypoint(artifactDir, signal);
|
|
166
|
-
await copyNextjsStaticAssets({
|
|
167
|
-
appPath: this.#appPath,
|
|
168
|
-
artifactDir,
|
|
169
|
-
outputRoot: nextOutputRootFromStandaloneDirectory(settings.outputDirectory),
|
|
170
|
-
entrypoint,
|
|
171
|
-
signal
|
|
172
|
-
});
|
|
173
|
-
return {
|
|
174
|
-
directory: artifactDir,
|
|
175
|
-
entrypoint,
|
|
176
|
-
defaultPortMapping: { http: 3e3 },
|
|
177
|
-
cleanup: () => rm(outDir, {
|
|
178
|
-
recursive: true,
|
|
179
|
-
force: true
|
|
180
|
-
})
|
|
181
|
-
};
|
|
182
|
-
} catch (error) {
|
|
183
|
-
await rm(outDir, {
|
|
184
|
-
recursive: true,
|
|
185
|
-
force: true
|
|
186
|
-
});
|
|
187
|
-
throw error;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
const FULL_TREE_NEXT_START_ENTRYPOINT = "prisma-next-start.cjs";
|
|
192
|
-
const FULL_TREE_NEXT_START_SOURCE = [
|
|
193
|
-
"process.chdir(__dirname);",
|
|
194
|
-
"process.env.NODE_ENV = \"production\";",
|
|
195
|
-
"process.argv.push(\"start\", \"-p\", process.env.PORT ?? \"3000\");",
|
|
196
|
-
"require(\"next/dist/bin/next\");",
|
|
197
|
-
""
|
|
198
|
-
].join("\n");
|
|
199
|
-
async function stageNextjsFullTreeFallbackArtifact(appPath, signal) {
|
|
200
|
-
const outDir = await unsupportedFilesystemBoundary(signal, () => mkdtemp(path.join(os.tmpdir(), "compute-build-")));
|
|
201
|
-
try {
|
|
202
|
-
const artifactDir = path.join(outDir, "app");
|
|
203
|
-
await unsupportedFilesystemBoundary(signal, () => cp(appPath, artifactDir, {
|
|
204
|
-
recursive: true,
|
|
205
|
-
verbatimSymlinks: true,
|
|
206
|
-
filter: (source) => !isExcludedFromFullTreeArtifact(path.basename(source))
|
|
207
|
-
}));
|
|
208
|
-
await unsupportedFilesystemBoundary(signal, () => writeFile(path.join(artifactDir, FULL_TREE_NEXT_START_ENTRYPOINT), FULL_TREE_NEXT_START_SOURCE));
|
|
209
|
-
return {
|
|
210
|
-
directory: artifactDir,
|
|
211
|
-
entrypoint: FULL_TREE_NEXT_START_ENTRYPOINT,
|
|
212
|
-
defaultPortMapping: { http: 3e3 },
|
|
213
|
-
cleanup: () => rm(outDir, {
|
|
214
|
-
recursive: true,
|
|
215
|
-
force: true
|
|
216
|
-
})
|
|
217
|
-
};
|
|
218
|
-
} catch (error) {
|
|
219
|
-
await rm(outDir, {
|
|
220
|
-
recursive: true,
|
|
221
|
-
force: true
|
|
222
|
-
});
|
|
223
|
-
throw error;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
/** Excludes VCS internals and dotenv files (local secrets, superseded by the deploy env). */
|
|
227
|
-
function isExcludedFromFullTreeArtifact(basename) {
|
|
228
|
-
return basename === ".git" || basename === ".env" || basename.startsWith(".env.");
|
|
229
|
-
}
|
|
230
|
-
var PreviewTanstackStartBuild = class {
|
|
231
|
-
#appPath;
|
|
232
|
-
#buildSettings;
|
|
233
|
-
constructor(options) {
|
|
234
|
-
this.#appPath = options.appPath;
|
|
235
|
-
this.#buildSettings = options.buildSettings;
|
|
236
|
-
}
|
|
237
|
-
async canBuild(signal) {
|
|
238
|
-
return hasAnyPackageDependency(await readBunPackageJson(this.#appPath, signal), TANSTACK_START_PACKAGES);
|
|
239
|
-
}
|
|
240
|
-
async execute(signal) {
|
|
241
|
-
const settings = this.#buildSettings ?? await resolvePreviewBuildSettings({
|
|
242
|
-
appPath: this.#appPath,
|
|
243
|
-
buildType: "tanstack-start",
|
|
244
|
-
signal
|
|
245
|
-
});
|
|
246
|
-
await runResolvedBuildCommand(this.#appPath, settings, "TanStack Start", signal);
|
|
247
|
-
const outputDir = path.join(this.#appPath, settings.outputDirectory);
|
|
248
|
-
const entrypoint = "server/index.mjs";
|
|
249
|
-
const entryPath = path.join(outputDir, entrypoint);
|
|
250
|
-
if (!(await unsupportedFilesystemBoundary(signal, () => stat(entryPath).catch(() => null)))?.isFile()) throw new Error(`TanStack Start build did not produce a Nitro node server entrypoint at ${joinPosix(settings.outputDirectory, entrypoint)}. Ensure your vite.config includes the tanstackStart() and nitro() plugins with the default node preset, or set build.outputDirectory in prisma.compute.ts.`);
|
|
251
|
-
const outDir = await unsupportedFilesystemBoundary(signal, () => mkdtemp(path.join(os.tmpdir(), "compute-build-")));
|
|
252
|
-
try {
|
|
253
|
-
const artifactDir = path.join(outDir, "app");
|
|
254
|
-
await unsupportedFilesystemBoundary(signal, () => cp(outputDir, artifactDir, {
|
|
255
|
-
recursive: true,
|
|
256
|
-
verbatimSymlinks: true
|
|
257
|
-
}));
|
|
258
|
-
return {
|
|
259
|
-
directory: artifactDir,
|
|
260
|
-
entrypoint,
|
|
261
|
-
defaultPortMapping: { http: 3e3 },
|
|
262
|
-
cleanup: () => rm(outDir, {
|
|
263
|
-
recursive: true,
|
|
264
|
-
force: true
|
|
265
|
-
})
|
|
266
|
-
};
|
|
267
|
-
} catch (error) {
|
|
268
|
-
await rm(outDir, {
|
|
269
|
-
recursive: true,
|
|
270
|
-
force: true
|
|
271
|
-
});
|
|
272
|
-
throw error;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
var PreviewBunBuild = class {
|
|
277
|
-
#appPath;
|
|
278
|
-
#strategy;
|
|
279
|
-
#buildSettings;
|
|
280
|
-
constructor(options) {
|
|
281
|
-
this.#appPath = options.appPath;
|
|
282
|
-
this.#strategy = options.strategy;
|
|
283
|
-
this.#buildSettings = options.buildSettings;
|
|
284
|
-
}
|
|
285
|
-
async canBuild(signal) {
|
|
286
|
-
return this.#strategy.canBuild(signal);
|
|
287
|
-
}
|
|
288
|
-
async execute(signal) {
|
|
289
|
-
const settings = this.#buildSettings ?? await resolvePreviewBuildSettings({
|
|
290
|
-
appPath: this.#appPath,
|
|
291
|
-
buildType: "bun",
|
|
292
|
-
signal
|
|
293
|
-
});
|
|
294
|
-
await runResolvedBuildCommand(this.#appPath, settings, "Bun", signal);
|
|
295
|
-
return this.#strategy.execute(signal);
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
const NEXT_CONFIG_FILENAMES = [
|
|
299
|
-
"next.config.js",
|
|
300
|
-
"next.config.mjs",
|
|
301
|
-
"next.config.ts",
|
|
302
|
-
"next.config.mts"
|
|
303
|
-
];
|
|
304
|
-
const TANSTACK_START_PACKAGES = [
|
|
305
|
-
"@tanstack/react-start",
|
|
306
|
-
"@tanstack/solid-start",
|
|
307
|
-
"@tanstack/start"
|
|
308
|
-
];
|
|
309
|
-
async function stageNextjsStandaloneArtifact(options) {
|
|
310
|
-
const standaloneRoot = path.resolve(options.standaloneDir);
|
|
311
|
-
const artifactRoot = path.resolve(options.artifactDir);
|
|
312
|
-
const appRoot = path.resolve(options.appPath);
|
|
313
|
-
await copyPathMaterializingSymlinks(standaloneRoot, artifactRoot, {
|
|
314
|
-
standaloneRoot,
|
|
315
|
-
appRoot,
|
|
316
|
-
sourceRoot: await resolveSourceRoot(appRoot, options.signal),
|
|
317
|
-
signal: options.signal
|
|
318
|
-
});
|
|
319
|
-
await hoistIsolatedStoreDependencies(path.join(artifactRoot, "node_modules"), options.signal);
|
|
320
|
-
}
|
|
321
|
-
async function copyNextjsStaticAssets(options) {
|
|
322
|
-
const serverSubpath = nextjsServerSubpath(options.entrypoint);
|
|
323
|
-
const serverDir = serverSubpath ? path.join(options.artifactDir, serverSubpath) : options.artifactDir;
|
|
324
|
-
const publicDir = path.join(options.appPath, "public");
|
|
325
|
-
if (await directoryExists(publicDir, options.signal)) await unsupportedFilesystemBoundary(options.signal, () => cp(publicDir, path.join(serverDir, "public"), {
|
|
326
|
-
recursive: true,
|
|
327
|
-
verbatimSymlinks: true
|
|
328
|
-
}));
|
|
329
|
-
const staticDir = path.join(options.appPath, options.outputRoot, "static");
|
|
330
|
-
if (await directoryExists(staticDir, options.signal)) await unsupportedFilesystemBoundary(options.signal, () => cp(staticDir, path.join(serverDir, options.outputRoot, "static"), {
|
|
331
|
-
recursive: true,
|
|
332
|
-
verbatimSymlinks: true
|
|
333
|
-
}));
|
|
334
|
-
}
|
|
335
|
-
async function findNextStandaloneEntrypoint(artifactDir, signal) {
|
|
336
|
-
const rootEntrypoint = path.join(artifactDir, "server.js");
|
|
337
|
-
if ((await unsupportedFilesystemBoundary(signal, () => stat(rootEntrypoint).catch(() => null)))?.isFile()) return "server.js";
|
|
338
|
-
const candidates = [];
|
|
339
|
-
await walk(artifactDir);
|
|
340
|
-
candidates.sort((left, right) => left.split("/").length - right.split("/").length || left.localeCompare(right));
|
|
341
|
-
const selected = candidates[0];
|
|
342
|
-
if (!selected) throw new Error(`Next.js standalone output did not contain server.js in ${artifactDir}`);
|
|
343
|
-
return selected;
|
|
344
|
-
async function walk(directory) {
|
|
345
|
-
const entries = await unsupportedFilesystemBoundary(signal, () => readdir(directory, { withFileTypes: true }));
|
|
346
|
-
for (const entry of entries) {
|
|
347
|
-
if (entry.name === "node_modules") continue;
|
|
348
|
-
const fullPath = path.join(directory, entry.name);
|
|
349
|
-
if (entry.isDirectory()) {
|
|
350
|
-
await walk(fullPath);
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
if (entry.isFile() && entry.name === "server.js") candidates.push(path.relative(artifactDir, fullPath).split(path.sep).join("/"));
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
function nextjsServerSubpath(entrypoint) {
|
|
358
|
-
const dir = path.posix.dirname(entrypoint);
|
|
359
|
-
return dir === "." ? "" : dir;
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* pnpm and bun (isolated linker) both keep packages in a virtual store with a
|
|
363
|
-
* shared symlink farm (`.pnpm/node_modules`, `.bun/node_modules`). Hoist the
|
|
364
|
-
* farm entries to the artifact's node_modules root so Node-style resolution
|
|
365
|
-
* works after symlinks are materialized.
|
|
366
|
-
*/
|
|
367
|
-
async function hoistIsolatedStoreDependencies(nodeModulesDir, signal) {
|
|
368
|
-
await hoistStoreDependencies(nodeModulesDir, path.join(nodeModulesDir, ".pnpm", "node_modules"), signal);
|
|
369
|
-
await hoistStoreDependencies(nodeModulesDir, path.join(nodeModulesDir, ".bun", "node_modules"), signal);
|
|
370
|
-
}
|
|
371
|
-
async function hoistStoreDependencies(nodeModulesDir, storeNodeModulesDir, signal) {
|
|
372
|
-
if (!await directoryExists(storeNodeModulesDir, signal)) return;
|
|
373
|
-
const entries = await unsupportedFilesystemBoundary(signal, () => readdir(storeNodeModulesDir, { withFileTypes: true }));
|
|
374
|
-
for (const entry of entries) {
|
|
375
|
-
const sourcePath = path.join(storeNodeModulesDir, entry.name);
|
|
376
|
-
if (entry.name.startsWith("@") && entry.isDirectory()) {
|
|
377
|
-
const scopedEntries = await unsupportedFilesystemBoundary(signal, () => readdir(sourcePath, { withFileTypes: true }));
|
|
378
|
-
for (const scopedEntry of scopedEntries) {
|
|
379
|
-
const scopedDestination = path.join(nodeModulesDir, entry.name, scopedEntry.name);
|
|
380
|
-
if (await pathExists(scopedDestination, signal)) continue;
|
|
381
|
-
await unsupportedFilesystemBoundary(signal, () => mkdir(path.dirname(scopedDestination), { recursive: true }));
|
|
382
|
-
await copyPathMaterializingSymlinks(path.join(sourcePath, scopedEntry.name), scopedDestination, {
|
|
383
|
-
standaloneRoot: storeNodeModulesDir,
|
|
384
|
-
appRoot: nodeModulesDir,
|
|
385
|
-
sourceRoot: nodeModulesDir,
|
|
386
|
-
signal
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
const destinationPath = path.join(nodeModulesDir, entry.name);
|
|
392
|
-
if (await pathExists(destinationPath, signal)) continue;
|
|
393
|
-
await copyPathMaterializingSymlinks(sourcePath, destinationPath, {
|
|
394
|
-
standaloneRoot: storeNodeModulesDir,
|
|
395
|
-
appRoot: nodeModulesDir,
|
|
396
|
-
sourceRoot: nodeModulesDir,
|
|
397
|
-
signal
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
async function normalizeArtifactSymlinks(artifactDir, appPath, signal) {
|
|
402
|
-
const normalizedArtifactDir = path.resolve(artifactDir);
|
|
403
|
-
const normalizedAppPath = path.resolve(appPath);
|
|
404
|
-
await walkDirectory(normalizedArtifactDir);
|
|
405
|
-
async function walkDirectory(directory) {
|
|
406
|
-
const entries = await unsupportedFilesystemBoundary(signal, () => readdir(directory, { withFileTypes: true }));
|
|
407
|
-
for (const entry of entries) {
|
|
408
|
-
const fullPath = path.join(directory, entry.name);
|
|
409
|
-
if (entry.isDirectory()) {
|
|
410
|
-
await walkDirectory(fullPath);
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
if (!entry.isSymbolicLink()) continue;
|
|
414
|
-
const target = await unsupportedFilesystemBoundary(signal, () => readlink(fullPath));
|
|
415
|
-
const resolvedTarget = path.resolve(path.dirname(fullPath), target);
|
|
416
|
-
if (isPathWithin(normalizedArtifactDir, resolvedTarget)) continue;
|
|
417
|
-
if (!isPathWithin(normalizedAppPath, resolvedTarget)) throw new Error(`Build artifact symlink escapes the app directory: ${resolvedTarget}`);
|
|
418
|
-
const targetStat = await unsupportedFilesystemBoundary(signal, () => stat(resolvedTarget));
|
|
419
|
-
await unsupportedFilesystemBoundary(signal, () => rm(fullPath, {
|
|
420
|
-
force: true,
|
|
421
|
-
recursive: true
|
|
422
|
-
}));
|
|
423
|
-
await unsupportedFilesystemBoundary(signal, () => cp(resolvedTarget, fullPath, {
|
|
424
|
-
recursive: targetStat.isDirectory(),
|
|
425
|
-
dereference: true
|
|
426
|
-
}));
|
|
427
|
-
if (targetStat.isDirectory()) await walkDirectory(fullPath);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
function isPathWithin(rootPath, candidatePath) {
|
|
432
|
-
const relativePath = path.relative(rootPath, candidatePath);
|
|
433
|
-
return relativePath === "" || !relativePath.startsWith(`..${path.sep}`) && relativePath !== ".." && !path.isAbsolute(relativePath);
|
|
434
|
-
}
|
|
435
|
-
function isPathWithinWorkspaceDependency(sourceRoot, candidatePath) {
|
|
436
|
-
return isPathWithin(path.join(sourceRoot, "node_modules"), candidatePath);
|
|
437
|
-
}
|
|
438
|
-
async function copyPathMaterializingSymlinks(sourcePath, destinationPath, options) {
|
|
439
|
-
const sourceStat = await unsupportedFilesystemBoundary(options.signal, () => lstat(sourcePath));
|
|
440
|
-
if (sourceStat.isSymbolicLink()) {
|
|
441
|
-
const resolvedTarget = await resolveSymlinkTarget(sourcePath, options);
|
|
442
|
-
if (resolvedTarget === null) return;
|
|
443
|
-
await copyPathMaterializingSymlinks(resolvedTarget, destinationPath, options);
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
if (sourceStat.isDirectory()) {
|
|
447
|
-
await unsupportedFilesystemBoundary(options.signal, () => mkdir(destinationPath, { recursive: true }));
|
|
448
|
-
const entries = await unsupportedFilesystemBoundary(options.signal, () => readdir(sourcePath, { withFileTypes: true }));
|
|
449
|
-
for (const entry of entries) await copyPathMaterializingSymlinks(path.join(sourcePath, entry.name), path.join(destinationPath, entry.name), options);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
if (sourceStat.isFile()) {
|
|
453
|
-
await unsupportedFilesystemBoundary(options.signal, () => mkdir(path.dirname(destinationPath), { recursive: true }));
|
|
454
|
-
await unsupportedFilesystemBoundary(options.signal, () => copyFile(sourcePath, destinationPath));
|
|
455
|
-
await unsupportedFilesystemBoundary(options.signal, () => chmod(destinationPath, sourceStat.mode));
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
async function resolveSymlinkTarget(symlinkPath, options) {
|
|
459
|
-
const linkTarget = await unsupportedFilesystemBoundary(options.signal, () => readlink(symlinkPath));
|
|
460
|
-
const resolvedTarget = path.resolve(path.dirname(symlinkPath), linkTarget);
|
|
461
|
-
if (await pathExists(resolvedTarget, options.signal)) {
|
|
462
|
-
if (!isPathWithin(options.appRoot, resolvedTarget) && !isPathWithinWorkspaceDependency(options.sourceRoot, resolvedTarget)) throw new Error(`Build artifact symlink escapes the app directory: ${resolvedTarget}`);
|
|
463
|
-
return resolvedTarget;
|
|
464
|
-
}
|
|
465
|
-
if (isPathWithin(options.standaloneRoot, resolvedTarget)) {
|
|
466
|
-
const fallbackTarget = path.join(options.appRoot, path.relative(options.standaloneRoot, resolvedTarget));
|
|
467
|
-
if (await pathExists(fallbackTarget, options.signal)) return fallbackTarget;
|
|
468
|
-
}
|
|
469
|
-
if (isPnpmHoistLink(symlinkPath)) return null;
|
|
470
|
-
throw new Error(`Next.js standalone symlink target is missing: ${symlinkPath} -> ${linkTarget} (resolved to ${resolvedTarget})`);
|
|
471
|
-
}
|
|
472
|
-
function isPnpmHoistLink(symlinkPath) {
|
|
473
|
-
const parts = path.dirname(symlinkPath).split(path.sep);
|
|
474
|
-
for (let i = 0; i < parts.length - 1; i++) if (parts[i] === ".pnpm" && parts[i + 1] === "node_modules") return true;
|
|
475
|
-
return false;
|
|
476
|
-
}
|
|
477
|
-
async function pathExists(targetPath, signal) {
|
|
478
|
-
try {
|
|
479
|
-
await unsupportedFilesystemBoundary(signal, () => stat(targetPath));
|
|
480
|
-
return true;
|
|
481
|
-
} catch (error) {
|
|
482
|
-
if (signal?.aborted) throw error;
|
|
483
|
-
return false;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
async function directoryExists(targetPath, signal) {
|
|
487
|
-
try {
|
|
488
|
-
return (await unsupportedFilesystemBoundary(signal, () => stat(targetPath))).isDirectory();
|
|
489
|
-
} catch (error) {
|
|
490
|
-
if (signal?.aborted) throw error;
|
|
491
|
-
return false;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
async function unsupportedFilesystemBoundary(signal, operation) {
|
|
495
|
-
signal?.throwIfAborted();
|
|
496
|
-
const result = await operation();
|
|
497
|
-
signal?.throwIfAborted();
|
|
498
|
-
return result;
|
|
499
|
-
}
|
|
500
|
-
//#endregion
|
|
501
|
-
export { PREVIEW_BUILD_TYPES, PreviewBuildStrategy, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild };
|