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

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.
@@ -11,8 +11,6 @@ export declare function convertRuntimeToPlugin(buildRuntime: (options: BuildOpti
11
11
  vercelConfig: {
12
12
  functions?: BuilderFunctions;
13
13
  regions?: string[];
14
- trailingSlash?: boolean;
15
- cleanUrls?: boolean;
16
14
  };
17
15
  workPath: string;
18
16
  }) => Promise<void>;
@@ -8,7 +8,6 @@ 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");
12
11
  const lambda_1 = require("./lambda");
13
12
  const minimatch_1 = __importDefault(require("minimatch"));
14
13
  /**
@@ -19,13 +18,12 @@ const minimatch_1 = __importDefault(require("minimatch"));
19
18
  function convertRuntimeToPlugin(buildRuntime, ext) {
20
19
  // This `build()` signature should match `plugin.build()` signature in `vercel build`.
21
20
  return async function build({ vercelConfig, workPath, }) {
22
- var _a;
23
21
  const opts = { cwd: workPath };
24
22
  const files = await glob_1.default('**', opts);
25
23
  delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
26
24
  const entrypoints = await glob_1.default(`api/**/*${ext}`, opts);
27
25
  const pages = {};
28
- const { functions = {}, cleanUrls, trailingSlash } = vercelConfig;
26
+ const { functions = {} } = vercelConfig;
29
27
  const traceDir = path_1.join(workPath, '.output', 'runtime-traced-files');
30
28
  await fs_extra_1.default.ensureDir(traceDir);
31
29
  for (const entrypoint of Object.keys(entrypoints)) {
@@ -40,6 +38,9 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
40
38
  includeFiles: config.includeFiles,
41
39
  excludeFiles: config.excludeFiles,
42
40
  },
41
+ meta: {
42
+ avoidTopLevelInstall: true,
43
+ },
43
44
  });
44
45
  pages[entrypoint] = {
45
46
  handler: output.handler,
@@ -48,7 +49,7 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
48
49
  maxDuration: output.maxDuration,
49
50
  environment: output.environment,
50
51
  allowQuery: output.allowQuery,
51
- regions: output.regions,
52
+ //regions: output.regions,
52
53
  };
53
54
  // @ts-ignore This symbol is a private API
54
55
  const lambdaFiles = output[lambda_1.FILES_SYMBOL];
@@ -82,53 +83,6 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
82
83
  await fs_extra_1.default.writeFile(nft, json);
83
84
  }
84
85
  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
- });
132
86
  };
133
87
  }
134
88
  exports.convertRuntimeToPlugin = convertRuntimeToPlugin;
@@ -6,13 +6,6 @@ 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
- }
16
9
  interface Options {
17
10
  tag?: 'canary' | 'latest' | string;
18
11
  functions?: BuilderFunctions;
@@ -41,6 +34,11 @@ export declare function detectBuilders(files: string[], pkg?: PackageJson | unde
41
34
  redirectRoutes: Route[] | null;
42
35
  rewriteRoutes: Route[] | null;
43
36
  errorRoutes: Route[] | null;
44
- dynamicRoutesWithKeys: DynamicRoutesWithKeys[] | null;
37
+ limitedRoutes: LimitedRoutes | null;
45
38
  }>;
39
+ interface LimitedRoutes {
40
+ defaultRoutes: Route[];
41
+ redirectRoutes: Route[];
42
+ rewriteRoutes: Route[];
43
+ }
46
44
  export {};
@@ -66,7 +66,7 @@ async function detectBuilders(files, pkg, options = {}) {
66
66
  redirectRoutes: null,
67
67
  rewriteRoutes: null,
68
68
  errorRoutes: null,
69
- dynamicRoutesWithKeys: null,
69
+ limitedRoutes: null,
70
70
  };
71
71
  }
72
72
  const sortedFiles = files.sort(sortFiles);
@@ -100,12 +100,11 @@ async function detectBuilders(files, pkg, options = {}) {
100
100
  let fallbackEntrypoint = null;
101
101
  const apiRoutes = [];
102
102
  const dynamicRoutes = [];
103
- const dynamicRoutesWithKeys = [];
104
103
  // API
105
104
  for (const fileName of sortedFiles) {
106
105
  const apiBuilder = maybeGetApiBuilder(fileName, apiMatches, options);
107
106
  if (apiBuilder) {
108
- const { routeError, apiRoute, isDynamic, routeKeys } = getApiRoute(fileName, apiSortedFiles, options, absolutePathCache);
107
+ const { routeError, apiRoute, isDynamic } = getApiRoute(fileName, apiSortedFiles, options, absolutePathCache);
109
108
  if (routeError) {
110
109
  return {
111
110
  builders: null,
@@ -115,18 +114,13 @@ async function detectBuilders(files, pkg, options = {}) {
115
114
  redirectRoutes: null,
116
115
  rewriteRoutes: null,
117
116
  errorRoutes: null,
118
- dynamicRoutesWithKeys: null,
117
+ limitedRoutes: null,
119
118
  };
120
119
  }
121
120
  if (apiRoute) {
122
121
  apiRoutes.push(apiRoute);
123
122
  if (isDynamic) {
124
123
  dynamicRoutes.push(apiRoute);
125
- dynamicRoutesWithKeys.push({
126
- fileName,
127
- regex: apiRoute.src,
128
- routeKeys,
129
- });
130
124
  }
131
125
  }
132
126
  addToUsedFunctions(apiBuilder);
@@ -175,7 +169,7 @@ async function detectBuilders(files, pkg, options = {}) {
175
169
  defaultRoutes: null,
176
170
  rewriteRoutes: null,
177
171
  errorRoutes: null,
178
- dynamicRoutesWithKeys: null,
172
+ limitedRoutes: null,
179
173
  };
180
174
  }
181
175
  // If `outputDirectory` is an empty string,
@@ -212,7 +206,7 @@ async function detectBuilders(files, pkg, options = {}) {
212
206
  defaultRoutes: null,
213
207
  rewriteRoutes: null,
214
208
  errorRoutes: null,
215
- dynamicRoutesWithKeys: null,
209
+ limitedRoutes: null,
216
210
  };
217
211
  }
218
212
  const builders = [];
@@ -231,7 +225,7 @@ async function detectBuilders(files, pkg, options = {}) {
231
225
  });
232
226
  }
233
227
  }
234
- const routesResult = getRouteResult(apiRoutes, dynamicRoutes, usedOutputDirectory, apiBuilders, frontendBuilder, options);
228
+ const routesResult = getRouteResult(pkg, apiRoutes, dynamicRoutes, usedOutputDirectory, apiBuilders, frontendBuilder, options);
235
229
  return {
236
230
  warnings,
237
231
  builders: builders.length ? builders : null,
@@ -240,7 +234,7 @@ async function detectBuilders(files, pkg, options = {}) {
240
234
  defaultRoutes: routesResult.defaultRoutes,
241
235
  rewriteRoutes: routesResult.rewriteRoutes,
242
236
  errorRoutes: routesResult.errorRoutes,
243
- dynamicRoutesWithKeys,
237
+ limitedRoutes: routesResult.limitedRoutes,
244
238
  };
245
239
  }
246
240
  exports.detectBuilders = detectBuilders;
@@ -495,7 +489,6 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
495
489
  return {
496
490
  apiRoute: null,
497
491
  isDynamic: false,
498
- routeKeys: {},
499
492
  routeError: {
500
493
  code: 'conflicting_path_segment',
501
494
  message: `The segment "${conflictingSegment}" occurs more than ` +
@@ -510,7 +503,6 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
510
503
  return {
511
504
  apiRoute: null,
512
505
  isDynamic: false,
513
- routeKeys: {},
514
506
  routeError: {
515
507
  code: 'conflicting_file_path',
516
508
  message: `Two or more files have conflicting paths or names. ` +
@@ -523,7 +515,6 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
523
515
  return {
524
516
  apiRoute: out.route,
525
517
  isDynamic: out.isDynamic,
526
- routeKeys: out.routeKeys,
527
518
  routeError: null,
528
519
  };
529
520
  }
@@ -633,7 +624,6 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
633
624
  const parts = filePath.split('/');
634
625
  let counter = 1;
635
626
  const query = [];
636
- const routeKeys = {};
637
627
  let isDynamic = false;
638
628
  const srcParts = parts.map((segment, i) => {
639
629
  const name = getSegmentName(segment);
@@ -641,7 +631,6 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
641
631
  if (name !== null) {
642
632
  // We can't use `URLSearchParams` because `$` would get escaped
643
633
  query.push(`${name}=$${counter++}`);
644
- routeKeys[name] = name;
645
634
  isDynamic = true;
646
635
  return `([^/]+)`;
647
636
  }
@@ -684,25 +673,53 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
684
673
  dest: `/${filePath}${queryString}`,
685
674
  };
686
675
  }
687
- return { route, isDynamic, routeKeys };
676
+ return { route, isDynamic };
688
677
  }
689
- function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders, frontendBuilder, options) {
678
+ function getRouteResult(pkg, apiRoutes, dynamicRoutes, outputDirectory, apiBuilders, frontendBuilder, options) {
690
679
  var _a, _b;
680
+ const deps = Object.assign({}, pkg === null || pkg === void 0 ? void 0 : pkg.dependencies, pkg === null || pkg === void 0 ? void 0 : pkg.devDependencies);
691
681
  const defaultRoutes = [];
692
682
  const redirectRoutes = [];
693
683
  const rewriteRoutes = [];
694
684
  const errorRoutes = [];
685
+ const limitedRoutes = {
686
+ defaultRoutes: [],
687
+ redirectRoutes: [],
688
+ rewriteRoutes: [],
689
+ };
695
690
  const framework = ((_a = frontendBuilder === null || frontendBuilder === void 0 ? void 0 : frontendBuilder.config) === null || _a === void 0 ? void 0 : _a.framework) || '';
696
691
  const isNextjs = framework === 'nextjs' || _1.isOfficialRuntime('next', frontendBuilder === null || frontendBuilder === void 0 ? void 0 : frontendBuilder.use);
697
692
  const ignoreRuntimes = (_b = slugToFramework.get(framework)) === null || _b === void 0 ? void 0 : _b.ignoreRuntimes;
698
693
  if (apiRoutes && apiRoutes.length > 0) {
699
694
  if (options.featHandleMiss) {
695
+ // Exclude extension names if the corresponding plugin is not found in package.json
696
+ // detectBuilders({ignoreRoutesForBuilders: ['@vercel/python']})
697
+ // return a copy of routes.
698
+ // We should exclud errorRoutes and
700
699
  const extSet = detectApiExtensions(apiBuilders);
700
+ const withTag = options.tag ? `@${options.tag}` : '';
701
+ const extSetLimited = detectApiExtensions(apiBuilders.filter(b => {
702
+ if (b.use === `@vercel/python${withTag}` &&
703
+ !('vercel-plugin-python' in deps)) {
704
+ return false;
705
+ }
706
+ if (b.use === `@vercel/go${withTag}` &&
707
+ !('vercel-plugin-go' in deps)) {
708
+ return false;
709
+ }
710
+ if (b.use === `@vercel/ruby${withTag}` &&
711
+ !('vercel-plugin-ruby' in deps)) {
712
+ return false;
713
+ }
714
+ return true;
715
+ }));
701
716
  if (extSet.size > 0) {
702
- const exts = Array.from(extSet)
717
+ const extGroup = `(?:\\.(?:${Array.from(extSet)
703
718
  .map(ext => ext.slice(1))
704
- .join('|');
705
- const extGroup = `(?:\\.(?:${exts}))`;
719
+ .join('|')}))`;
720
+ const extGroupLimited = `(?:\\.(?:${Array.from(extSetLimited)
721
+ .map(ext => ext.slice(1))
722
+ .join('|')}))`;
706
723
  if (options.cleanUrls) {
707
724
  redirectRoutes.push({
708
725
  src: `^/(api(?:.+)?)/index${extGroup}?/?$`,
@@ -716,6 +733,18 @@ function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders,
716
733
  },
717
734
  status: 308,
718
735
  });
736
+ limitedRoutes.redirectRoutes.push({
737
+ src: `^/(api(?:.+)?)/index${extGroupLimited}?/?$`,
738
+ headers: { Location: options.trailingSlash ? '/$1/' : '/$1' },
739
+ status: 308,
740
+ });
741
+ limitedRoutes.redirectRoutes.push({
742
+ src: `^/api/(.+)${extGroupLimited}/?$`,
743
+ headers: {
744
+ Location: options.trailingSlash ? '/api/$1/' : '/api/$1',
745
+ },
746
+ status: 308,
747
+ });
719
748
  }
720
749
  else {
721
750
  defaultRoutes.push({ handle: 'miss' });
@@ -724,9 +753,16 @@ function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders,
724
753
  dest: '/api/$1',
725
754
  check: true,
726
755
  });
756
+ limitedRoutes.defaultRoutes.push({ handle: 'miss' });
757
+ limitedRoutes.defaultRoutes.push({
758
+ src: `^/api/(.+)${extGroupLimited}$`,
759
+ dest: '/api/$1',
760
+ check: true,
761
+ });
727
762
  }
728
763
  }
729
764
  rewriteRoutes.push(...dynamicRoutes);
765
+ limitedRoutes.rewriteRoutes.push(...dynamicRoutes);
730
766
  if (typeof ignoreRuntimes === 'undefined') {
731
767
  // This route is only necessary to hide the directory listing
732
768
  // to avoid enumerating serverless function names.
@@ -771,6 +807,7 @@ function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders,
771
807
  redirectRoutes,
772
808
  rewriteRoutes,
773
809
  errorRoutes,
810
+ limitedRoutes,
774
811
  };
775
812
  }
776
813
  function sortFilesBySegmentCount(fileA, fileB) {
package/dist/index.js CHANGED
@@ -26274,7 +26274,7 @@ exports.frameworks = [
26274
26274
  name: 'Remix',
26275
26275
  slug: 'remix',
26276
26276
  demo: 'https://remix.examples.vercel.com',
26277
- logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/remix.svg',
26277
+ logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/remix-no-shadow.svg',
26278
26278
  tagline: 'Build Better Websites',
26279
26279
  description: 'A new Remix app — the result of running `npx create-remix`.',
26280
26280
  website: 'https://remix.run',
@@ -26329,8 +26329,8 @@ exports.frameworks = [
26329
26329
  ],
26330
26330
  defaultHeaders: [
26331
26331
  {
26332
- source: '^/build/(.*)$',
26333
- regex: '^/build/(.*)$',
26332
+ source: '/build/(.*)',
26333
+ regex: '/build/(.*)',
26334
26334
  headers: [
26335
26335
  { key: 'cache-control', value: 'public, max-age=31536000, immutable' },
26336
26336
  ],
@@ -32285,7 +32285,6 @@ const fs_extra_1 = __importDefault(__webpack_require__(5392));
32285
32285
  const path_1 = __webpack_require__(5622);
32286
32286
  const glob_1 = __importDefault(__webpack_require__(4240));
32287
32287
  const normalize_path_1 = __webpack_require__(6261);
32288
- const detect_builders_1 = __webpack_require__(4246);
32289
32288
  const lambda_1 = __webpack_require__(6721);
32290
32289
  const minimatch_1 = __importDefault(__webpack_require__(9566));
32291
32290
  /**
@@ -32296,13 +32295,12 @@ const minimatch_1 = __importDefault(__webpack_require__(9566));
32296
32295
  function convertRuntimeToPlugin(buildRuntime, ext) {
32297
32296
  // This `build()` signature should match `plugin.build()` signature in `vercel build`.
32298
32297
  return async function build({ vercelConfig, workPath, }) {
32299
- var _a;
32300
32298
  const opts = { cwd: workPath };
32301
32299
  const files = await glob_1.default('**', opts);
32302
32300
  delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
32303
32301
  const entrypoints = await glob_1.default(`api/**/*${ext}`, opts);
32304
32302
  const pages = {};
32305
- const { functions = {}, cleanUrls, trailingSlash } = vercelConfig;
32303
+ const { functions = {} } = vercelConfig;
32306
32304
  const traceDir = path_1.join(workPath, '.output', 'runtime-traced-files');
32307
32305
  await fs_extra_1.default.ensureDir(traceDir);
32308
32306
  for (const entrypoint of Object.keys(entrypoints)) {
@@ -32317,6 +32315,9 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
32317
32315
  includeFiles: config.includeFiles,
32318
32316
  excludeFiles: config.excludeFiles,
32319
32317
  },
32318
+ meta: {
32319
+ avoidTopLevelInstall: true,
32320
+ },
32320
32321
  });
32321
32322
  pages[entrypoint] = {
32322
32323
  handler: output.handler,
@@ -32325,7 +32326,7 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
32325
32326
  maxDuration: output.maxDuration,
32326
32327
  environment: output.environment,
32327
32328
  allowQuery: output.allowQuery,
32328
- regions: output.regions,
32329
+ //regions: output.regions,
32329
32330
  };
32330
32331
  // @ts-ignore This symbol is a private API
32331
32332
  const lambdaFiles = output[lambda_1.FILES_SYMBOL];
@@ -32359,53 +32360,6 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
32359
32360
  await fs_extra_1.default.writeFile(nft, json);
32360
32361
  }
32361
32362
  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
- });
32409
32363
  };
32410
32364
  }
32411
32365
  exports.convertRuntimeToPlugin = convertRuntimeToPlugin;
@@ -32590,7 +32544,7 @@ async function detectBuilders(files, pkg, options = {}) {
32590
32544
  redirectRoutes: null,
32591
32545
  rewriteRoutes: null,
32592
32546
  errorRoutes: null,
32593
- dynamicRoutesWithKeys: null,
32547
+ limitedRoutes: null,
32594
32548
  };
32595
32549
  }
32596
32550
  const sortedFiles = files.sort(sortFiles);
@@ -32624,12 +32578,11 @@ async function detectBuilders(files, pkg, options = {}) {
32624
32578
  let fallbackEntrypoint = null;
32625
32579
  const apiRoutes = [];
32626
32580
  const dynamicRoutes = [];
32627
- const dynamicRoutesWithKeys = [];
32628
32581
  // API
32629
32582
  for (const fileName of sortedFiles) {
32630
32583
  const apiBuilder = maybeGetApiBuilder(fileName, apiMatches, options);
32631
32584
  if (apiBuilder) {
32632
- const { routeError, apiRoute, isDynamic, routeKeys } = getApiRoute(fileName, apiSortedFiles, options, absolutePathCache);
32585
+ const { routeError, apiRoute, isDynamic } = getApiRoute(fileName, apiSortedFiles, options, absolutePathCache);
32633
32586
  if (routeError) {
32634
32587
  return {
32635
32588
  builders: null,
@@ -32639,18 +32592,13 @@ async function detectBuilders(files, pkg, options = {}) {
32639
32592
  redirectRoutes: null,
32640
32593
  rewriteRoutes: null,
32641
32594
  errorRoutes: null,
32642
- dynamicRoutesWithKeys: null,
32595
+ limitedRoutes: null,
32643
32596
  };
32644
32597
  }
32645
32598
  if (apiRoute) {
32646
32599
  apiRoutes.push(apiRoute);
32647
32600
  if (isDynamic) {
32648
32601
  dynamicRoutes.push(apiRoute);
32649
- dynamicRoutesWithKeys.push({
32650
- fileName,
32651
- regex: apiRoute.src,
32652
- routeKeys,
32653
- });
32654
32602
  }
32655
32603
  }
32656
32604
  addToUsedFunctions(apiBuilder);
@@ -32699,7 +32647,7 @@ async function detectBuilders(files, pkg, options = {}) {
32699
32647
  defaultRoutes: null,
32700
32648
  rewriteRoutes: null,
32701
32649
  errorRoutes: null,
32702
- dynamicRoutesWithKeys: null,
32650
+ limitedRoutes: null,
32703
32651
  };
32704
32652
  }
32705
32653
  // If `outputDirectory` is an empty string,
@@ -32736,7 +32684,7 @@ async function detectBuilders(files, pkg, options = {}) {
32736
32684
  defaultRoutes: null,
32737
32685
  rewriteRoutes: null,
32738
32686
  errorRoutes: null,
32739
- dynamicRoutesWithKeys: null,
32687
+ limitedRoutes: null,
32740
32688
  };
32741
32689
  }
32742
32690
  const builders = [];
@@ -32755,7 +32703,7 @@ async function detectBuilders(files, pkg, options = {}) {
32755
32703
  });
32756
32704
  }
32757
32705
  }
32758
- const routesResult = getRouteResult(apiRoutes, dynamicRoutes, usedOutputDirectory, apiBuilders, frontendBuilder, options);
32706
+ const routesResult = getRouteResult(pkg, apiRoutes, dynamicRoutes, usedOutputDirectory, apiBuilders, frontendBuilder, options);
32759
32707
  return {
32760
32708
  warnings,
32761
32709
  builders: builders.length ? builders : null,
@@ -32764,7 +32712,7 @@ async function detectBuilders(files, pkg, options = {}) {
32764
32712
  defaultRoutes: routesResult.defaultRoutes,
32765
32713
  rewriteRoutes: routesResult.rewriteRoutes,
32766
32714
  errorRoutes: routesResult.errorRoutes,
32767
- dynamicRoutesWithKeys,
32715
+ limitedRoutes: routesResult.limitedRoutes,
32768
32716
  };
32769
32717
  }
32770
32718
  exports.detectBuilders = detectBuilders;
@@ -33019,7 +32967,6 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
33019
32967
  return {
33020
32968
  apiRoute: null,
33021
32969
  isDynamic: false,
33022
- routeKeys: {},
33023
32970
  routeError: {
33024
32971
  code: 'conflicting_path_segment',
33025
32972
  message: `The segment "${conflictingSegment}" occurs more than ` +
@@ -33034,7 +32981,6 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
33034
32981
  return {
33035
32982
  apiRoute: null,
33036
32983
  isDynamic: false,
33037
- routeKeys: {},
33038
32984
  routeError: {
33039
32985
  code: 'conflicting_file_path',
33040
32986
  message: `Two or more files have conflicting paths or names. ` +
@@ -33047,7 +32993,6 @@ function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
33047
32993
  return {
33048
32994
  apiRoute: out.route,
33049
32995
  isDynamic: out.isDynamic,
33050
- routeKeys: out.routeKeys,
33051
32996
  routeError: null,
33052
32997
  };
33053
32998
  }
@@ -33157,7 +33102,6 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
33157
33102
  const parts = filePath.split('/');
33158
33103
  let counter = 1;
33159
33104
  const query = [];
33160
- const routeKeys = {};
33161
33105
  let isDynamic = false;
33162
33106
  const srcParts = parts.map((segment, i) => {
33163
33107
  const name = getSegmentName(segment);
@@ -33165,7 +33109,6 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
33165
33109
  if (name !== null) {
33166
33110
  // We can't use `URLSearchParams` because `$` would get escaped
33167
33111
  query.push(`${name}=$${counter++}`);
33168
- routeKeys[name] = name;
33169
33112
  isDynamic = true;
33170
33113
  return `([^/]+)`;
33171
33114
  }
@@ -33208,25 +33151,53 @@ function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
33208
33151
  dest: `/${filePath}${queryString}`,
33209
33152
  };
33210
33153
  }
33211
- return { route, isDynamic, routeKeys };
33154
+ return { route, isDynamic };
33212
33155
  }
33213
- function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders, frontendBuilder, options) {
33156
+ function getRouteResult(pkg, apiRoutes, dynamicRoutes, outputDirectory, apiBuilders, frontendBuilder, options) {
33214
33157
  var _a, _b;
33158
+ const deps = Object.assign({}, pkg === null || pkg === void 0 ? void 0 : pkg.dependencies, pkg === null || pkg === void 0 ? void 0 : pkg.devDependencies);
33215
33159
  const defaultRoutes = [];
33216
33160
  const redirectRoutes = [];
33217
33161
  const rewriteRoutes = [];
33218
33162
  const errorRoutes = [];
33163
+ const limitedRoutes = {
33164
+ defaultRoutes: [],
33165
+ redirectRoutes: [],
33166
+ rewriteRoutes: [],
33167
+ };
33219
33168
  const framework = ((_a = frontendBuilder === null || frontendBuilder === void 0 ? void 0 : frontendBuilder.config) === null || _a === void 0 ? void 0 : _a.framework) || '';
33220
33169
  const isNextjs = framework === 'nextjs' || _1.isOfficialRuntime('next', frontendBuilder === null || frontendBuilder === void 0 ? void 0 : frontendBuilder.use);
33221
33170
  const ignoreRuntimes = (_b = slugToFramework.get(framework)) === null || _b === void 0 ? void 0 : _b.ignoreRuntimes;
33222
33171
  if (apiRoutes && apiRoutes.length > 0) {
33223
33172
  if (options.featHandleMiss) {
33173
+ // Exclude extension names if the corresponding plugin is not found in package.json
33174
+ // detectBuilders({ignoreRoutesForBuilders: ['@vercel/python']})
33175
+ // return a copy of routes.
33176
+ // We should exclud errorRoutes and
33224
33177
  const extSet = detectApiExtensions(apiBuilders);
33178
+ const withTag = options.tag ? `@${options.tag}` : '';
33179
+ const extSetLimited = detectApiExtensions(apiBuilders.filter(b => {
33180
+ if (b.use === `@vercel/python${withTag}` &&
33181
+ !('vercel-plugin-python' in deps)) {
33182
+ return false;
33183
+ }
33184
+ if (b.use === `@vercel/go${withTag}` &&
33185
+ !('vercel-plugin-go' in deps)) {
33186
+ return false;
33187
+ }
33188
+ if (b.use === `@vercel/ruby${withTag}` &&
33189
+ !('vercel-plugin-ruby' in deps)) {
33190
+ return false;
33191
+ }
33192
+ return true;
33193
+ }));
33225
33194
  if (extSet.size > 0) {
33226
- const exts = Array.from(extSet)
33195
+ const extGroup = `(?:\\.(?:${Array.from(extSet)
33196
+ .map(ext => ext.slice(1))
33197
+ .join('|')}))`;
33198
+ const extGroupLimited = `(?:\\.(?:${Array.from(extSetLimited)
33227
33199
  .map(ext => ext.slice(1))
33228
- .join('|');
33229
- const extGroup = `(?:\\.(?:${exts}))`;
33200
+ .join('|')}))`;
33230
33201
  if (options.cleanUrls) {
33231
33202
  redirectRoutes.push({
33232
33203
  src: `^/(api(?:.+)?)/index${extGroup}?/?$`,
@@ -33240,6 +33211,18 @@ function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders,
33240
33211
  },
33241
33212
  status: 308,
33242
33213
  });
33214
+ limitedRoutes.redirectRoutes.push({
33215
+ src: `^/(api(?:.+)?)/index${extGroupLimited}?/?$`,
33216
+ headers: { Location: options.trailingSlash ? '/$1/' : '/$1' },
33217
+ status: 308,
33218
+ });
33219
+ limitedRoutes.redirectRoutes.push({
33220
+ src: `^/api/(.+)${extGroupLimited}/?$`,
33221
+ headers: {
33222
+ Location: options.trailingSlash ? '/api/$1/' : '/api/$1',
33223
+ },
33224
+ status: 308,
33225
+ });
33243
33226
  }
33244
33227
  else {
33245
33228
  defaultRoutes.push({ handle: 'miss' });
@@ -33248,9 +33231,16 @@ function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders,
33248
33231
  dest: '/api/$1',
33249
33232
  check: true,
33250
33233
  });
33234
+ limitedRoutes.defaultRoutes.push({ handle: 'miss' });
33235
+ limitedRoutes.defaultRoutes.push({
33236
+ src: `^/api/(.+)${extGroupLimited}$`,
33237
+ dest: '/api/$1',
33238
+ check: true,
33239
+ });
33251
33240
  }
33252
33241
  }
33253
33242
  rewriteRoutes.push(...dynamicRoutes);
33243
+ limitedRoutes.rewriteRoutes.push(...dynamicRoutes);
33254
33244
  if (typeof ignoreRuntimes === 'undefined') {
33255
33245
  // This route is only necessary to hide the directory listing
33256
33246
  // to avoid enumerating serverless function names.
@@ -33295,6 +33285,7 @@ function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders,
33295
33285
  redirectRoutes,
33296
33286
  rewriteRoutes,
33297
33287
  errorRoutes,
33288
+ limitedRoutes,
33298
33289
  };
33299
33290
  }
33300
33291
  function sortFilesBySegmentCount(fileA, fileB) {
package/dist/types.d.ts CHANGED
@@ -51,6 +51,7 @@ export interface Meta {
51
51
  filesRemoved?: string[];
52
52
  env?: Env;
53
53
  buildEnv?: Env;
54
+ avoidTopLevelInstall?: boolean;
54
55
  }
55
56
  export interface AnalyzeOptions {
56
57
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/build-utils",
3
- "version": "2.12.3-canary.22",
3
+ "version": "2.12.3-canary.26",
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.13",
33
+ "@vercel/frameworks": "0.5.1-canary.16",
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": "0cacb1bdace342133fad4bd7a98354e5b2948df0"
52
+ "gitHead": "1202ff7b2b8a821c6404a86baa12996f3078ed36"
53
53
  }