@vercel/build-utils 2.12.3-canary.19 → 2.12.3-canary.22

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.
@@ -1,5 +1,5 @@
1
1
  import { Lambda } from './lambda';
2
- import type { BuildOptions } from './types';
2
+ import type { BuilderFunctions, BuildOptions } from './types';
3
3
  /**
4
4
  * Convert legacy Runtime to a Plugin.
5
5
  * @param buildRuntime - a legacy build() function from a Runtime
@@ -7,7 +7,13 @@ import type { BuildOptions } from './types';
7
7
  */
8
8
  export declare function convertRuntimeToPlugin(buildRuntime: (options: BuildOptions) => Promise<{
9
9
  output: Lambda;
10
- }>, ext: string): ({ workPath }: {
10
+ }>, ext: string): ({ vercelConfig, workPath, }: {
11
+ vercelConfig: {
12
+ functions?: BuilderFunctions;
13
+ regions?: string[];
14
+ trailingSlash?: boolean;
15
+ cleanUrls?: boolean;
16
+ };
11
17
  workPath: string;
12
18
  }) => Promise<void>;
13
19
  /**
@@ -15,9 +21,55 @@ export declare function convertRuntimeToPlugin(buildRuntime: (options: BuildOpti
15
21
  * property. Otherwise write a new file. This will also read `vercel.json`
16
22
  * and apply relevant `functions` property config.
17
23
  */
