@prisma/compute-sdk 0.25.0 → 0.27.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/artifact-stage.d.ts +24 -0
- package/dist/artifact-stage.d.ts.map +1 -0
- package/dist/artifact-stage.js +165 -0
- package/dist/artifact-stage.js.map +1 -0
- package/dist/astro-build.d.ts +2 -0
- package/dist/astro-build.d.ts.map +1 -1
- package/dist/astro-build.js +10 -1
- package/dist/astro-build.js.map +1 -1
- package/dist/auto-build.d.ts +7 -4
- package/dist/auto-build.d.ts.map +1 -1
- package/dist/auto-build.js +25 -33
- package/dist/auto-build.js.map +1 -1
- package/dist/build-settings.d.ts +55 -0
- package/dist/build-settings.d.ts.map +1 -0
- package/dist/build-settings.js +301 -0
- package/dist/build-settings.js.map +1 -0
- package/dist/build-strategy.d.ts +4 -0
- package/dist/build-strategy.d.ts.map +1 -1
- package/dist/build-strategy.js +13 -20
- package/dist/build-strategy.js.map +1 -1
- package/dist/build.d.ts +45 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +86 -0
- package/dist/build.js.map +1 -0
- package/dist/bun-build.d.ts +8 -0
- package/dist/bun-build.d.ts.map +1 -1
- package/dist/bun-build.js +49 -23
- package/dist/bun-build.js.map +1 -1
- package/dist/config/frameworks.d.ts +8 -1
- package/dist/config/frameworks.d.ts.map +1 -1
- package/dist/config/frameworks.js +28 -0
- package/dist/config/frameworks.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/nextjs-build.d.ts +12 -2
- package/dist/nextjs-build.d.ts.map +1 -1
- package/dist/nextjs-build.js +158 -76
- package/dist/nextjs-build.js.map +1 -1
- package/dist/nuxt-build.d.ts +2 -0
- package/dist/nuxt-build.d.ts.map +1 -1
- package/dist/nuxt-build.js +10 -1
- package/dist/nuxt-build.js.map +1 -1
- package/dist/tanstack-start-build.d.ts +6 -1
- package/dist/tanstack-start-build.d.ts.map +1 -1
- package/dist/tanstack-start-build.js +39 -16
- package/dist/tanstack-start-build.js.map +1 -1
- package/dist/workspace.d.ts +90 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +205 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +4 -3
- package/src/artifact-stage.ts +280 -0
- package/src/astro-build.ts +13 -2
- package/src/auto-build.ts +28 -34
- package/src/build-settings.ts +430 -0
- package/src/build-strategy.ts +22 -29
- package/src/build.ts +124 -0
- package/src/bun-build.ts +62 -27
- package/src/config/frameworks.ts +38 -0
- package/src/config/index.ts +1 -0
- package/src/index.ts +28 -0
- package/src/nextjs-build.ts +208 -104
- package/src/nuxt-build.ts +13 -2
- package/src/tanstack-start-build.ts +48 -19
- package/src/workspace.ts +319 -0
package/src/bun-build.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
1
|
import {
|
|
3
2
|
access,
|
|
4
3
|
mkdir,
|
|
@@ -9,19 +8,40 @@ import {
|
|
|
9
8
|
} from "node:fs/promises";
|
|
10
9
|
import os from "node:os";
|
|
11
10
|
import path from "node:path";
|
|
11
|
+
import type { BuildSettings } from "./build-settings.ts";
|
|
12
12
|
import type { BuildArtifact, BuildStrategy } from "./build-strategy.ts";
|
|
13
|
+
import { defaultHttpPortForBuildType } from "./config/frameworks.ts";
|
|
14
|
+
import {
|
|
15
|
+
type BuildCommandIo,
|
|
16
|
+
buildCommandEnv,
|
|
17
|
+
runBuildCommand,
|
|
18
|
+
runChildProcess,
|
|
19
|
+
} from "./workspace.ts";
|
|
13
20
|
|
|
14
21
|
/**
|
|
15
22
|
* Build strategy that runs `bun build` CLI and manages its own temp output directory.
|
|
16
23
|
* Owns entrypoint resolution from explicit arg or package.json main field.
|
|
24
|
+
*
|
|
25
|
+
* When build settings carry a build command (a `package.json` build script),
|
|
26
|
+
* it runs first — so a Bun app can compile assets or generate code before the
|
|
27
|
+
* entrypoint is bundled.
|
|
17
28
|
*/
|
|
18
29
|
export class BunBuild implements BuildStrategy {
|
|
19
30
|
readonly #appPath: string;
|
|
20
31
|
readonly #entrypoint?: string;
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
readonly #buildSettings?: BuildSettings;
|
|
33
|
+
readonly #io?: BuildCommandIo;
|
|
34
|
+
|
|
35
|
+
constructor(options: {
|
|
36
|
+
appPath: string;
|
|
37
|
+
entrypoint?: string;
|
|
38
|
+
buildSettings?: BuildSettings;
|
|
39
|
+
io?: BuildCommandIo;
|
|
40
|
+
}) {
|
|
23
41
|
this.#appPath = options.appPath;
|
|
24
42
|
this.#entrypoint = options.entrypoint;
|
|
43
|
+
this.#buildSettings = options.buildSettings;
|
|
44
|
+
this.#io = options.io;
|
|
25
45
|
}
|
|
26
46
|
|
|
27
47
|
async canBuild(_signal?: AbortSignal): Promise<boolean> {
|
|
@@ -30,6 +50,16 @@ export class BunBuild implements BuildStrategy {
|
|
|
30
50
|
|
|
31
51
|
async execute(signal?: AbortSignal): Promise<BuildArtifact> {
|
|
32
52
|
signal?.throwIfAborted();
|
|
53
|
+
if (this.#buildSettings?.buildCommand) {
|
|
54
|
+
await runBuildCommand({
|
|
55
|
+
appPath: this.#appPath,
|
|
56
|
+
command: this.#buildSettings.buildCommand,
|
|
57
|
+
failurePrefix: "Bun",
|
|
58
|
+
env: this.#io?.env,
|
|
59
|
+
onOutput: this.#io?.onOutput,
|
|
60
|
+
signal,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
33
63
|
const entrypoint = await this.#resolveEntrypoint(signal);
|
|
34
64
|
|
|
35
65
|
const outDir = await mkdtemp(path.join(os.tmpdir(), "compute-build-"));
|
|
@@ -59,6 +89,7 @@ export class BunBuild implements BuildStrategy {
|
|
|
59
89
|
return {
|
|
60
90
|
directory: bundleDir,
|
|
61
91
|
entrypoint: runtimeEntrypoint,
|
|
92
|
+
defaultPortMapping: { http: defaultHttpPortForBuildType("bun") },
|
|
62
93
|
cleanup: () => rm(outDir, { recursive: true, force: true }),
|
|
63
94
|
};
|
|
64
95
|
}
|
|
@@ -127,15 +158,20 @@ export class BunBuild implements BuildStrategy {
|
|
|
127
158
|
return typeof parsed.main === "string" ? parsed.main : undefined;
|
|
128
159
|
}
|
|
129
160
|
|
|
130
|
-
#runBuild(
|
|
161
|
+
async #runBuild(
|
|
131
162
|
absoluteEntrypoint: string,
|
|
132
163
|
bundleDir: string,
|
|
133
164
|
signal?: AbortSignal,
|
|
134
165
|
): Promise<void> {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
166
|
+
const env = await buildCommandEnv(
|
|
167
|
+
this.#appPath,
|
|
168
|
+
{ ...process.env, ...this.#io?.env },
|
|
169
|
+
signal,
|
|
170
|
+
);
|
|
171
|
+
try {
|
|
172
|
+
await runChildProcess({
|
|
173
|
+
command: "bun",
|
|
174
|
+
args: [
|
|
139
175
|
"build",
|
|
140
176
|
absoluteEntrypoint,
|
|
141
177
|
"--outdir",
|
|
@@ -144,25 +180,24 @@ export class BunBuild implements BuildStrategy {
|
|
|
144
180
|
"bun",
|
|
145
181
|
"--sourcemap=external",
|
|
146
182
|
],
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
});
|
|
183
|
+
cwd: this.#appPath,
|
|
184
|
+
env,
|
|
185
|
+
failurePrefix: "Bun",
|
|
186
|
+
onOutput: this.#io?.onOutput,
|
|
187
|
+
signal,
|
|
188
|
+
});
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (
|
|
191
|
+
error instanceof Error &&
|
|
192
|
+
"code" in error &&
|
|
193
|
+
(error as NodeJS.ErrnoException).code === "ENOENT"
|
|
194
|
+
) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
"Bun is required to build the application but was not found. Install it from https://bun.sh",
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
166
201
|
}
|
|
167
202
|
|
|
168
203
|
#selectRuntimeEntrypoint(
|
package/src/config/frameworks.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface FrameworkDescriptor {
|
|
|
31
31
|
readonly defaultEntrypoint: string | null;
|
|
32
32
|
/** Has a local dev server (`app run`) in the current preview. */
|
|
33
33
|
readonly hasLocalDevServer: boolean;
|
|
34
|
+
/** Port the framework's built app listens on by default; the gateway routes here unless overridden. */
|
|
35
|
+
readonly defaultHttpPort: number;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export const NEXT_CONFIG_FILENAMES = [
|
|
@@ -68,6 +70,7 @@ export const FRAMEWORKS: readonly FrameworkDescriptor[] = [
|
|
|
68
70
|
usesEntrypoint: false,
|
|
69
71
|
defaultEntrypoint: null,
|
|
70
72
|
hasLocalDevServer: true,
|
|
73
|
+
defaultHttpPort: 3000,
|
|
71
74
|
},
|
|
72
75
|
{
|
|
73
76
|
key: "nuxt",
|
|
@@ -79,6 +82,7 @@ export const FRAMEWORKS: readonly FrameworkDescriptor[] = [
|
|
|
79
82
|
usesEntrypoint: false,
|
|
80
83
|
defaultEntrypoint: null,
|
|
81
84
|
hasLocalDevServer: false,
|
|
85
|
+
defaultHttpPort: 3000,
|
|
82
86
|
},
|
|
83
87
|
{
|
|
84
88
|
key: "astro",
|
|
@@ -90,6 +94,7 @@ export const FRAMEWORKS: readonly FrameworkDescriptor[] = [
|
|
|
90
94
|
usesEntrypoint: false,
|
|
91
95
|
defaultEntrypoint: null,
|
|
92
96
|
hasLocalDevServer: false,
|
|
97
|
+
defaultHttpPort: 4321,
|
|
93
98
|
},
|
|
94
99
|
{
|
|
95
100
|
key: "hono",
|
|
@@ -101,6 +106,7 @@ export const FRAMEWORKS: readonly FrameworkDescriptor[] = [
|
|
|
101
106
|
usesEntrypoint: true,
|
|
102
107
|
defaultEntrypoint: "src/index.ts",
|
|
103
108
|
hasLocalDevServer: true,
|
|
109
|
+
defaultHttpPort: 3000,
|
|
104
110
|
},
|
|
105
111
|
{
|
|
106
112
|
key: "tanstack-start",
|
|
@@ -117,6 +123,7 @@ export const FRAMEWORKS: readonly FrameworkDescriptor[] = [
|
|
|
117
123
|
usesEntrypoint: false,
|
|
118
124
|
defaultEntrypoint: null,
|
|
119
125
|
hasLocalDevServer: false,
|
|
126
|
+
defaultHttpPort: 3000,
|
|
120
127
|
},
|
|
121
128
|
{
|
|
122
129
|
key: "bun",
|
|
@@ -128,6 +135,7 @@ export const FRAMEWORKS: readonly FrameworkDescriptor[] = [
|
|
|
128
135
|
usesEntrypoint: true,
|
|
129
136
|
defaultEntrypoint: null,
|
|
130
137
|
hasLocalDevServer: true,
|
|
138
|
+
defaultHttpPort: 3000,
|
|
131
139
|
},
|
|
132
140
|
];
|
|
133
141
|
|
|
@@ -164,6 +172,36 @@ export const LOCAL_DEV_BUILD_TYPES: readonly FrameworkBuildType[] = [
|
|
|
164
172
|
),
|
|
165
173
|
];
|
|
166
174
|
|
|
175
|
+
const DEFAULT_HTTP_PORT_BY_BUILD_TYPE: Readonly<
|
|
176
|
+
Record<FrameworkBuildType, number>
|
|
177
|
+
> = (() => {
|
|
178
|
+
const ports = new Map<FrameworkBuildType, number>();
|
|
179
|
+
for (const framework of FRAMEWORKS) {
|
|
180
|
+
const existing = ports.get(framework.buildType);
|
|
181
|
+
if (existing !== undefined && existing !== framework.defaultHttpPort) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Conflicting defaultHttpPort for build type "${framework.buildType}".`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
ports.set(framework.buildType, framework.defaultHttpPort);
|
|
187
|
+
}
|
|
188
|
+
return Object.fromEntries(ports) as Record<FrameworkBuildType, number>;
|
|
189
|
+
})();
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Default HTTP port for a build type, sourced from the framework registry so
|
|
193
|
+
* build strategies and deploy front doors agree on where the gateway routes.
|
|
194
|
+
*/
|
|
195
|
+
export function defaultHttpPortForBuildType(
|
|
196
|
+
buildType: FrameworkBuildType,
|
|
197
|
+
): number {
|
|
198
|
+
const port = DEFAULT_HTTP_PORT_BY_BUILD_TYPE[buildType];
|
|
199
|
+
if (port === undefined) {
|
|
200
|
+
throw new Error(`Unknown framework build type "${buildType}".`);
|
|
201
|
+
}
|
|
202
|
+
return port;
|
|
203
|
+
}
|
|
204
|
+
|
|
167
205
|
export function frameworkByKey(key: ComputeFramework): FrameworkDescriptor {
|
|
168
206
|
const framework = FRAMEWORKS.find((candidate) => candidate.key === key);
|
|
169
207
|
if (!framework) {
|
package/src/config/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -14,8 +14,26 @@ export type {
|
|
|
14
14
|
export { applyMigrations } from "./apply-migrations.ts";
|
|
15
15
|
export { createArchive } from "./archive.ts";
|
|
16
16
|
export { normalizeArtifactSymlinks } from "./artifact-postprocess.ts";
|
|
17
|
+
export {
|
|
18
|
+
hoistIsolatedStoreDependencies,
|
|
19
|
+
stageStandaloneArtifact,
|
|
20
|
+
} from "./artifact-stage.ts";
|
|
17
21
|
export { AstroBuild } from "./astro-build.ts";
|
|
18
22
|
export { AutoBuild } from "./auto-build.ts";
|
|
23
|
+
export {
|
|
24
|
+
type BuildStrategyHooks,
|
|
25
|
+
type BuildType,
|
|
26
|
+
createBuildStrategy,
|
|
27
|
+
resolveBuildStrategy,
|
|
28
|
+
} from "./build.ts";
|
|
29
|
+
export {
|
|
30
|
+
type BuildSettings,
|
|
31
|
+
joinPosix,
|
|
32
|
+
nextOutputRootFromStandaloneDirectory,
|
|
33
|
+
readStaticNextConfig,
|
|
34
|
+
resolveBuildSettings,
|
|
35
|
+
resolveConfiguredBuildSettings,
|
|
36
|
+
} from "./build-settings.ts";
|
|
19
37
|
export type { BuildArtifact, BuildStrategy } from "./build-strategy.ts";
|
|
20
38
|
export { PreBuilt } from "./build-strategy.ts";
|
|
21
39
|
export { BunBuild } from "./bun-build.ts";
|
|
@@ -111,3 +129,13 @@ export type {
|
|
|
111
129
|
} from "./types.ts";
|
|
112
130
|
export { KNOWN_REGION_IDS, REGIONS } from "./types.ts";
|
|
113
131
|
export { uploadArtifact } from "./upload-artifact.ts";
|
|
132
|
+
export {
|
|
133
|
+
type BuildCommandIo,
|
|
134
|
+
buildCommandEnv,
|
|
135
|
+
type PackageManager,
|
|
136
|
+
type PackageManifest,
|
|
137
|
+
readBuildScript,
|
|
138
|
+
readPackageManifest,
|
|
139
|
+
resolvePackageManager,
|
|
140
|
+
runBuildCommand,
|
|
141
|
+
} from "./workspace.ts";
|
package/src/nextjs-build.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { cp, mkdtemp, readdir, rm, stat } from "node:fs/promises";
|
|
1
|
+
import { cp, mkdtemp, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
import
|
|
4
|
+
|
|
5
|
+
import { stageStandaloneArtifact } from "./artifact-stage.ts";
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "./build-
|
|
7
|
+
type BuildSettings,
|
|
8
|
+
nextOutputRootFromStandaloneDirectory,
|
|
9
|
+
resolveBuildSettings,
|
|
10
|
+
} from "./build-settings.ts";
|
|
11
|
+
import type { BuildArtifact, BuildStrategy } from "./build-strategy.ts";
|
|
12
|
+
import { hasPackageDependency, hasRootFile } from "./build-strategy.ts";
|
|
13
|
+
import { defaultHttpPortForBuildType } from "./config/frameworks.ts";
|
|
14
|
+
import { type BuildCommandIo, runBuildCommand } from "./workspace.ts";
|
|
11
15
|
|
|
12
16
|
const NEXT_CONFIG_FILENAMES = [
|
|
13
17
|
"next.config.js",
|
|
@@ -17,14 +21,30 @@ const NEXT_CONFIG_FILENAMES = [
|
|
|
17
21
|
];
|
|
18
22
|
|
|
19
23
|
/**
|
|
20
|
-
* Build strategy for Next.js applications
|
|
21
|
-
*
|
|
24
|
+
* Build strategy for Next.js applications. Runs the resolved build command
|
|
25
|
+
* (the user's `package.json` build script, or `next build`), then stages the
|
|
26
|
+
* `output: "standalone"` tree — materializing workspace symlinks and hoisting
|
|
27
|
+
* isolated-store dependencies. Apps built without standalone output fall back
|
|
28
|
+
* to packaging the full tree and serving with `next start`, unless
|
|
29
|
+
* `requireStandalone` is set (a disk-limited deploy target opts out of that).
|
|
22
30
|
*/
|
|
23
31
|
export class NextjsBuild implements BuildStrategy {
|
|
24
32
|
readonly #appPath: string;
|
|
33
|
+
readonly #buildSettings?: BuildSettings;
|
|
34
|
+
readonly #io?: BuildCommandIo;
|
|
35
|
+
readonly #requireStandalone: boolean;
|
|
25
36
|
|
|
26
|
-
constructor(options: {
|
|
37
|
+
constructor(options: {
|
|
38
|
+
appPath: string;
|
|
39
|
+
buildSettings?: BuildSettings;
|
|
40
|
+
io?: BuildCommandIo;
|
|
41
|
+
/** Fail instead of falling back to the full-tree `next start` artifact. */
|
|
42
|
+
requireStandalone?: boolean;
|
|
43
|
+
}) {
|
|
27
44
|
this.#appPath = options.appPath;
|
|
45
|
+
this.#buildSettings = options.buildSettings;
|
|
46
|
+
this.#io = options.io;
|
|
47
|
+
this.#requireStandalone = options.requireStandalone ?? false;
|
|
28
48
|
}
|
|
29
49
|
|
|
30
50
|
async canBuild(signal?: AbortSignal): Promise<boolean> {
|
|
@@ -36,72 +56,62 @@ export class NextjsBuild implements BuildStrategy {
|
|
|
36
56
|
|
|
37
57
|
async execute(signal?: AbortSignal): Promise<BuildArtifact> {
|
|
38
58
|
signal?.throwIfAborted();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
59
|
+
const settings =
|
|
60
|
+
this.#buildSettings ??
|
|
61
|
+
(await resolveBuildSettings({
|
|
62
|
+
appPath: this.#appPath,
|
|
63
|
+
buildType: "nextjs",
|
|
64
|
+
signal,
|
|
65
|
+
}));
|
|
66
|
+
if (settings.buildCommand) {
|
|
67
|
+
await runBuildCommand({
|
|
68
|
+
appPath: this.#appPath,
|
|
69
|
+
command: settings.buildCommand,
|
|
70
|
+
failurePrefix: "Next.js",
|
|
71
|
+
env: this.#io?.env,
|
|
72
|
+
onOutput: this.#io?.onOutput,
|
|
73
|
+
signal,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
48
76
|
|
|
49
|
-
const standaloneDir = path.join(this.#appPath,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
77
|
+
const standaloneDir = path.join(this.#appPath, settings.outputDirectory);
|
|
78
|
+
if (!(await directoryExists(standaloneDir))) {
|
|
79
|
+
if (this.#requireStandalone) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Next.js build did not produce standalone output at ${settings.outputDirectory}. ` +
|
|
82
|
+
'Set output: "standalone" in your next.config; this deploy target requires it.',
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
// No `output: "standalone"` in next.config: the build succeeded, so
|
|
86
|
+
// package the full tree and serve with `next start`. Bigger artifact,
|
|
87
|
+
// same running app.
|
|
88
|
+
return stageFullTreeFallbackArtifact(this.#appPath, signal);
|
|
55
89
|
}
|
|
56
90
|
|
|
57
91
|
const outDir = await mkdtemp(path.join(os.tmpdir(), "compute-build-"));
|
|
58
|
-
|
|
59
92
|
try {
|
|
60
93
|
const artifactDir = path.join(outDir, "app");
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
94
|
+
await stageStandaloneArtifact({
|
|
95
|
+
standaloneDir,
|
|
96
|
+
artifactDir,
|
|
97
|
+
appPath: this.#appPath,
|
|
98
|
+
signal,
|
|
99
|
+
});
|
|
100
|
+
const entrypoint = await findStandaloneEntrypoint(artifactDir);
|
|
101
|
+
await copyStaticAssets({
|
|
102
|
+
appPath: this.#appPath,
|
|
103
|
+
artifactDir,
|
|
104
|
+
outputRoot: nextOutputRootFromStandaloneDirectory(
|
|
105
|
+
settings.outputDirectory,
|
|
106
|
+
),
|
|
107
|
+
entrypoint,
|
|
108
|
+
signal,
|
|
65
109
|
});
|
|
66
|
-
|
|
67
|
-
// In monorepos, Next preserves the workspace-relative path from the
|
|
68
|
-
// workspace root down to the app, so server.js lands at e.g.
|
|
69
|
-
// .next/standalone/apps/web/server.js, not at the standalone root.
|
|
70
|
-
// The runtime needs the correct entrypoint, and Next expects public/
|
|
71
|
-
// and .next/static/ to be siblings of server.js.
|
|
72
|
-
const serverSubpath = await findServerSubpath(artifactDir);
|
|
73
|
-
const serverDir =
|
|
74
|
-
serverSubpath === undefined
|
|
75
|
-
? artifactDir
|
|
76
|
-
: path.join(artifactDir, serverSubpath);
|
|
77
|
-
|
|
78
|
-
const publicDir = path.join(this.#appPath, "public");
|
|
79
|
-
if (await directoryExists(publicDir)) {
|
|
80
|
-
await cp(publicDir, path.join(serverDir, "public"), {
|
|
81
|
-
recursive: true,
|
|
82
|
-
verbatimSymlinks: true,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const staticDir = path.join(this.#appPath, ".next", "static");
|
|
87
|
-
if (await directoryExists(staticDir)) {
|
|
88
|
-
await cp(staticDir, path.join(serverDir, ".next", "static"), {
|
|
89
|
-
recursive: true,
|
|
90
|
-
verbatimSymlinks: true,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const entrypoint =
|
|
95
|
-
serverSubpath === undefined
|
|
96
|
-
? "server.js"
|
|
97
|
-
: path.posix.join(serverSubpath, "server.js");
|
|
98
|
-
|
|
99
|
-
await normalizeArtifactSymlinks(artifactDir, this.#appPath, signal);
|
|
100
110
|
|
|
101
111
|
return {
|
|
102
112
|
directory: artifactDir,
|
|
103
113
|
entrypoint,
|
|
104
|
-
defaultPortMapping: { http:
|
|
114
|
+
defaultPortMapping: { http: defaultHttpPortForBuildType("nextjs") },
|
|
105
115
|
cleanup: () => rm(outDir, { recursive: true, force: true }),
|
|
106
116
|
};
|
|
107
117
|
} catch (error) {
|
|
@@ -111,55 +121,149 @@ export class NextjsBuild implements BuildStrategy {
|
|
|
111
121
|
}
|
|
112
122
|
}
|
|
113
123
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
124
|
+
const FULL_TREE_ENTRYPOINT = "prisma-next-start.cjs";
|
|
125
|
+
|
|
126
|
+
// Bootstrap for apps built without `output: "standalone"`. Entering through
|
|
127
|
+
// the CLI bin (not `next/dist/server/lib/start-server`) keeps Next in charge
|
|
128
|
+
// of config loading, which is the part that drifts across Next majors.
|
|
129
|
+
const FULL_TREE_START_SOURCE = [
|
|
130
|
+
// The runtime unpacks us at the artifact root, but `next start` resolves
|
|
131
|
+
// `.next` from cwd.
|
|
132
|
+
"process.chdir(__dirname);",
|
|
133
|
+
'process.env.NODE_ENV = "production";',
|
|
134
|
+
'process.argv.push("start", "-p", process.env.PORT ?? "3000");',
|
|
135
|
+
'require("next/dist/bin/next");',
|
|
136
|
+
"",
|
|
137
|
+
].join("\n");
|
|
138
|
+
|
|
139
|
+
async function stageFullTreeFallbackArtifact(
|
|
140
|
+
appPath: string,
|
|
141
|
+
signal?: AbortSignal,
|
|
142
|
+
): Promise<BuildArtifact> {
|
|
143
|
+
const outDir = await mkdtemp(path.join(os.tmpdir(), "compute-build-"));
|
|
144
|
+
try {
|
|
145
|
+
const artifactDir = path.join(outDir, "app");
|
|
146
|
+
// node_modules ships on purpose: without standalone output the artifact
|
|
147
|
+
// must carry the full runtime dependency tree for `next start`.
|
|
148
|
+
signal?.throwIfAborted();
|
|
149
|
+
await cp(appPath, artifactDir, {
|
|
150
|
+
recursive: true,
|
|
151
|
+
// Keep relative symlinks relative (node_modules/.bin/*): the default
|
|
152
|
+
// rewrites them to absolute paths into the source tree, which the
|
|
153
|
+
// archiver rejects as escaping the artifact root.
|
|
154
|
+
verbatimSymlinks: true,
|
|
155
|
+
filter: (source) => !isExcludedFromFullTree(path.basename(source)),
|
|
156
|
+
});
|
|
157
|
+
signal?.throwIfAborted();
|
|
158
|
+
await writeFile(
|
|
159
|
+
path.join(artifactDir, FULL_TREE_ENTRYPOINT),
|
|
160
|
+
FULL_TREE_START_SOURCE,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
directory: artifactDir,
|
|
165
|
+
entrypoint: FULL_TREE_ENTRYPOINT,
|
|
166
|
+
defaultPortMapping: { http: defaultHttpPortForBuildType("nextjs") },
|
|
167
|
+
cleanup: () => rm(outDir, { recursive: true, force: true }),
|
|
168
|
+
};
|
|
169
|
+
} catch (error) {
|
|
170
|
+
await rm(outDir, { recursive: true, force: true });
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Excludes VCS internals and dotenv files (local secrets, superseded by the deploy env). */
|
|
176
|
+
function isExcludedFromFullTree(basename: string): boolean {
|
|
177
|
+
return (
|
|
178
|
+
basename === ".git" || basename === ".env" || basename.startsWith(".env.")
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function copyStaticAssets(options: {
|
|
183
|
+
appPath: string;
|
|
184
|
+
artifactDir: string;
|
|
185
|
+
outputRoot: string;
|
|
186
|
+
entrypoint: string;
|
|
187
|
+
signal?: AbortSignal;
|
|
188
|
+
}): Promise<void> {
|
|
189
|
+
const serverSubpath = serverSubpathOf(options.entrypoint);
|
|
190
|
+
const serverDir = serverSubpath
|
|
191
|
+
? path.join(options.artifactDir, serverSubpath)
|
|
192
|
+
: options.artifactDir;
|
|
193
|
+
|
|
194
|
+
const publicDir = path.join(options.appPath, "public");
|
|
195
|
+
if (await directoryExists(publicDir)) {
|
|
196
|
+
options.signal?.throwIfAborted();
|
|
197
|
+
await cp(publicDir, path.join(serverDir, "public"), {
|
|
198
|
+
recursive: true,
|
|
199
|
+
verbatimSymlinks: true,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const staticDir = path.join(options.appPath, options.outputRoot, "static");
|
|
204
|
+
if (await directoryExists(staticDir)) {
|
|
205
|
+
options.signal?.throwIfAborted();
|
|
206
|
+
await cp(staticDir, path.join(serverDir, options.outputRoot, "static"), {
|
|
207
|
+
recursive: true,
|
|
208
|
+
verbatimSymlinks: true,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
117
211
|
}
|
|
118
212
|
|
|
119
213
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* third-party packages may ship a file named server.js.
|
|
214
|
+
* Locates `server.js` in the staged standalone output. In monorepos Next
|
|
215
|
+
* preserves the workspace-relative path (e.g. `apps/web/server.js`), so the
|
|
216
|
+
* entrypoint is the shallowest match. `node_modules` is skipped because
|
|
217
|
+
* third-party packages may ship their own `server.js`.
|
|
125
218
|
*/
|
|
126
|
-
async function
|
|
127
|
-
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
219
|
+
async function findStandaloneEntrypoint(artifactDir: string): Promise<string> {
|
|
220
|
+
const rootStat = await stat(path.join(artifactDir, "server.js")).catch(
|
|
221
|
+
() => null,
|
|
222
|
+
);
|
|
223
|
+
if (rootStat?.isFile()) {
|
|
224
|
+
return "server.js";
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const candidates: string[] = [];
|
|
228
|
+
await walk(artifactDir);
|
|
229
|
+
candidates.sort(
|
|
230
|
+
(left, right) =>
|
|
231
|
+
left.split("/").length - right.split("/").length ||
|
|
232
|
+
left.localeCompare(right),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const selected = candidates[0];
|
|
236
|
+
if (!selected) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`Next.js standalone output did not contain server.js in ${artifactDir}`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
return selected;
|
|
242
|
+
|
|
243
|
+
async function walk(directory: string): Promise<void> {
|
|
244
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
136
245
|
for (const entry of entries) {
|
|
137
|
-
if (entry.name === "node_modules")
|
|
138
|
-
|
|
246
|
+
if (entry.name === "node_modules") {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const fullPath = path.join(directory, entry.name);
|
|
139
250
|
if (entry.isDirectory()) {
|
|
140
|
-
|
|
141
|
-
relative === undefined
|
|
142
|
-
? entry.name
|
|
143
|
-
: path.posix.join(relative, entry.name);
|
|
144
|
-
await walk(fullPath, nextRelative);
|
|
251
|
+
await walk(fullPath);
|
|
145
252
|
} else if (entry.isFile() && entry.name === "server.js") {
|
|
146
|
-
|
|
253
|
+
candidates.push(
|
|
254
|
+
path.relative(artifactDir, fullPath).split(path.sep).join("/"),
|
|
255
|
+
);
|
|
147
256
|
}
|
|
148
257
|
}
|
|
149
258
|
}
|
|
259
|
+
}
|
|
150
260
|
|
|
151
|
-
|
|
261
|
+
function serverSubpathOf(entrypoint: string): string {
|
|
262
|
+
const dir = path.posix.dirname(entrypoint);
|
|
263
|
+
return dir === "." ? "" : dir;
|
|
264
|
+
}
|
|
152
265
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
`Next.js standalone output has multiple server.js files (${locations}). Cannot determine the application entrypoint.`,
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
if (matches.length === 0) {
|
|
160
|
-
throw new Error(
|
|
161
|
-
"Next.js standalone output is missing server.js. The build may not have completed successfully.",
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
return matches[0];
|
|
266
|
+
async function directoryExists(dirPath: string): Promise<boolean> {
|
|
267
|
+
const s = await stat(dirPath).catch(() => null);
|
|
268
|
+
return s?.isDirectory() ?? false;
|
|
165
269
|
}
|