@vercel/build-utils 13.19.0 → 13.20.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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @vercel/build-utils
2
2
 
3
+ ## 13.20.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add framework to package manifest for python and backends builders. ([#16072](https://github.com/vercel/vercel/pull/16072))
8
+
9
+ ### Patch Changes
10
+
11
+ - Add shared build output deserialization helpers for existing callers. ([#16072](https://github.com/vercel/vercel/pull/16072))
12
+
13
+ ## 13.19.1
14
+
15
+ ### Patch Changes
16
+
17
+ - [build-utils] simplify streaming lambda check ([#15795](https://github.com/vercel/vercel/pull/15795))
18
+
3
19
  ## 13.19.0
4
20
 
5
21
  ### Minor Changes
@@ -0,0 +1,57 @@
1
+ import type FileFsRef from '../file-fs-ref';
2
+ import type { Lambda } from '../lambda';
3
+ import type { Prerender } from '../prerender';
4
+ import type { BuildResultV2Typical, Files } from '../types';
5
+ import type { SerializedEdgeFunction, SerializedLambda, SerializedNodejsLambda, SerializedPrerender } from './serialized-types';
6
+ export interface DeserializeBuildOutputPathOverride {
7
+ contentType?: string;
8
+ mode?: number;
9
+ path?: string;
10
+ }
11
+ export interface DeserializeBuildOutputConfig<TFlags = unknown> {
12
+ version?: 3;
13
+ wildcard?: BuildResultV2Typical['wildcard'];
14
+ images?: BuildResultV2Typical['images'];
15
+ routes?: BuildResultV2Typical['routes'];
16
+ overrides?: Record<string, DeserializeBuildOutputPathOverride>;
17
+ framework?: BuildResultV2Typical['framework'];
18
+ crons?: BuildResultV2Typical['crons'];
19
+ flags?: TFlags;
20
+ deploymentId?: string;
21
+ }
22
+ export type DeserializeBuildOutputResult<TFlags = unknown, TMeta = unknown> = Omit<BuildResultV2Typical, 'flags'> & {
23
+ flags?: TFlags;
24
+ meta?: TMeta;
25
+ };
26
+ export type DeserializeBuildOutputLambdaOptions = {
27
+ forceNodejsStreaming?: boolean;
28
+ useOnlyStreamingLambda?: boolean;
29
+ };
30
+ export type GroupLambdasOptions = {
31
+ force: 'all' | undefined;
32
+ maxBundleSizeMb: number | undefined;
33
+ debug: boolean | undefined;
34
+ };
35
+ export type DeserializeBuildOutputLambda<TLambda extends Lambda> = (files: Files, config: SerializedLambda | SerializedNodejsLambda, repoRootPath: string, fileFsRefsCache: Map<string, FileFsRef>, options?: DeserializeBuildOutputLambdaOptions) => Promise<TLambda>;
36
+ export type GroupLambdas<TLambda extends Lambda> = (lambdas: Record<string, TLambda>, options: GroupLambdasOptions) => Promise<Record<string, TLambda>>;
37
+ export type InspectSerializedLambda = (path: string, config: SerializedLambda | SerializedNodejsLambda, repoRootPath: string, hasServerActions: boolean) => Promise<boolean>;
38
+ export interface DeserializeBuildOutputOptions<TResult extends DeserializeBuildOutputResult = DeserializeBuildOutputResult, TLambda extends Lambda = Lambda> {
39
+ outputDir: string;
40
+ repoRootPath: string;
41
+ maxBundleSizeMb?: number;
42
+ debugGroupLambdas?: boolean;
43
+ useOnlyStreamingLambda?: boolean;
44
+ forceNodejsStreaming?: boolean;
45
+ deserializeLambda: DeserializeBuildOutputLambda<TLambda>;
46
+ groupLambdas: GroupLambdas<TLambda>;
47
+ inspectSerializedLambda?: InspectSerializedLambda;
48
+ warn?: (message: string) => void;
49
+ includeDeploymentId?: boolean;
50
+ getMeta?: (hasServerActions: boolean) => TResult extends {
51
+ meta?: infer TMeta;
52
+ } ? TMeta : never;
53
+ }
54
+ export type DeserializeBuildOutputFiles = BuildResultV2Typical['output'];
55
+ export type DeserializeBuildOutputPrerenderFallback = Prerender['fallback'];
56
+ export type DeserializeBuildOutputSerializedConfig = SerializedEdgeFunction | SerializedLambda | SerializedNodejsLambda;
57
+ export type DeserializeBuildOutputSerializedPrerender = SerializedPrerender;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var deserialize_build_output_types_exports = {};
16
+ module.exports = __toCommonJS(deserialize_build_output_types_exports);
@@ -0,0 +1,4 @@
1
+ import type { Lambda } from '../lambda';
2
+ import type { DeserializeBuildOutputConfig, DeserializeBuildOutputOptions, DeserializeBuildOutputResult } from './deserialize-build-output-types';
3
+ export declare function validateDeploymentId(deploymentId?: string): void;
4
+ export declare function deserializeBuildOutput<TConfig extends DeserializeBuildOutputConfig = DeserializeBuildOutputConfig, TResult extends DeserializeBuildOutputResult = DeserializeBuildOutputResult, TLambda extends Lambda = Lambda>(options: DeserializeBuildOutputOptions<TResult, TLambda>): Promise<TResult>;
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var deserialize_build_output_exports = {};
30
+ __export(deserialize_build_output_exports, {
31
+ deserializeBuildOutput: () => deserializeBuildOutput,
32
+ validateDeploymentId: () => validateDeploymentId
33
+ });
34
+ module.exports = __toCommonJS(deserialize_build_output_exports);
35
+ var fs = __toESM(require("fs-extra"));
36
+ var import_path = require("path");
37
+ var import_errors = require("../errors");
38
+ var import_file_fs_ref = __toESM(require("../file-fs-ref"));
39
+ var import_glob = __toESM(require("../fs/glob"));
40
+ var import_prerender = require("../prerender");
41
+ var import_create_functions_iterator = require("./create-functions-iterator");
42
+ var import_deserialize_edge_function = require("./deserialize-edge-function");
43
+ var import_maybe_read_json = require("./maybe-read-json");
44
+ var import_validate_framework_version = require("./validate-framework-version");
45
+ const MAX_DEPLOYMENT_ID_LENGTH = 32;
46
+ const VALID_DEPLOYMENT_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
47
+ function validateDeploymentId(deploymentId) {
48
+ if (deploymentId && deploymentId.length > MAX_DEPLOYMENT_ID_LENGTH) {
49
+ throw new import_errors.NowBuildError({
50
+ message: `The configured deploymentId "${deploymentId}" exceeds the maximum length of ${MAX_DEPLOYMENT_ID_LENGTH} characters. Please use a shorter deploymentId.`,
51
+ code: "VC_BUILD_INVALID_DEPLOYMENT_ID_LENGTH"
52
+ });
53
+ }
54
+ if (deploymentId && !VALID_DEPLOYMENT_ID_PATTERN.test(deploymentId)) {
55
+ throw new import_errors.NowBuildError({
56
+ message: `The configured deploymentId "${deploymentId}" contains invalid characters. Only alphanumeric characters (a-z, A-Z, 0-9), hyphens (-), and underscores (_) are allowed.`,
57
+ code: "VC_BUILD_INVALID_DEPLOYMENT_ID_CHARACTERS"
58
+ });
59
+ }
60
+ }
61
+ function applyOutputOverrides(output, overrides, warn) {
62
+ for (const [name, override] of Object.entries(overrides || {})) {
63
+ const entry = output[name];
64
+ if (entry) {
65
+ if (override.contentType) {
66
+ entry.contentType = override.contentType;
67
+ }
68
+ if (override.mode) {
69
+ entry.mode = override.mode;
70
+ }
71
+ if (override.path) {
72
+ output[override.path] = entry;
73
+ delete output[name];
74
+ }
75
+ } else {
76
+ warn?.(
77
+ `Warning: Override path "${name}" was not detected as an output path`
78
+ );
79
+ }
80
+ }
81
+ }
82
+ async function deserializePrerenderFallback(prerenderConfigPath, fallbackConfig) {
83
+ if (typeof fallbackConfig === "string") {
84
+ return import_file_fs_ref.default.fromFsPath({
85
+ fsPath: (0, import_path.join)((0, import_path.dirname)(prerenderConfigPath), fallbackConfig)
86
+ });
87
+ }
88
+ if (fallbackConfig) {
89
+ return import_file_fs_ref.default.fromFsPath({
90
+ mode: fallbackConfig.mode,
91
+ contentType: fallbackConfig.contentType,
92
+ fsPath: (0, import_path.join)((0, import_path.dirname)(prerenderConfigPath), fallbackConfig.fsPath)
93
+ });
94
+ }
95
+ return null;
96
+ }
97
+ function applyFunctionSymlinks(output, prerenders, functionSymlinks) {
98
+ for (const [path, target] of functionSymlinks.entries()) {
99
+ const targetOutput = prerenders.get(target) || output[target];
100
+ let targetFunction;
101
+ if (targetOutput?.type === "Prerender") {
102
+ targetFunction = targetOutput.lambda;
103
+ } else if (targetOutput?.type === "Lambda" || targetOutput?.type === "EdgeFunction") {
104
+ targetFunction = targetOutput;
105
+ }
106
+ if (!targetFunction) {
107
+ throw new Error(
108
+ `Could not find target "${target}" Lambda or EdgeFunction for path "${path}"`
109
+ );
110
+ }
111
+ const srcOutput = prerenders.get(path);
112
+ if (srcOutput) {
113
+ if (srcOutput.type === "Prerender") {
114
+ if (targetFunction.type === "Lambda") {
115
+ srcOutput.lambda = targetFunction;
116
+ } else {
117
+ throw new Error(
118
+ `Unexpected function type "${targetFunction.type}" at path "${path}"`
119
+ );
120
+ }
121
+ } else {
122
+ throw new Error(
123
+ `Unexpected output type "${srcOutput.type}" at path "${path}"`
124
+ );
125
+ }
126
+ } else {
127
+ output[path] = targetFunction;
128
+ }
129
+ }
130
+ }
131
+ function appendSortedPrerenders(output, prerenders) {
132
+ const sortedPrerenders = Array.from(prerenders.entries()).sort((a, b) => {
133
+ return (a[1].group ?? 0) - (b[1].group ?? 0);
134
+ }).reduce((o, [path, prerender]) => {
135
+ o[path] = prerender;
136
+ return o;
137
+ }, {});
138
+ Object.assign(output, sortedPrerenders);
139
+ }
140
+ function getBundleableLambdas(output) {
141
+ const bundleableLambdas = {};
142
+ for (const [outputName, curOutput] of Object.entries(output)) {
143
+ if (curOutput.type === "Lambda" && curOutput.experimentalAllowBundling) {
144
+ bundleableLambdas[outputName] = curOutput;
145
+ } else if (curOutput.type === "Prerender" && curOutput.lambda && curOutput.lambda.experimentalAllowBundling) {
146
+ bundleableLambdas[outputName] = curOutput.lambda;
147
+ }
148
+ }
149
+ return bundleableLambdas;
150
+ }
151
+ function applyGroupedLambdas(output, groupedLambdas) {
152
+ for (const outputName of Object.keys(groupedLambdas)) {
153
+ const groupedLambda = groupedLambdas[outputName];
154
+ const origOutput = output[outputName];
155
+ if (origOutput.type === "Lambda") {
156
+ output[outputName] = groupedLambda;
157
+ } else if (origOutput.type === "Prerender" && origOutput.lambda) {
158
+ origOutput.lambda = groupedLambda;
159
+ }
160
+ }
161
+ }
162
+ async function deserializeBuildOutput(options) {
163
+ const {
164
+ outputDir,
165
+ repoRootPath,
166
+ maxBundleSizeMb,
167
+ debugGroupLambdas,
168
+ useOnlyStreamingLambda,
169
+ forceNodejsStreaming,
170
+ deserializeLambda,
171
+ groupLambdas,
172
+ inspectSerializedLambda,
173
+ warn,
174
+ includeDeploymentId,
175
+ getMeta
176
+ } = options;
177
+ let hasServerActions = false;
178
+ const configPath = (0, import_path.join)(outputDir, "config.json");
179
+ const config = await (0, import_maybe_read_json.maybeReadJSON)(configPath);
180
+ if (!config) {
181
+ throw new Error(`Config file was not found at "${configPath}"`);
182
+ }
183
+ if (config.version !== 3) {
184
+ throw new Error(
185
+ `Expected \`version: 3\` in "${configPath}" file (received \`${config.version}\`)`
186
+ );
187
+ }
188
+ validateDeploymentId(config.deploymentId);
189
+ const flags = await (0, import_maybe_read_json.maybeReadJSON)(
190
+ (0, import_path.join)(outputDir, "flags.json")
191
+ );
192
+ const staticDir = (0, import_path.join)(outputDir, "static");
193
+ const output = await (0, import_glob.default)("**", {
194
+ cwd: staticDir,
195
+ follow: true
196
+ });
197
+ applyOutputOverrides(output, config.overrides, warn);
198
+ const fileFsRefsCache = /* @__PURE__ */ new Map();
199
+ const prerenders = /* @__PURE__ */ new Map();
200
+ const functionsDir = (0, import_path.join)(outputDir, "functions");
201
+ const functionSymlinks = /* @__PURE__ */ new Map();
202
+ for await (const path of (0, import_create_functions_iterator.createFunctionsIterator)(functionsDir)) {
203
+ let lambda = void 0;
204
+ const fnDir = (0, import_path.join)(functionsDir, `${path}.func`);
205
+ try {
206
+ const link = await fs.readlink(fnDir);
207
+ const target = (0, import_path.join)((0, import_path.dirname)(path), link).slice(0, -5);
208
+ functionSymlinks.set(path, target);
209
+ } catch (err) {
210
+ if (err.code !== "EINVAL")
211
+ throw err;
212
+ const funcConfigPath = (0, import_path.join)(fnDir, ".vc-config.json");
213
+ const funcConfig = await (0, import_maybe_read_json.maybeReadJSON)(
214
+ funcConfigPath
215
+ );
216
+ if (!funcConfig) {
217
+ throw new Error(`Could not load function config: "${funcConfigPath}"`);
218
+ }
219
+ const files = await (0, import_glob.default)("**", { cwd: fnDir, includeDirectories: true });
220
+ delete files[".vc-config.json"];
221
+ if (funcConfig.type === "EdgeFunction" || funcConfig.runtime === "edge") {
222
+ output[path] = await (0, import_deserialize_edge_function.deserializeEdgeFunction)(
223
+ files,
224
+ funcConfig,
225
+ repoRootPath,
226
+ fileFsRefsCache
227
+ );
228
+ continue;
229
+ }
230
+ lambda = await deserializeLambda(
231
+ files,
232
+ funcConfig,
233
+ repoRootPath,
234
+ fileFsRefsCache,
235
+ { useOnlyStreamingLambda, forceNodejsStreaming }
236
+ );
237
+ if (inspectSerializedLambda) {
238
+ hasServerActions = await inspectSerializedLambda(
239
+ path,
240
+ funcConfig,
241
+ repoRootPath,
242
+ hasServerActions
243
+ );
244
+ }
245
+ }
246
+ const prerenderConfigPath = (0, import_path.join)(
247
+ functionsDir,
248
+ `${path}.prerender-config.json`
249
+ );
250
+ const prerenderConfig = await (0, import_maybe_read_json.maybeReadJSON)(
251
+ prerenderConfigPath
252
+ );
253
+ if (prerenderConfig) {
254
+ const fallback = await deserializePrerenderFallback(
255
+ prerenderConfigPath,
256
+ prerenderConfig.fallback
257
+ );
258
+ const prerender = new import_prerender.Prerender({
259
+ ...prerenderConfig,
260
+ lambda,
261
+ fallback
262
+ });
263
+ prerenders.set(path, prerender);
264
+ } else if (lambda) {
265
+ output[path] = lambda;
266
+ }
267
+ }
268
+ applyFunctionSymlinks(output, prerenders, functionSymlinks);
269
+ appendSortedPrerenders(output, prerenders);
270
+ const groupedLambdas = await groupLambdas(
271
+ getBundleableLambdas(output),
272
+ {
273
+ force: void 0,
274
+ maxBundleSizeMb,
275
+ debug: debugGroupLambdas
276
+ }
277
+ );
278
+ applyGroupedLambdas(output, groupedLambdas);
279
+ const framework = (0, import_validate_framework_version.validateFrameworkVersion)(config?.framework?.version);
280
+ const meta = getMeta?.(hasServerActions);
281
+ return {
282
+ wildcard: config.wildcard,
283
+ images: config.images,
284
+ crons: config.crons,
285
+ flags: flags ? flags : config.flags,
286
+ routes: config.routes,
287
+ output,
288
+ framework,
289
+ ...includeDeploymentId ? { deploymentId: config.deploymentId } : {},
290
+ ...meta !== void 0 ? { meta } : {}
291
+ };
292
+ }
293
+ // Annotate the CommonJS export names for ESM import in node:
294
+ 0 && (module.exports = {
295
+ deserializeBuildOutput,
296
+ validateDeploymentId
297
+ });
package/dist/index.d.ts CHANGED
@@ -57,6 +57,8 @@ export { validateFrameworkVersion } from './deserialize/validate-framework-versi
57
57
  export { hydrateFilesMap } from './deserialize/hydrate-files-map';
58
58
  export { createFunctionsIterator } from './deserialize/create-functions-iterator';
59
59
  export { maybeReadJSON } from './deserialize/maybe-read-json';
60
+ export { deserializeBuildOutput, validateDeploymentId, } from './deserialize/deserialize-build-output';
61
+ export type { DeserializeBuildOutputConfig, DeserializeBuildOutputResult, DeserializeBuildOutputPathOverride, DeserializeBuildOutputOptions, DeserializeBuildOutputLambdaOptions, GroupLambdasOptions, DeserializeBuildOutputSerializedConfig, DeserializeBuildOutputSerializedPrerender, } from './deserialize/deserialize-build-output-types';
60
62
  export { deserializeLambda, type DeserializeLambdaOptions, } from './deserialize/deserialize-lambda';
61
63
  export { deserializeEdgeFunction } from './deserialize/deserialize-edge-function';
62
64
  export type { Properties, SerializedLambda, SerializedNodejsLambda, SerializedEdgeFunction, SerializedFileFsRef, SerializedPrerender, } from './deserialize/serialized-types';