18
- export declare function updateFunctionsManifest({ workPath, pages, }: {
24
+ export declare function updateFunctionsManifest({ vercelConfig, workPath, pages, }: {
25
+ vercelConfig: {
26
+ functions?: BuilderFunctions;
27
+ regions?: string[];
28
+ };
19
29
  workPath: string;
20
30
  pages: {
21
31
  [key: string]: any;
22
32
  };
23
33
  }): Promise<void>;
34
+ /**
35
+ * Append routes to the `routes-manifest.json` file.
36
+ * If the file does not exist, it will be created.
37
+ */
38
+ export declare function updateRoutesManifest({ workPath, redirects, rewrites, headers, dynamicRoutes, staticRoutes, }: {
39
+ workPath: string;
40
+ redirects?: {
41
+ source: string;
42
+ destination: string;
43
+ statusCode: number;
44
+ regex: string;
45
+ }[];
46
+ rewrites?: {
47
+ source: string;
48
+ destination: string;
49
+ regex: string;
50
+ }[];
51
+ headers?: {
52
+ source: string;
53
+ headers: {
54
+ key: string;
55
+ value: string;
56
+ }[];
57
+ regex: string;
58
+ }[];
59
+ dynamicRoutes?: {
60
+ page: string;
61
+ regex: string;
62
+ namedRegex?: string;
63
+ routeKeys?: {
64
+ [named: string]: string;
65
+ };
66
+ }[];
67
+ staticRoutes?: {
68
+ page: string;
69
+ regex: string;
70
+ namedRegex?: string;
71
+ routeKeys?: {
72
+ [named: string]: string;
73
+ };
74
+ }[];
75
+ }): Promise<void>;
@@ -3,11 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.updateFunctionsManifest = exports.convertRuntimeToPlugin = void 0;
6
+ exports.updateRoutesManifest = exports.updateFunctionsManifest = exports.convertRuntimeToPlugin = void 0;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const path_1 = require("path");
9
9
  const glob_1 = __importDefault(require("./fs/glob"));
10
10
  const normalize_path_1 = require("./fs/normalize-path");
11
+ const detect_builders_1 = require("./detect-builders");
11
12
  const lambda_1 = require("./lambda");
12
13
  const minimatch_1 = __importDefault(require("minimatch"));
13
14
  /**
@@ -16,13 +17,15 @@ const minimatch_1 = __importDefault(require("minimatch"));
16
17
  * @param ext - the file extension, for example `.py`
17
18
  */
18
19
  function convertRuntimeToPlugin(buildRuntime, ext) {
19
- return async function build({ workPath }) {
20
+ // This `build()` signature should match `plugin.build()` signature in `vercel build`.
21
+ return async function build({ vercelConfig, workPath, }) {
22
+ var _a;
20
23
  const opts = { cwd: workPath };
21
24
  const files = await glob_1.default('**', opts);
22
25
  delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
23
26
  const entrypoints = await glob_1.default(`api/**/*${ext}`, opts);
24
27
  const pages = {};
25
- const { functions = {} } = await readVercelConfig(workPath);
28
+ const { functions = {}, cleanUrls, trailingSlash } = vercelConfig;
26
29
  const traceDir = path_1.join(workPath, '.output', 'runtime-traced-files');
27
30
  await fs_extra_1.default.ensureDir(traceDir);
28
31
  for (const entrypoint of Object.keys(entrypoints)) {
@@ -78,7 +81,54 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
78
81
  await fs_extra_1.default.ensureDir(path_1.dirname(nft));
79
82
  await fs_extra_1.default.writeFile(nft, json);
80
83
  }
81
- await updateFunctionsManifest({ workPath, pages });
84
+ await updateFunctionsManifest({ vercelConfig, workPath, pages });
85
+ const { warnings, errors,
86
+ //defaultRoutes,
87
+ redirectRoutes,
88
+ //rewriteRoutes,
89
+ dynamicRoutesWithKeys,
90
+ // errorRoutes, already handled by pages404
91
+ } = await detect_builders_1.detectBuilders(Object.keys(files), null, {
92
+ tag: 'latest',
93
+ functions: functions,
94
+ projectSettings: undefined,
95
+ featHandleMiss: true,
96
+ cleanUrls,
97
+ trailingSlash,
98
+ });
99
+ if (errors) {
100
+ throw new Error(errors[0].message);
101
+ }
102
+ if (warnings) {
103
+ warnings.forEach(warning => console.warn(warning.message, warning.link));
104
+ }
105
+ const redirects = (_a = redirectRoutes === null || redirectRoutes === void 0 ? void 0 : redirectRoutes.filter(r => r.src && 'headers' in r)) === null || _a === void 0 ? void 0 : _a.map(r => {
106
+ var _a;
107
+ return ({
108
+ source: r.src || '',
109
+ destination: 'headers' in r && ((_a = r.headers) === null || _a === void 0 ? void 0 : _a.Location) ? r.headers.Location : '',
110
+ statusCode: 'status' in r && r.status ? r.status : 307,
111
+ regex: r.src || '',
112
+ });
113
+ });
114
+ const dynamicRoutes = dynamicRoutesWithKeys === null || dynamicRoutesWithKeys === void 0 ? void 0 : dynamicRoutesWithKeys.map(r => {
115
+ const keys = Object.keys(r.routeKeys);
116
+ return {
117
+ page: '/' + r.fileName.slice(0, -ext.length),
118
+ regex: r.regex,
119
+ routeKeys: r.routeKeys,
120
+ namedRegex: r.regex
121
+ .split('([^/]+)')
122
+ .map((str, i) => str + (keys[i] ? `(?<${keys[i]}>[^/]+)` : ''))
123
+ .join(''),
124
+ };
125
+ });
126
+ await updateRoutesManifest({
127
+ workPath,
128
+ redirects,
129
+ rewrites: [],
130
+ dynamicRoutes,
131
+ });
82
132
  };
83
133
  }
84
134
  exports.convertRuntimeToPlugin = convertRuntimeToPlugin;
@@ -104,18 +154,13 @@ async function readJson(filePath) {
104
154
  throw err;
105
155
  }
106
156
  }
107
- async function readVercelConfig(workPath) {
108
- const vercelJsonPath = path_1.join(workPath, 'vercel.json');
109
- return readJson(vercelJsonPath);
110
- }
111
157
  /**
112
158
  * If `.output/functions-manifest.json` exists, append to the pages
113
159
  * property. Otherwise write a new file. This will also read `vercel.json`
114
160
  * and apply relevant `functions` property config.
115
161
  */
116
- async function updateFunctionsManifest({ workPath, pages, }) {
162
+ async function updateFunctionsManifest({ vercelConfig, workPath, pages, }) {
117
163
  const functionsManifestPath = path_1.join(workPath, '.output', 'functions-manifest.json');
118
- const vercelConfig = await readVercelConfig(workPath);
119
164
  const functionsManifest = await readJson(functionsManifestPath);
120
165
  if (!functionsManifest.version)
121
166
  functionsManifest.version = 1;
@@ -136,3 +181,42 @@ async function updateFunctionsManifest({ workPath, pages, }) {
136
181
  await fs_extra_1.default.writeFile(functionsManifestPath, JSON.stringify(functionsManifest));
137
182
  }
138
183
  exports.updateFunctionsManifest = updateFunctionsManifest;
184
+ /**
185
+ * Append routes to the `routes-manifest.json` file.
186
+ * If the file does not exist, it will be created.
187
+ */
188
+ async function updateRoutesManifest({ workPath, redirects, rewrites, headers, dynamicRoutes, staticRoutes, }) {
189
+ const routesManifestPath = path_1.join(workPath, '.output', 'routes-manifest.json');
190
+ const routesManifest = await readJson(routesManifestPath);
191
+ if (!routesManifest.version)
192
+ routesManifest.version = 3;
193
+ if (routesManifest.pages404 === undefined)
194
+ routesManifest.pages404 = true;
195
+ if (redirects) {
196
+ if (!routesManifest.redirects)
197
+ routesManifest.redirects = [];
198
+ routesManifest.redirects.push(...redirects);
199
+ }
200
+ if (rewrites) {
201
+ if (!routesManifest.rewrites)
202
+ routesManifest.rewrites = [];
203
+ routesManifest.rewrites.push(...rewrites);
204
+ }
205
+ if (headers) {
206
+ if (!routesManifest.headers)
207
+ routesManifest.headers = [];
208
+ routesManifest.headers.push(...headers);
209
+ }
210
+ if (dynamicRoutes) {
211
+ if (!routesManifest.dynamicRoutes)
212
+ routesManifest.dynamicRoutes = [];
213
+ routesManifest.dynamicRoutes.push(...dynamicRoutes);
214
+ }
215
+ if (staticRoutes) {
216
+ if (!routesManifest.staticRoutes)
217
+ routesManifest.staticRoutes = [];
218
+ routesManifest.staticRoutes.push(...staticRoutes);
219
+ }
220
+ await fs_extra_1.default.writeFile(routesManifestPath, JSON.stringify(routesManifest));
221
+ }
222
+ exports.updateRoutesManifest = updateRoutesManifest;
@@ -6,6 +6,13 @@ interface ErrorResponse {
6
6
  action?: string;
7
7
  link?: string;
8
8
  }
9
+ interface DynamicRoutesWithKeys {
10
+ fileName: string;
11
+ regex: string;
12
+ routeKeys: {
13
+ [key: string]: string;
14
+ };
15
+ }
9
16
  interface Options {
10
17
  tag?: 'canary' | 'latest' | string;
11
18
  functions?: BuilderFunctions;
@@ -34,5 +41,6 @@ export declare function detectBuilders(files: string[], pkg?: PackageJson | unde
34
41
  redirectRoutes: Route[] | null;
35
42
  rewriteRoutes: Route[] | null;
36
43
  errorRoutes: Route[] | null;
44
+ dynamicRoutesWithKeys: DynamicRoutesWithKeys[] | null;
37
45
  }>;
38
46
  export {};
@@ -66,6 +66,7 @@ async function detectBuilders(files, pkg, options = {}) {
66
66
  redirectRoutes: null,
67
67
  rewriteRoutes: null,
68
68
  errorRoutes: null,
69
+ dynamicRoutesWithKeys: null,
69
70
  };
70
71
  }
71
72
  const sortedFiles = files.sort(sortFiles);
@@ -99,11 +100,12 @@ async function detectBuilders(files, pkg, options = {}) {
99
100
  let fallbackEntrypoint = null;
100
101
  const apiRoutes = [];
101
102
  const dynamicRoutes = [];
103
+ const dynamicRoutesWithKeys = [];
102
104
  // API
103
105
  for (const fileName of sortedFiles) {
104
106
  const apiBuilder = maybeGetApiBuilder(fileName, apiMatches, options);
105
107
  if (apiBuilder) {
106
- const { routeError, apiRoute, isDynamic } = getApiRoute(fileName, apiSortedFiles, options, absolutePathCache);
108
+ const { routeError, apiRoute, isDynamic, routeKeys } = getApiRoute(fileName, apiSortedFiles, options, absolutePathCache);
107
109
  if (routeError) {
108
110
  return {
109
111
  builders: null,
@@ -113,12 +115,18 @@ async function detectBuilders(files, pkg, options = {}) {
113
115
  redirectRoutes: null,
114
116
  rewriteRoutes: null,
115
117
  errorRoutes: null,
118
+ dynamicRoutesWithKeys: null,
116
119
  };
117
120
  }
118
121
  if (apiRoute) {
119
122
  apiRoutes.push(apiRoute);
120
123
  if (isDynamic) {
121
124
  dynamicRoutes.push(apiRoute);
125
+ dynamicRoutesWithKeys.push({
126
+ fileName,
127
+ regex: apiRoute.src,
128
+ routeKeys,
129
+ });
122
130
  }
123
131
  }
124
132
  addToUsedFunctions(apiBuilder);
@@ -167,6 +175,7 @@ async function detectBuilders(files, pkg, options = {}) {
167
175
  defaultRoutes: null,
168
176
  rewriteRoutes: null,
169
177
  errorRoutes: null,
178
+ dynamicRoutesWithKeys: null,
170
179
  };
171
180
  }
172
181
  // If `outputDirectory` is an empty string,
@@ -203,6 +212,7 @@ async function detectBuilders(files, pkg, options = {}) {
203
212
  defaultRoutes: null,
204
213
  rewriteRoutes: null,
205
214
  errorRoutes: null,
215
+ dynamicRoutesWithKeys: null,
206
216
  };
207
217
  }
208
218
  const builders = [];
@@ -230,6 +240,7 @@ async function detectBuilders(files, pkg, options = {}) {
230
240
  defaultRoutes: routesResult.defaultRoutes,
231
241
  rewriteRoutes: routesResult.rewriteRoutes,
232
242
  errorRoutes: routesResult.errorRoutes,
243
+ dynamicRoutesWithKeys,
233
244
  };
234
245
  }
235
246
  exports.detectBuilders = detectBuilders;
@@ -484,6 +495,7 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
484
495
  return {
485
496
  apiRoute: null,
486
497
  isDynamic: false,
498
+ routeKeys: {},
487
499
  routeError: {
488
500
  code: 'conflicting_path_segment',
489
501
  message: `The segment "${conflictingSegment}" occurs more than ` +
@@ -498,6 +510,7 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
498
510
  return {
499
511
  apiRoute: null,
500
512
  isDynamic: false,
513
+ routeKeys: {},
501
514
  routeError: {
502
515
  code: 'conflicting_file_path',
503
516
  message: `Two or more files have conflicting paths or names. ` +
@@ -510,6 +523,7 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
510
523
  return {
511
524
  apiRoute: out.route,
512
525
  isDynamic: out.isDynamic,
526
+ routeKeys: out.routeKeys,
513
527
  routeError: null,
514
528
  };
515
529
  }
@@ -619,6 +633,7 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
619
633
  const parts = filePath.split('/');
620
634
  let counter = 1;
621
635
  const query = [];
636
+ const routeKeys = {};
622
637
  let isDynamic = false;
623
638
  const srcParts = parts.map((segment, i) => {
624
639
  const name = getSegmentName(segment);
@@ -626,6 +641,7 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
626
641
  if (name !== null) {
627
642
  // We can't use `URLSearchParams` because `$` would get escaped
628
643
  query.push(`${name}=$${counter++}`);
644
+ routeKeys[name] = name;
629
645
  isDynamic = true;
630
646
  return `([^/]+)`;
631
647
  }
@@ -668,7 +684,7 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
668
684
  dest: `/${filePath}${queryString}`,
669
685
  };
670
686
  }
671
- return { route, isDynamic };
687
+ return { route, isDynamic, routeKeys };
672
688
  }
673
689
  function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders, frontendBuilder, options) {
674
690
  var _a, _b;
package/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@ export { detectFramework } from './detect-framework';
18
18
  export { DetectorFilesystem } from './detectors/filesystem';
19
19
  export { readConfigFile } from './fs/read-config-file';
20
20
  export { normalizePath } from './fs/normalize-path';
21
- export { convertRuntimeToPlugin, updateFunctionsManifest, } from './convert-runtime-to-plugin';
21
+ export { convertRuntimeToPlugin, updateFunctionsManifest, updateRoutesManifest, } from './convert-runtime-to-plugin';
22
22
  export * from './schemas';
23
23
  export * from './types';
24
24
  export * from './errors';
package/dist/index.js CHANGED
@@ -26195,7 +26195,7 @@ exports.frameworks = [
26195
26195
  tagline: 'Gatsby helps developers build blazing fast websites and apps with React.',
26196
26196
  description: 'A Gatsby app, using the default starter theme and a Serverless Function API.',
26197
26197
  website: 'https://gatsbyjs.org',
26198
- sort: 2,
26198
+ sort: 5,
26199
26199
  envPrefix: 'GATSBY_',
26200
26200
  detectors: {
26201
26201
  every: [
@@ -26270,6 +26270,73 @@ exports.frameworks = [
26270
26270
  },
26271
26271
  cachePattern: '{.cache,public}/**',
26272
26272
  },
26273
+ {
26274
+ name: 'Remix',
26275
+ slug: 'remix',
26276
+ demo: 'https://remix.examples.vercel.com',
26277
+ logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/remix.svg',
26278
+ tagline: 'Build Better Websites',
26279
+ description: 'A new Remix app — the result of running `npx create-remix`.',
26280
+ website: 'https://remix.run',
26281
+ sort: 6,
26282
+ detectors: {
26283
+ every: [
26284
+ {
26285
+ path: 'package.json',
26286
+ matchContent: '"(dev)?(d|D)ependencies":\\s*{[^}]*"remix":\\s*".+?"[^}]*}',
26287
+ },
26288
+ ],
26289
+ },
26290
+ settings: {
26291
+ installCommand: {
26292
+ placeholder: '`yarn install` or `npm install`',
26293
+ },
26294
+ buildCommand: {
26295
+ value: 'remix build',
26296
+ placeholder: '`npm run build` or `remix build`',
26297
+ },
26298
+ devCommand: {
26299
+ value: 'remix dev',
26300
+ placeholder: 'remix dev',
26301
+ },
26302
+ outputDirectory: {
26303
+ value: 'public',
26304
+ },
26305
+ },
26306
+ dependency: 'remix',
26307
+ getFsOutputDir: async () => 'public',
26308
+ getOutputDirName: async () => 'public',
26309
+ defaultRoutes: [
26310
+ {
26311
+ src: '^/build/(.*)$',
26312
+ headers: { 'cache-control': 'public, max-age=31536000, immutable' },
26313
+ continue: true,
26314
+ },
26315
+ {
26316
+ handle: 'filesystem',
26317
+ },
26318
+ {
26319
+ src: '/(.*)',
26320
+ dest: '/api',
26321
+ },
26322
+ ],
26323
+ defaultRewrites: [
26324
+ {
26325
+ source: '/(.*)',
26326
+ regex: '/(.*)',
26327
+ destination: '/api',
26328
+ },
26329
+ ],
26330
+ defaultHeaders: [
26331
+ {
26332
+ source: '^/build/(.*)$',
26333
+ regex: '^/build/(.*)$',
26334
+ headers: [
26335
+ { key: 'cache-control', value: 'public, max-age=31536000, immutable' },
26336
+ ],
26337
+ },
26338
+ ]
26339
+ },
26273
26340
  {
26274
26341
  name: 'Hexo',
26275
26342
  slug: 'hexo',
@@ -26278,7 +26345,6 @@ exports.frameworks = [
26278
26345
  tagline: 'Hexo is a fast, simple & powerful blog framework powered by Node.js.',
26279
26346
  description: 'A Hexo site, created with the Hexo CLI.',
26280
26347
  website: 'https://hexo.io',
26281
- sort: 3,
26282
26348
  detectors: {
26283
26349
  every: [
26284
26350
  {
@@ -26315,7 +26381,6 @@ exports.frameworks = [
26315
26381
  tagline: '11ty is a simpler static site generator written in JavaScript, created to be an alternative to Jekyll.',
26316
26382
  description: 'An Eleventy site, created with npm init.',
26317
26383
  website: 'https://www.11ty.dev',
26318
- sort: 4,
26319
26384
  detectors: {
26320
26385
  every: [
26321
26386
  {
@@ -27027,6 +27092,7 @@ exports.frameworks = [
27027
27092
  tagline: 'Svelte lets you write high performance reactive apps with significantly less boilerplate.',
27028
27093
  description: 'A basic Svelte app using the default template.',
27029
27094
  website: 'https://svelte.dev',
27095
+ sort: 3,
27030
27096
  detectors: {
27031
27097
  every: [
27032
27098
  {
@@ -27216,6 +27282,7 @@ exports.frameworks = [
27216
27282
  tagline: 'Create React App allows you to get going with React in no time.',
27217
27283
  description: 'A React app, bootstrapped with create-react-app, and a Serverless Function API.',
27218
27284
  website: 'https://create-react-app.dev',
27285
+ sort: 4,
27219
27286
  envPrefix: 'REACT_APP_',
27220
27287
  detectors: {
27221
27288
  some: [
@@ -27597,6 +27664,7 @@ exports.frameworks = [
27597
27664
  tagline: 'Nuxt.js is the web comprehensive framework that lets you dream big with Vue.js.',
27598
27665
  description: 'A Nuxt.js app, bootstrapped with create-nuxt-app.',
27599
27666
  website: 'https://nuxtjs.org',
27667
+ sort: 2,
27600
27668
  envPrefix: 'NUXT_ENV_',
27601
27669
  detectors: {
27602
27670
  every: [
@@ -27690,7 +27758,6 @@ exports.frameworks = [
27690
27758
  tagline: 'Hugo is the world’s fastest framework for building websites, written in Go.',
27691
27759
  description: 'A Hugo site, created with the Hugo CLI.',
27692
27760
  website: 'https://gohugo.io',
27693
- sort: 5,
27694
27761
  detectors: {
27695
27762
  some: [
27696
27763
  {
@@ -32213,11 +32280,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
32213
32280
  return (mod && mod.__esModule) ? mod : { "default": mod };
32214
32281
  };
32215
32282
  Object.defineProperty(exports, "__esModule", ({ value: true }));
32216
- exports.updateFunctionsManifest = exports.convertRuntimeToPlugin = void 0;
32283
+ exports.updateRoutesManifest = exports.updateFunctionsManifest = exports.convertRuntimeToPlugin = void 0;
32217
32284
  const fs_extra_1 = __importDefault(__webpack_require__(5392));
32218
32285
  const path_1 = __webpack_require__(5622);
32219
32286
  const glob_1 = __importDefault(__webpack_require__(4240));
32220
32287
  const normalize_path_1 = __webpack_require__(6261);
32288
+ const detect_builders_1 = __webpack_require__(4246);
32221
32289
  const lambda_1 = __webpack_require__(6721);
32222
32290
  const minimatch_1 = __importDefault(__webpack_require__(9566));
32223
32291
  /**
@@ -32226,13 +32294,15 @@ const minimatch_1 = __importDefault(__webpack_require__(9566));
32226
32294
  * @param ext - the file extension, for example `.py`
32227
32295
  */
32228
32296
  function convertRuntimeToPlugin(buildRuntime, ext) {
32229
- return async function build({ workPath }) {
32297
+ // This `build()` signature should match `plugin.build()` signature in `vercel build`.
32298
+ return async function build({ vercelConfig, workPath, }) {
32299
+ var _a;
32230
32300
  const opts = { cwd: workPath };
32231
32301
  const files = await glob_1.default('**', opts);
32232
32302
  delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
32233
32303
  const entrypoints = await glob_1.default(`api/**/*${ext}`, opts);
32234
32304
  const pages = {};
32235
- const { functions = {} } = await readVercelConfig(workPath);
32305
+ const { functions = {}, cleanUrls, trailingSlash } = vercelConfig;
32236
32306
  const traceDir = path_1.join(workPath, '.output', 'runtime-traced-files');
32237
32307
  await fs_extra_1.default.ensureDir(traceDir);
32238
32308
  for (const entrypoint of Object.keys(entrypoints)) {
@@ -32288,7 +32358,54 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
32288
32358
  await fs_extra_1.default.ensureDir(path_1.dirname(nft));
32289
32359
  await fs_extra_1.default.writeFile(nft, json);
32290
32360
  }
32291
- await updateFunctionsManifest({ workPath, pages });
32361
+ await updateFunctionsManifest({ vercelConfig, workPath, pages });
32362
+ const { warnings, errors,
32363
+ //defaultRoutes,
32364
+ redirectRoutes,
32365
+ //rewriteRoutes,
32366
+ dynamicRoutesWithKeys,
32367
+ // errorRoutes, already handled by pages404
32368
+ } = await detect_builders_1.detectBuilders(Object.keys(files), null, {
32369
+ tag: 'latest',
32370
+ functions: functions,
32371
+ projectSettings: undefined,
32372
+ featHandleMiss: true,
32373
+ cleanUrls,
32374
+ trailingSlash,
32375
+ });
32376
+ if (errors) {
32377
+ throw new Error(errors[0].message);
32378
+ }
32379
+ if (warnings) {
32380
+ warnings.forEach(warning => console.warn(warning.message, warning.link));
32381
+ }
32382
+ const redirects = (_a = redirectRoutes === null || redirectRoutes === void 0 ? void 0 : redirectRoutes.filter(r => r.src && 'headers' in r)) === null || _a === void 0 ? void 0 : _a.map(r => {
32383
+ var _a;
32384
+ return ({
32385
+ source: r.src || '',
32386
+ destination: 'headers' in r && ((_a = r.headers) === null || _a === void 0 ? void 0 : _a.Location) ? r.headers.Location : '',
32387
+ statusCode: 'status' in r && r.status ? r.status : 307,
32388
+ regex: r.src || '',
32389
+ });
32390
+ });
32391
+ const dynamicRoutes = dynamicRoutesWithKeys === null || dynamicRoutesWithKeys === void 0 ? void 0 : dynamicRoutesWithKeys.map(r => {
32392
+ const keys = Object.keys(r.routeKeys);
32393
+ return {
32394
+ page: '/' + r.fileName.slice(0, -ext.length),
32395
+ regex: r.regex,
32396
+ routeKeys: r.routeKeys,
32397
+ namedRegex: r.regex
32398
+ .split('([^/]+)')
32399
+ .map((str, i) => str + (keys[i] ? `(?<${keys[i]}>[^/]+)` : ''))
32400
+ .join(''),
32401
+ };
32402
+ });
32403
+ await updateRoutesManifest({
32404
+ workPath,
32405
+ redirects,
32406
+ rewrites: [],
32407
+ dynamicRoutes,
32408
+ });
32292
32409
  };
32293
32410
  }
32294
32411
  exports.convertRuntimeToPlugin = convertRuntimeToPlugin;
@@ -32314,18 +32431,13 @@ async function readJson(filePath) {
32314
32431
  throw err;
32315
32432
  }
32316
32433
  }
32317
- async function readVercelConfig(workPath) {
32318
- const vercelJsonPath = path_1.join(workPath, 'vercel.json');
32319
- return readJson(vercelJsonPath);
32320
- }
32321
32434
  /**
32322
32435
  * If `.output/functions-manifest.json` exists, append to the pages
32323
32436
  * property. Otherwise write a new file. This will also read `vercel.json`
32324
32437
  * and apply relevant `functions` property config.
32325
32438
  */
32326
- async function updateFunctionsManifest({ workPath, pages, }) {
32439
+ async function updateFunctionsManifest({ vercelConfig, workPath, pages, }) {
32327
32440
  const functionsManifestPath = path_1.join(workPath, '.output', 'functions-manifest.json');
32328
- const vercelConfig = await readVercelConfig(workPath);
32329
32441
  const functionsManifest = await readJson(functionsManifestPath);
32330
32442
  if (!functionsManifest.version)
32331
32443
  functionsManifest.version = 1;
@@ -32346,6 +32458,45 @@ async function updateFunctionsManifest({ workPath, pages, }) {
32346
32458
  await fs_extra_1.default.writeFile(functionsManifestPath, JSON.stringify(functionsManifest));
32347
32459
  }
32348
32460
  exports.updateFunctionsManifest = updateFunctionsManifest;
32461
+ /**
32462
+ * Append routes to the `routes-manifest.json` file.
32463
+ * If the file does not exist, it will be created.
32464
+ */
32465
+ async function updateRoutesManifest({ workPath, redirects, rewrites, headers, dynamicRoutes, staticRoutes, }) {
32466
+ const routesManifestPath = path_1.join(workPath, '.output', 'routes-manifest.json');
32467
+ const routesManifest = await readJson(routesManifestPath);
32468
+ if (!routesManifest.version)
32469
+ routesManifest.version = 3;
32470
+ if (routesManifest.pages404 === undefined)
32471
+ routesManifest.pages404 = true;
32472
+ if (redirects) {
32473
+ if (!routesManifest.redirects)
32474
+ routesManifest.redirects = [];
32475
+ routesManifest.redirects.push(...redirects);
32476
+ }
32477
+ if (rewrites) {
32478
+ if (!routesManifest.rewrites)
32479
+ routesManifest.rewrites = [];
32480
+ routesManifest.rewrites.push(...rewrites);
32481
+ }
32482
+ if (headers) {
32483
+ if (!routesManifest.headers)
32484
+ routesManifest.headers = [];
32485
+ routesManifest.headers.push(...headers);
32486
+ }
32487
+ if (dynamicRoutes) {
32488
+ if (!routesManifest.dynamicRoutes)
32489
+ routesManifest.dynamicRoutes = [];
32490
+ routesManifest.dynamicRoutes.push(...dynamicRoutes);
32491
+ }
32492
+ if (staticRoutes) {
32493
+ if (!routesManifest.staticRoutes)
32494
+ routesManifest.staticRoutes = [];
32495
+ routesManifest.staticRoutes.push(...staticRoutes);
32496
+ }
32497
+ await fs_extra_1.default.writeFile(routesManifestPath, JSON.stringify(routesManifest));
32498
+ }
32499
+ exports.updateRoutesManifest = updateRoutesManifest;
32349
32500
 
32350
32501
 
32351
32502
  /***/ }),
@@ -32439,6 +32590,7 @@ async function detectBuilders(files, pkg, options = {}) {
32439
32590
  redirectRoutes: null,
32440
32591
  rewriteRoutes: null,
32441
32592
  errorRoutes: null,
32593
+ dynamicRoutesWithKeys: null,
32442
32594
  };
32443
32595
  }
32444
32596
  const sortedFiles = files.sort(sortFiles);
@@ -32472,11 +32624,12 @@ async function detectBuilders(files, pkg, options = {}) {
32472
32624
  let fallbackEntrypoint = null;
32473
32625
  const apiRoutes = [];
32474
32626
  const dynamicRoutes = [];
32627
+ const dynamicRoutesWithKeys = [];
32475
32628
  // API
32476
32629
  for (const fileName of sortedFiles) {
32477
32630
  const apiBuilder = maybeGetApiBuilder(fileName, apiMatches, options);
32478
32631
  if (apiBuilder) {
32479
- const { routeError, apiRoute, isDynamic } = getApiRoute(fileName, apiSortedFiles, options, absolutePathCache);
32632
+ const { routeError, apiRoute, isDynamic, routeKeys } = getApiRoute(fileName, apiSortedFiles, options, absolutePathCache);
32480
32633
  if (routeError) {
32481
32634
  return {
32482
32635
  builders: null,
@@ -32486,12 +32639,18 @@ async function detectBuilders(files, pkg, options = {}) {
32486
32639
  redirectRoutes: null,
32487
32640
  rewriteRoutes: null,
32488
32641
  errorRoutes: null,
32642
+ dynamicRoutesWithKeys: null,
32489
32643
  };
32490
32644
  }
32491
32645
  if (apiRoute) {
32492
32646
  apiRoutes.push(apiRoute);
32493
32647
  if (isDynamic) {
32494
32648
  dynamicRoutes.push(apiRoute);
32649
+ dynamicRoutesWithKeys.push({
32650
+ fileName,
32651
+ regex: apiRoute.src,
32652
+ routeKeys,
32653
+ });
32495
32654
  }
32496
32655
  }
32497
32656
  addToUsedFunctions(apiBuilder);
@@ -32540,6 +32699,7 @@ async function detectBuilders(files, pkg, options = {}) {
32540
32699
  defaultRoutes: null,
32541
32700
  rewriteRoutes: null,
32542
32701
  errorRoutes: null,
32702
+ dynamicRoutesWithKeys: null,
32543
32703
  };
32544
32704
  }
32545
32705
  // If `outputDirectory` is an empty string,
@@ -32576,6 +32736,7 @@ async function detectBuilders(files, pkg, options = {}) {
32576
32736
  defaultRoutes: null,
32577
32737
  rewriteRoutes: null,
32578
32738
  errorRoutes: null,
32739
+ dynamicRoutesWithKeys: null,
32579
32740
  };
32580
32741
  }
32581
32742
  const builders = [];
@@ -32603,6 +32764,7 @@ async function detectBuilders(files, pkg, options = {}) {
32603
32764
  defaultRoutes: routesResult.defaultRoutes,
32604
32765
  rewriteRoutes: routesResult.rewriteRoutes,
32605
32766
  errorRoutes: routesResult.errorRoutes,
32767
+ dynamicRoutesWithKeys,
32606
32768
  };
32607
32769
  }
32608
32770
  exports.detectBuilders = detectBuilders;
@@ -32857,6 +33019,7 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
32857
33019
  return {
32858
33020
  apiRoute: null,
32859
33021
  isDynamic: false,
33022
+ routeKeys: {},
32860
33023
  routeError: {
32861
33024
  code: 'conflicting_path_segment',
32862
33025
  message: `The segment "${conflictingSegment}" occurs more than ` +
@@ -32871,6 +33034,7 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
32871
33034
  return {
32872
33035
  apiRoute: null,
32873
33036
  isDynamic: false,
33037
+ routeKeys: {},
32874
33038
  routeError: {
32875
33039
  code: 'conflicting_file_path',
32876
33040
  message: `Two or more files have conflicting paths or names. ` +
@@ -32883,6 +33047,7 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
32883
33047
  return {
32884
33048
  apiRoute: out.route,
32885
33049
  isDynamic: out.isDynamic,
33050
+ routeKeys: out.routeKeys,
32886
33051
  routeError: null,
32887
33052
  };
32888
33053
  }
@@ -32992,6 +33157,7 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
32992
33157
  const parts = filePath.split('/');
32993
33158
  let counter = 1;
32994
33159
  const query = [];
33160
+ const routeKeys = {};
32995
33161
  let isDynamic = false;
32996
33162
  const srcParts = parts.map((segment, i) => {
32997
33163
  const name = getSegmentName(segment);
@@ -32999,6 +33165,7 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
32999
33165
  if (name !== null) {
33000
33166
  // We can't use `URLSearchParams` because `$` would get escaped
33001
33167
  query.push(`${name}=$${counter++}`);
33168
+ routeKeys[name] = name;
33002
33169
  isDynamic = true;
33003
33170
  return `([^/]+)`;
33004
33171
  }
@@ -33041,7 +33208,7 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
33041
33208
  dest: `/${filePath}${queryString}`,
33042
33209
  };
33043
33210
  }
33044
- return { route, isDynamic };
33211
+ return { route, isDynamic, routeKeys };
33045
33212
  }
33046
33213
  function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders, frontendBuilder, options) {
33047
33214
  var _a, _b;
@@ -34318,7 +34485,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
34318
34485
  return (mod && mod.__esModule) ? mod : { "default": mod };
34319
34486
  };
34320
34487
  Object.defineProperty(exports, "__esModule", ({ value: true }));
34321
- exports.getPlatformEnv = exports.isStaticRuntime = exports.isOfficialRuntime = exports.updateFunctionsManifest = exports.convertRuntimeToPlugin = exports.normalizePath = exports.readConfigFile = exports.DetectorFilesystem = exports.detectFramework = exports.detectApiExtensions = exports.detectApiDirectory = exports.detectOutputDirectory = exports.detectBuilders = exports.scanParentDirs = exports.getLambdaOptionsFromFunction = exports.isSymbolicLink = exports.debug = exports.shouldServe = exports.streamToBuffer = exports.getSpawnOptions = exports.getDiscontinuedNodeVersions = exports.getLatestNodeVersion = exports.getNodeVersion = exports.runShellScript = exports.runPipInstall = exports.runBundleInstall = exports.runNpmInstall = exports.getNodeBinPath = exports.walkParentDirs = exports.spawnCommand = exports.execCommand = exports.runPackageJsonScript = exports.installDependencies = exports.getScriptName = exports.spawnAsync = exports.execAsync = exports.rename = exports.glob = exports.getWriteableDirectory = exports.download = exports.Prerender = exports.createLambda = exports.Lambda = exports.FileRef = exports.FileFsRef = exports.FileBlob = void 0;
34488
+ exports.getPlatformEnv = exports.isStaticRuntime = exports.isOfficialRuntime = exports.updateRoutesManifest = exports.updateFunctionsManifest = exports.convertRuntimeToPlugin = exports.normalizePath = exports.readConfigFile = exports.DetectorFilesystem = exports.detectFramework = exports.detectApiExtensions = exports.detectApiDirectory = exports.detectOutputDirectory = exports.detectBuilders = exports.scanParentDirs = exports.getLambdaOptionsFromFunction = exports.isSymbolicLink = exports.debug = exports.shouldServe = exports.streamToBuffer = exports.getSpawnOptions = exports.getDiscontinuedNodeVersions = exports.getLatestNodeVersion = exports.getNodeVersion = exports.runShellScript = exports.runPipInstall = exports.runBundleInstall = exports.runNpmInstall = exports.getNodeBinPath = exports.walkParentDirs = exports.spawnCommand = exports.execCommand = exports.runPackageJsonScript = exports.installDependencies = exports.getScriptName = exports.spawnAsync = exports.execAsync = exports.rename = exports.glob = exports.getWriteableDirectory = exports.download = exports.Prerender = exports.createLambda = exports.Lambda = exports.FileRef = exports.FileFsRef = exports.FileBlob = void 0;
34322
34489
  const file_blob_1 = __importDefault(__webpack_require__(2397));
34323
34490
  exports.FileBlob = file_blob_1.default;
34324
34491
  const file_fs_ref_1 = __importDefault(__webpack_require__(9331));
@@ -34383,6 +34550,7 @@ Object.defineProperty(exports, "normalizePath", ({ enumerable: true, get: functi
34383
34550
  var convert_runtime_to_plugin_1 = __webpack_require__(7276);
34384
34551
  Object.defineProperty(exports, "convertRuntimeToPlugin", ({ enumerable: true, get: function () { return convert_runtime_to_plugin_1.convertRuntimeToPlugin; } }));
34385
34552
  Object.defineProperty(exports, "updateFunctionsManifest", ({ enumerable: true, get: function () { return convert_runtime_to_plugin_1.updateFunctionsManifest; } }));
34553
+ Object.defineProperty(exports, "updateRoutesManifest", ({ enumerable: true, get: function () { return convert_runtime_to_plugin_1.updateRoutesManifest; } }));
34386
34554
  __exportStar(__webpack_require__(2416), exports);
34387
34555
  __exportStar(__webpack_require__(5748), exports);
34388
34556
  __exportStar(__webpack_require__(3983), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/build-utils",
3
- "version": "2.12.3-canary.19",
3
+ "version": "2.12.3-canary.22",
4
4
  "license": "MIT",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",
@@ -30,7 +30,7 @@
30
30
  "@types/node-fetch": "^2.1.6",
31
31
  "@types/semver": "6.0.0",
32
32
  "@types/yazl": "^2.4.1",
33
- "@vercel/frameworks": "0.5.1-canary.12",
33
+ "@vercel/frameworks": "0.5.1-canary.13",
34
34
  "@vercel/ncc": "0.24.0",
35
35
  "aggregate-error": "3.0.1",
36
36
  "async-retry": "1.2.3",
@@ -49,5 +49,5 @@
49
49
  "typescript": "4.3.4",
50
50
  "yazl": "2.4.3"
51
51
  },
52
- "gitHead": "32664cd13b5d7a771d465a091bff2e966eed2f94"
52
+ "gitHead": "0cacb1bdace342133fad4bd7a98354e5b2948df0"
53
53
  }