@vercel/build-utils 2.12.3-canary.40 → 2.12.3-canary.44

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.
@@ -78,7 +78,6 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
78
78
  // need to be able to easily inspect the output.
79
79
  `api-routes-${pluginName}`);
80
80
  await fs_extra_1.default.ensureDir(traceDir);
81
- let newPathsRuntime = new Set();
82
81
  const entryRoot = path_1.join(outputPath, 'server', 'pages');
83
82
  for (const entrypoint of Object.keys(entrypoints)) {
84
83
  const { output } = await buildRuntime({
@@ -93,13 +92,6 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
93
92
  skipDownload: true,
94
93
  },
95
94
  });
96
- // Legacy Runtimes tend to pollute the `workPath` with compiled results,
97
- // because the `workPath` used to be a place that was a place where they could
98
- // just put anything, but nowadays it's the working directory of the `vercel build`
99
- // command, which is the place where the developer keeps their source files,
100
- // so we don't want to pollute this space unnecessarily. That means we have to clean
101
- // up files that were created by the build, which is done further below.
102
- const sourceFilesAfterBuild = await getSourceFiles(workPath, ignoreFilter);
103
95
  // @ts-ignore This symbol is a private API
104
96
  const lambdaFiles = output[lambda_1.FILES_SYMBOL];
105
97
  // When deploying, the `files` that are passed to the Legacy Runtimes already
@@ -134,12 +126,9 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
134
126
  const entryBase = path_1.basename(entrypoint).replace(ext, handlerExtName);
135
127
  const entryPath = path_1.join(path_1.dirname(entrypoint), entryBase);
136
128
  const entry = path_1.join(entryRoot, entryPath);
137
- // We never want to link here, only copy, because the launcher
138
- // file often has the same name for every entrypoint, which means that
139
- // every build for every entrypoint overwrites the launcher of the previous
140
- // one, so linking would end with a broken reference.
129
+ // Create the parent directory of the API Route that will be created
130
+ // for the current entrypoint inside of `.output/server/pages/api`.
141
131
  await fs_extra_1.default.ensureDir(path_1.dirname(entry));
142
- await fs_extra_1.default.copy(handlerFile.fsPath, entry);
143
132
  // For compiled languages, the launcher file will be binary and therefore
144
133
  // won't try to import a user-provided request handler (instead, it will
145
134
  // contain it). But for interpreted languages, the launcher might try to
@@ -156,8 +145,12 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
156
145
  const locationPrefix = path_1.relative(entry, outputPath);
157
146
  let handlerContent = await fs_extra_1.default.readFile(fsPath, encoding);
158
147
  const importPaths = [
159
- // This is the full entrypoint path, like `./api/test.py`
160
- `./${entrypoint}`,
148
+ // This is the full entrypoint path, like `./api/test.py`. In our tests
149
+ // Python didn't support importing from a parent directory without using different
150
+ // code in the launcher that registers it as a location for modules and then changing
151
+ // the importing syntax, but continuing to import it like before seems to work. If
152
+ // other languages need this, we should consider excluding Python explicitly.
153
+ // `./${entrypoint}`,
161
154
  // This is the entrypoint path without extension, like `api/test`
162
155
  entrypoint.slice(0, -ext.length),
163
156
  ];
@@ -187,52 +180,15 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
187
180
  else {
188
181
  await fs_extra_1.default.copy(handlerFile.fsPath, entry);
189
182
  }
190
- const newFilesEntrypoint = [];
191
- const newDirectoriesEntrypoint = [];
192
- const preBuildFiles = Object.values(sourceFilesPreBuild).map(file => {
193
- return file.fsPath;
194
- });
195
- // Generate a list of directories and files that weren't present
196
- // before the entrypoint was processed by the Legacy Runtime, so
197
- // that we can perform a cleanup later. We need to divide into files
198
- // and directories because only cleaning up files might leave empty
199
- // directories, and listing directories separately also speeds up the
200
- // build because we can just delete them, which wipes all of their nested
201
- // paths, instead of iterating through all files that should be deleted.
202
- for (const file in sourceFilesAfterBuild) {
203
- if (!sourceFilesPreBuild[file]) {
204
- const path = sourceFilesAfterBuild[file].fsPath;
205
- const dirPath = path_1.dirname(path);
206
- // If none of the files that were present before the entrypoint
207
- // was processed are contained within the directory we're looking
208
- // at right now, then we know it's a newly added directory
209
- // and it can therefore be removed later on.
210
- const isNewDir = !preBuildFiles.some(filePath => {
211
- return path_1.dirname(filePath).startsWith(dirPath);
212
- });
213
- // Check out the list of tracked directories that were
214
- // newly added and see if one of them contains the path
215
- // we're looking at.
216
- const hasParentDir = newDirectoriesEntrypoint.some(dir => {
217
- return path.startsWith(dir);
218
- });
219
- // If we have already tracked a directory that was newly
220
- // added that sits above the file or directory that we're
221
- // looking at, we don't need to add more entries to the list
222
- // because when the parent will get removed in the future,
223
- // all of its children (and therefore the path we're looking at)
224
- // will automatically get removed anyways.
225
- if (hasParentDir) {
226
- continue;
227
- }
228
- if (isNewDir) {
229
- newDirectoriesEntrypoint.push(dirPath);
230
- }
231
- else {
232
- newFilesEntrypoint.push(path);
233
- }
234
- }
235
- }
183
+ // Legacy Runtimes based on interpreted languages will create a new launcher file
184
+ // for every entrypoint, but they will create each one inside `workPath`, which means that
185
+ // the launcher for one entrypoint will overwrite the launcher provided for the previous
186
+ // entrypoint. That's why, above, we copy the file contents into the new destination (and
187
+ // optionally transform them along the way), instead of linking. We then also want to remove
188
+ // the copy origin right here, so that the `workPath` doesn't contain a useless launcher file
189
+ // once the build has finished running.
190
+ await fs_extra_1.default.remove(handlerFile.fsPath);
191
+ _1.debug(`Removed temporary file "${handlerFile.fsPath}"`);
236
192
  const nft = `${entry}.nft.json`;
237
193
  const json = JSON.stringify({
238
194
  version: 2,
@@ -250,16 +206,7 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
250
206
  })
251
207
  .filter(Boolean),
252
208
  });
253
- await fs_extra_1.default.ensureDir(path_1.dirname(nft));
254
209
  await fs_extra_1.default.writeFile(nft, json);
255
- // Extend the list of directories and files that were created by the
256
- // Legacy Runtime with the list of directories and files that were
257
- // created for the entrypoint that was just processed above.
258
- newPathsRuntime = new Set([
259
- ...newPathsRuntime,
260
- ...newFilesEntrypoint,
261
- ...newDirectoriesEntrypoint,
262
- ]);
263
210
  // Add an entry that will later on be added to the `functions-manifest.json`
264
211
  // file that is placed inside of the `.output` directory.
265
212
  pages[normalize_path_1.normalizePath(entryPath)] = {
@@ -275,18 +222,6 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
275
222
  allowQuery: output.allowQuery,
276
223
  };
277
224
  }
278
- // A list of all the files that were created by the Legacy Runtime,
279
- // which we'd like to remove from the File System.
280
- const toRemove = Array.from(newPathsRuntime).map(path => {
281
- _1.debug(`Removing ${path} as part of cleanup`);
282
- return fs_extra_1.default.remove(path);
283
- });
284
- // Once all the entrypoints have been processed, we'd like to
285
- // remove all the files from `workPath` that originally weren't present
286
- // before the Legacy Runtime began running, because the `workPath`
287
- // is nowadays the directory in which the user keeps their source code, since
288
- // we're no longer running separate parallel builds for every Legacy Runtime.
289
- await Promise.all(toRemove);
290
225
  // Add any Serverless Functions that were exposed by the Legacy Runtime
291
226
  // to the `functions-manifest.json` file provided in `.output`.
292
227
  await updateFunctionsManifest({ workPath, pages });
@@ -1,5 +1,5 @@
1
1
  import { Route } from '@vercel/routing-utils';
2
- import { PackageJson, Builder, BuilderFunctions } from './types';
2
+ import { PackageJson, Builder, BuilderFunctions, ProjectSettings } from './types';
3
3
  interface ErrorResponse {
4
4
  code: string;
5
5
  message: string;
@@ -10,14 +10,7 @@ interface Options {
10
10
  tag?: 'canary' | 'latest' | string;
11
11
  functions?: BuilderFunctions;
12
12
  ignoreBuildScript?: boolean;
13
- projectSettings?: {
14
- framework?: string | null;
15
- devCommand?: string | null;
16
- installCommand?: string | null;
17
- buildCommand?: string | null;
18
- outputDirectory?: string | null;
19
- createdAt?: number;
20
- };
13
+ projectSettings?: ProjectSettings;
21
14
  cleanUrls?: boolean;
22
15
  trailingSlash?: boolean;
23
16
  featHandleMiss?: boolean;
@@ -0,0 +1,34 @@
1
+ import type { Builder, BuilderFunctions, PackageJson, ProjectSettings } from './types';
2
+ interface Metadata {
3
+ plugins: string[];
4
+ hasDotOutput: boolean;
5
+ hasMiddleware: boolean;
6
+ }
7
+ /**
8
+ * If the Deployment can be built with the new File System API,
9
+ * return the new Builder. Otherwise an "Exclusion Condition"
10
+ * was hit so return `null` builder with a `reason` for exclusion.
11
+ */
12
+ export declare function detectFileSystemAPI({ files, projectSettings, builders, vercelConfig, pkg, tag, enableFlag, }: {
13
+ files: {
14
+ [relPath: string]: any;
15
+ };
16
+ projectSettings: ProjectSettings;
17
+ builders: Builder[];
18
+ vercelConfig: {
19
+ builds?: Builder[];
20
+ functions?: BuilderFunctions;
21
+ } | null | undefined;
22
+ pkg: PackageJson | null | undefined;
23
+ tag: string | undefined;
24
+ enableFlag: boolean | undefined;
25
+ }): Promise<{
26
+ metadata: Metadata;
27
+ fsApiBuilder: Builder;
28
+ reason: null;
29
+ } | {
30
+ metadata: Metadata;
31
+ fsApiBuilder: null;
32
+ reason: string;
33
+ }>;
34
+ export {};
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.detectFileSystemAPI = void 0;
7
+ const semver_1 = __importDefault(require("semver"));
8
+ const _1 = require("./");
9
+ const enableFileSystemApiFrameworks = new Set(['solidstart']);
10
+ /**
11
+ * If the Deployment can be built with the new File System API,
12
+ * return the new Builder. Otherwise an "Exclusion Condition"
13
+ * was hit so return `null` builder with a `reason` for exclusion.
14
+ */
15
+ async function detectFileSystemAPI({ files, projectSettings, builders, vercelConfig, pkg, tag, enableFlag = false, }) {
16
+ const framework = projectSettings.framework || '';
17
+ const deps = Object.assign({}, pkg === null || pkg === void 0 ? void 0 : pkg.dependencies, pkg === null || pkg === void 0 ? void 0 : pkg.devDependencies);
18
+ const plugins = Object.keys(deps).filter(dep => dep.startsWith('vercel-plugin-'));
19
+ const hasDotOutput = Object.keys(files).some(file => file.startsWith('.output/'));
20
+ const hasMiddleware = Boolean(files['_middleware.js'] || files['_middleware.ts']);
21
+ const metadata = {
22
+ plugins,
23
+ hasDotOutput,
24
+ hasMiddleware,
25
+ };
26
+ const isEnabled = enableFlag ||
27
+ hasMiddleware ||
28
+ hasDotOutput ||
29
+ enableFileSystemApiFrameworks.has(framework);
30
+ if (!isEnabled) {
31
+ return { metadata, fsApiBuilder: null, reason: 'Flag not enabled.' };
32
+ }
33
+ if ((vercelConfig === null || vercelConfig === void 0 ? void 0 : vercelConfig.builds) && vercelConfig.builds.length > 0) {
34
+ return {
35
+ metadata,
36
+ fsApiBuilder: null,
37
+ reason: 'Detected `builds` in vercel.json. Please remove it in favor of CLI plugins.',
38
+ };
39
+ }
40
+ if (Object.values((vercelConfig === null || vercelConfig === void 0 ? void 0 : vercelConfig.functions) || {}).some(fn => !!fn.runtime)) {
41
+ return {
42
+ metadata,
43
+ fsApiBuilder: null,
44
+ reason: 'Detected `functions.runtime` in vercel.json. Please remove it in favor of CLI plugins.',
45
+ };
46
+ }
47
+ if (process.env.HUGO_VERSION) {
48
+ return {
49
+ metadata,
50
+ fsApiBuilder: null,
51
+ reason: 'Detected `HUGO_VERSION` environment variable. Please remove it.',
52
+ };
53
+ }
54
+ if (process.env.ZOLA_VERSION) {
55
+ return {
56
+ metadata,
57
+ fsApiBuilder: null,
58
+ reason: 'Detected `ZOLA_VERSION` environment variable. Please remove it.',
59
+ };
60
+ }
61
+ if (process.env.GUTENBERG_VERSION) {
62
+ return {
63
+ metadata,
64
+ fsApiBuilder: null,
65
+ reason: 'Detected `GUTENBERG_VERSION` environment variable. Please remove it.',
66
+ };
67
+ }
68
+ const invalidBuilder = builders.find(({ use }) => {
69
+ const valid = _1.isOfficialRuntime('go', use) ||
70
+ _1.isOfficialRuntime('python', use) ||
71
+ _1.isOfficialRuntime('ruby', use) ||
72
+ _1.isOfficialRuntime('node', use) ||
73
+ _1.isOfficialRuntime('next', use) ||
74
+ _1.isOfficialRuntime('static', use) ||
75
+ _1.isOfficialRuntime('static-build', use);
76
+ return !valid;
77
+ });
78
+ if (invalidBuilder) {
79
+ return {
80
+ metadata,
81
+ fsApiBuilder: null,
82
+ reason: `Detected \`${invalidBuilder.use}\` in vercel.json. Please remove it in favor of CLI plugins.`,
83
+ };
84
+ }
85
+ for (const lang of ['go', 'python', 'ruby']) {
86
+ for (const { use } of builders) {
87
+ const plugin = 'vercel-plugin-' + lang;
88
+ if (_1.isOfficialRuntime(lang, use) && !deps[plugin]) {
89
+ return {
90
+ metadata,
91
+ fsApiBuilder: null,
92
+ reason: `Detected \`${lang}\` Serverless Function usage without plugin \`${plugin}\`. Please run \`npm i ${plugin}\`.`,
93
+ };
94
+ }
95
+ }
96
+ }
97
+ if (framework === 'nuxtjs' ||
98
+ framework === 'sveltekit' ||
99
+ framework === 'redwoodjs') {
100
+ return {
101
+ metadata,
102
+ fsApiBuilder: null,
103
+ reason: `Detected framework \`${framework}\` that only supports legacy File System API. Please contact the framework author.`,
104
+ };
105
+ }
106
+ if (framework === 'nextjs' && !hasDotOutput) {
107
+ // Use the old pipeline if a custom output directory was specified for Next.js
108
+ // because `vercel build` cannot ensure that the directory will be in the same
109
+ // location as `.output`, which can break imports (not just nft.json files).
110
+ if (projectSettings === null || projectSettings === void 0 ? void 0 : projectSettings.outputDirectory) {
111
+ return {
112
+ metadata,
113
+ fsApiBuilder: null,
114
+ reason: `Detected Next.js with Output Directory \`${projectSettings.outputDirectory}\` override. Please change it back to the default.`,
115
+ };
116
+ }
117
+ const nextVersion = deps['next'];
118
+ if (!nextVersion) {
119
+ return {
120
+ metadata,
121
+ fsApiBuilder: null,
122
+ reason: `Detected Next.js in Project Settings but missing \`next\` package.json dependencies. Please run \`npm i next\`.`,
123
+ };
124
+ }
125
+ // TODO: Read version from lockfile instead of package.json
126
+ if (nextVersion !== 'latest' && nextVersion !== 'canary') {
127
+ const fixedVersion = semver_1.default.valid(semver_1.default.coerce(nextVersion) || '');
128
+ if (!fixedVersion || !semver_1.default.gte(fixedVersion, '12.0.0')) {
129
+ return {
130
+ metadata,
131
+ fsApiBuilder: null,
132
+ reason: `Detected legacy Next.js version "${nextVersion}" in package.json. Please run \`npm i next@latest\` to upgrade.`,
133
+ };
134
+ }
135
+ }
136
+ }
137
+ if (!hasDotOutput) {
138
+ // TODO: Read version from lockfile instead of package.json
139
+ const vercelCliVersion = deps['vercel'];
140
+ if (vercelCliVersion &&
141
+ vercelCliVersion !== 'latest' &&
142
+ vercelCliVersion !== 'canary') {
143
+ const fixedVersion = semver_1.default.valid(semver_1.default.coerce(vercelCliVersion) || '');
144
+ // TODO: we might want to use '24.0.0' once its released
145
+ if (!fixedVersion || !semver_1.default.gte(fixedVersion, '23.1.3-canary.68')) {
146
+ return {
147
+ metadata,
148
+ fsApiBuilder: null,
149
+ reason: `Detected legacy Vercel CLI version "${vercelCliVersion}" in package.json. Please run \`npm i vercel@latest\` to upgrade.`,
150
+ };
151
+ }
152
+ }
153
+ }
154
+ const frontendBuilder = builders.find(({ use }) => _1.isOfficialRuntime('next', use) ||
155
+ _1.isOfficialRuntime('static', use) ||
156
+ _1.isOfficialRuntime('static-build', use));
157
+ const config = (frontendBuilder === null || frontendBuilder === void 0 ? void 0 : frontendBuilder.config) || {};
158
+ const withTag = tag ? `@${tag}` : '';
159
+ const fsApiBuilder = {
160
+ use: `@vercelruntimes/file-system-api${withTag}`,
161
+ src: '**',
162
+ config: {
163
+ ...config,
164
+ fileSystemAPI: true,
165
+ framework: config.framework || framework || null,
166
+ projectSettings,
167
+ hasMiddleware,
168
+ hasDotOutput,
169
+ },
170
+ };
171
+ return { metadata, fsApiBuilder, reason: null };
172
+ }
173
+ exports.detectFileSystemAPI = detectFileSystemAPI;
package/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@ import debug from './debug';
16
16
  import getIgnoreFilter from './get-ignore-filter';
17
17
  export { FileBlob, FileFsRef, FileRef, Lambda, createLambda, Prerender, download, DownloadedFiles, getWriteableDirectory, glob, GlobOptions, rename, execAsync, spawnAsync, getScriptName, installDependencies, runPackageJsonScript, execCommand, spawnCommand, walkParentDirs, getNodeBinPath, runNpmInstall, runBundleInstall, runPipInstall, runShellScript, getNodeVersion, getLatestNodeVersion, getDiscontinuedNodeVersions, getSpawnOptions, streamToBuffer, shouldServe, debug, isSymbolicLink, getLambdaOptionsFromFunction, scanParentDirs, getIgnoreFilter, };
18
18
  export { detectBuilders, detectOutputDirectory, detectApiDirectory, detectApiExtensions, } from './detect-builders';
19
+ export { detectFileSystemAPI } from './detect-file-system-api';
19
20
  export { detectFramework } from './detect-framework';
20
21
  export { DetectorFilesystem } from './detectors/filesystem';
21
22
  export { readConfigFile } from './fs/read-config-file';
package/dist/index.js CHANGED
@@ -32760,7 +32760,6 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
32760
32760
  // need to be able to easily inspect the output.
32761
32761
  `api-routes-${pluginName}`);
32762
32762
  await fs_extra_1.default.ensureDir(traceDir);
32763
- let newPathsRuntime = new Set();
32764
32763
  const entryRoot = path_1.join(outputPath, 'server', 'pages');
32765
32764
  for (const entrypoint of Object.keys(entrypoints)) {
32766
32765
  const { output } = await buildRuntime({
@@ -32775,13 +32774,6 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
32775
32774
  skipDownload: true,
32776
32775
  },
32777
32776
  });
32778
- // Legacy Runtimes tend to pollute the `workPath` with compiled results,
32779
- // because the `workPath` used to be a place that was a place where they could
32780
- // just put anything, but nowadays it's the working directory of the `vercel build`
32781
- // command, which is the place where the developer keeps their source files,
32782
- // so we don't want to pollute this space unnecessarily. That means we have to clean
32783
- // up files that were created by the build, which is done further below.
32784
- const sourceFilesAfterBuild = await getSourceFiles(workPath, ignoreFilter);
32785
32777
  // @ts-ignore This symbol is a private API
32786
32778
  const lambdaFiles = output[lambda_1.FILES_SYMBOL];
32787
32779
  // When deploying, the `files` that are passed to the Legacy Runtimes already
@@ -32816,12 +32808,9 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
32816
32808
  const entryBase = path_1.basename(entrypoint).replace(ext, handlerExtName);
32817
32809
  const entryPath = path_1.join(path_1.dirname(entrypoint), entryBase);
32818
32810
  const entry = path_1.join(entryRoot, entryPath);
32819
- // We never want to link here, only copy, because the launcher
32820
- // file often has the same name for every entrypoint, which means that
32821
- // every build for every entrypoint overwrites the launcher of the previous
32822
- // one, so linking would end with a broken reference.
32811
+ // Create the parent directory of the API Route that will be created
32812
+ // for the current entrypoint inside of `.output/server/pages/api`.
32823
32813
  await fs_extra_1.default.ensureDir(path_1.dirname(entry));
32824
- await fs_extra_1.default.copy(handlerFile.fsPath, entry);
32825
32814
  // For compiled languages, the launcher file will be binary and therefore
32826
32815
  // won't try to import a user-provided request handler (instead, it will
32827
32816
  // contain it). But for interpreted languages, the launcher might try to
@@ -32838,8 +32827,12 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
32838
32827
  const locationPrefix = path_1.relative(entry, outputPath);
32839
32828
  let handlerContent = await fs_extra_1.default.readFile(fsPath, encoding);
32840
32829
  const importPaths = [
32841
- // This is the full entrypoint path, like `./api/test.py`
32842
- `./${entrypoint}`,
32830
+ // This is the full entrypoint path, like `./api/test.py`. In our tests
32831
+ // Python didn't support importing from a parent directory without using different
32832
+ // code in the launcher that registers it as a location for modules and then changing
32833
+ // the importing syntax, but continuing to import it like before seems to work. If
32834
+ // other languages need this, we should consider excluding Python explicitly.
32835
+ // `./${entrypoint}`,
32843
32836
  // This is the entrypoint path without extension, like `api/test`
32844
32837
  entrypoint.slice(0, -ext.length),
32845
32838
  ];
@@ -32869,52 +32862,15 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
32869
32862
  else {
32870
32863
  await fs_extra_1.default.copy(handlerFile.fsPath, entry);
32871
32864
  }
32872
- const newFilesEntrypoint = [];
32873
- const newDirectoriesEntrypoint = [];
32874
- const preBuildFiles = Object.values(sourceFilesPreBuild).map(file => {
32875
- return file.fsPath;
32876
- });
32877
- // Generate a list of directories and files that weren't present
32878
- // before the entrypoint was processed by the Legacy Runtime, so
32879
- // that we can perform a cleanup later. We need to divide into files
32880
- // and directories because only cleaning up files might leave empty
32881
- // directories, and listing directories separately also speeds up the
32882
- // build because we can just delete them, which wipes all of their nested
32883
- // paths, instead of iterating through all files that should be deleted.
32884
- for (const file in sourceFilesAfterBuild) {
32885
- if (!sourceFilesPreBuild[file]) {
32886
- const path = sourceFilesAfterBuild[file].fsPath;
32887
- const dirPath = path_1.dirname(path);
32888
- // If none of the files that were present before the entrypoint
32889
- // was processed are contained within the directory we're looking
32890
- // at right now, then we know it's a newly added directory
32891
- // and it can therefore be removed later on.
32892
- const isNewDir = !preBuildFiles.some(filePath => {
32893
- return path_1.dirname(filePath).startsWith(dirPath);
32894
- });
32895
- // Check out the list of tracked directories that were
32896
- // newly added and see if one of them contains the path
32897
- // we're looking at.
32898
- const hasParentDir = newDirectoriesEntrypoint.some(dir => {
32899
- return path.startsWith(dir);
32900
- });
32901
- // If we have already tracked a directory that was newly
32902
- // added that sits above the file or directory that we're
32903
- // looking at, we don't need to add more entries to the list
32904
- // because when the parent will get removed in the future,
32905
- // all of its children (and therefore the path we're looking at)
32906
- // will automatically get removed anyways.
32907
- if (hasParentDir) {
32908
- continue;
32909
- }
32910
- if (isNewDir) {
32911
- newDirectoriesEntrypoint.push(dirPath);
32912
- }
32913
- else {
32914
- newFilesEntrypoint.push(path);
32915
- }
32916
- }
32917
- }
32865
+ // Legacy Runtimes based on interpreted languages will create a new launcher file
32866
+ // for every entrypoint, but they will create each one inside `workPath`, which means that
32867
+ // the launcher for one entrypoint will overwrite the launcher provided for the previous
32868
+ // entrypoint. That's why, above, we copy the file contents into the new destination (and
32869
+ // optionally transform them along the way), instead of linking. We then also want to remove
32870
+ // the copy origin right here, so that the `workPath` doesn't contain a useless launcher file
32871
+ // once the build has finished running.
32872
+ await fs_extra_1.default.remove(handlerFile.fsPath);
32873
+ _1.debug(`Removed temporary file "${handlerFile.fsPath}"`);
32918
32874
  const nft = `${entry}.nft.json`;
32919
32875
  const json = JSON.stringify({
32920
32876
  version: 2,
@@ -32932,16 +32888,7 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
32932
32888
  })
32933
32889
  .filter(Boolean),
32934
32890
  });
32935
- await fs_extra_1.default.ensureDir(path_1.dirname(nft));
32936
32891
  await fs_extra_1.default.writeFile(nft, json);
32937
- // Extend the list of directories and files that were created by the
32938
- // Legacy Runtime with the list of directories and files that were
32939
- // created for the entrypoint that was just processed above.
32940
- newPathsRuntime = new Set([
32941
- ...newPathsRuntime,
32942
- ...newFilesEntrypoint,
32943
- ...newDirectoriesEntrypoint,
32944
- ]);
32945
32892
  // Add an entry that will later on be added to the `functions-manifest.json`
32946
32893
  // file that is placed inside of the `.output` directory.
32947
32894
  pages[normalize_path_1.normalizePath(entryPath)] = {
@@ -32957,18 +32904,6 @@ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
32957
32904
  allowQuery: output.allowQuery,
32958
32905
  };
32959
32906
  }
32960
- // A list of all the files that were created by the Legacy Runtime,
32961
- // which we'd like to remove from the File System.
32962
- const toRemove = Array.from(newPathsRuntime).map(path => {
32963
- _1.debug(`Removing ${path} as part of cleanup`);
32964
- return fs_extra_1.default.remove(path);
32965
- });
32966
- // Once all the entrypoints have been processed, we'd like to
32967
- // remove all the files from `workPath` that originally weren't present
32968
- // before the Legacy Runtime began running, because the `workPath`
32969
- // is nowadays the directory in which the user keeps their source code, since
32970
- // we're no longer running separate parallel builds for every Legacy Runtime.
32971
- await Promise.all(toRemove);
32972
32907
  // Add any Serverless Functions that were exposed by the Legacy Runtime
32973
32908
  // to the `functions-manifest.json` file provided in `.output`.
32974
32909
  await updateFunctionsManifest({ workPath, pages });
@@ -33904,6 +33839,187 @@ function sortFilesBySegmentCount(fileA, fileB) {
33904
33839
  }
33905
33840
 
33906
33841
 
33842
+ /***/ }),
33843
+
33844
+ /***/ 1182:
33845
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
33846
+
33847
+ "use strict";
33848
+
33849
+ var __importDefault = (this && this.__importDefault) || function (mod) {
33850
+ return (mod && mod.__esModule) ? mod : { "default": mod };
33851
+ };
33852
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
33853
+ exports.detectFileSystemAPI = void 0;
33854
+ const semver_1 = __importDefault(__webpack_require__(2879));
33855
+ const _1 = __webpack_require__(2855);
33856
+ const enableFileSystemApiFrameworks = new Set(['solidstart']);
33857
+ /**
33858
+ * If the Deployment can be built with the new File System API,
33859
+ * return the new Builder. Otherwise an "Exclusion Condition"
33860
+ * was hit so return `null` builder with a `reason` for exclusion.
33861
+ */
33862
+ async function detectFileSystemAPI({ files, projectSettings, builders, vercelConfig, pkg, tag, enableFlag = false, }) {
33863
+ const framework = projectSettings.framework || '';
33864
+ const deps = Object.assign({}, pkg === null || pkg === void 0 ? void 0 : pkg.dependencies, pkg === null || pkg === void 0 ? void 0 : pkg.devDependencies);
33865
+ const plugins = Object.keys(deps).filter(dep => dep.startsWith('vercel-plugin-'));
33866
+ const hasDotOutput = Object.keys(files).some(file => file.startsWith('.output/'));
33867
+ const hasMiddleware = Boolean(files['_middleware.js'] || files['_middleware.ts']);
33868
+ const metadata = {
33869
+ plugins,
33870
+ hasDotOutput,
33871
+ hasMiddleware,
33872
+ };
33873
+ const isEnabled = enableFlag ||
33874
+ hasMiddleware ||
33875
+ hasDotOutput ||
33876
+ enableFileSystemApiFrameworks.has(framework);
33877
+ if (!isEnabled) {
33878
+ return { metadata, fsApiBuilder: null, reason: 'Flag not enabled.' };
33879
+ }
33880
+ if ((vercelConfig === null || vercelConfig === void 0 ? void 0 : vercelConfig.builds) && vercelConfig.builds.length > 0) {
33881
+ return {
33882
+ metadata,
33883
+ fsApiBuilder: null,
33884
+ reason: 'Detected `builds` in vercel.json. Please remove it in favor of CLI plugins.',
33885
+ };
33886
+ }
33887
+ if (Object.values((vercelConfig === null || vercelConfig === void 0 ? void 0 : vercelConfig.functions) || {}).some(fn => !!fn.runtime)) {
33888
+ return {
33889
+ metadata,
33890
+ fsApiBuilder: null,
33891
+ reason: 'Detected `functions.runtime` in vercel.json. Please remove it in favor of CLI plugins.',
33892
+ };
33893
+ }
33894
+ if (process.env.HUGO_VERSION) {
33895
+ return {
33896
+ metadata,
33897
+ fsApiBuilder: null,
33898
+ reason: 'Detected `HUGO_VERSION` environment variable. Please remove it.',
33899
+ };
33900
+ }
33901
+ if (process.env.ZOLA_VERSION) {
33902
+ return {
33903
+ metadata,
33904
+ fsApiBuilder: null,
33905
+ reason: 'Detected `ZOLA_VERSION` environment variable. Please remove it.',
33906
+ };
33907
+ }
33908
+ if (process.env.GUTENBERG_VERSION) {
33909
+ return {
33910
+ metadata,
33911
+ fsApiBuilder: null,
33912
+ reason: 'Detected `GUTENBERG_VERSION` environment variable. Please remove it.',
33913
+ };
33914
+ }
33915
+ const invalidBuilder = builders.find(({ use }) => {
33916
+ const valid = _1.isOfficialRuntime('go', use) ||
33917
+ _1.isOfficialRuntime('python', use) ||
33918
+ _1.isOfficialRuntime('ruby', use) ||
33919
+ _1.isOfficialRuntime('node', use) ||
33920
+ _1.isOfficialRuntime('next', use) ||
33921
+ _1.isOfficialRuntime('static', use) ||
33922
+ _1.isOfficialRuntime('static-build', use);
33923
+ return !valid;
33924
+ });
33925
+ if (invalidBuilder) {
33926
+ return {
33927
+ metadata,
33928
+ fsApiBuilder: null,
33929
+ reason: `Detected \`${invalidBuilder.use}\` in vercel.json. Please remove it in favor of CLI plugins.`,
33930
+ };
33931
+ }
33932
+ for (const lang of ['go', 'python', 'ruby']) {
33933
+ for (const { use } of builders) {
33934
+ const plugin = 'vercel-plugin-' + lang;
33935
+ if (_1.isOfficialRuntime(lang, use) && !deps[plugin]) {
33936
+ return {
33937
+ metadata,
33938
+ fsApiBuilder: null,
33939
+ reason: `Detected \`${lang}\` Serverless Function usage without plugin \`${plugin}\`. Please run \`npm i ${plugin}\`.`,
33940
+ };
33941
+ }
33942
+ }
33943
+ }
33944
+ if (framework === 'nuxtjs' ||
33945
+ framework === 'sveltekit' ||
33946
+ framework === 'redwoodjs') {
33947
+ return {
33948
+ metadata,
33949
+ fsApiBuilder: null,
33950
+ reason: `Detected framework \`${framework}\` that only supports legacy File System API. Please contact the framework author.`,
33951
+ };
33952
+ }
33953
+ if (framework === 'nextjs' && !hasDotOutput) {
33954
+ // Use the old pipeline if a custom output directory was specified for Next.js
33955
+ // because `vercel build` cannot ensure that the directory will be in the same
33956
+ // location as `.output`, which can break imports (not just nft.json files).
33957
+ if (projectSettings === null || projectSettings === void 0 ? void 0 : projectSettings.outputDirectory) {
33958
+ return {
33959
+ metadata,
33960
+ fsApiBuilder: null,
33961
+ reason: `Detected Next.js with Output Directory \`${projectSettings.outputDirectory}\` override. Please change it back to the default.`,
33962
+ };
33963
+ }
33964
+ const nextVersion = deps['next'];
33965
+ if (!nextVersion) {
33966
+ return {
33967
+ metadata,
33968
+ fsApiBuilder: null,
33969
+ reason: `Detected Next.js in Project Settings but missing \`next\` package.json dependencies. Please run \`npm i next\`.`,
33970
+ };
33971
+ }
33972
+ // TODO: Read version from lockfile instead of package.json
33973
+ if (nextVersion !== 'latest' && nextVersion !== 'canary') {
33974
+ const fixedVersion = semver_1.default.valid(semver_1.default.coerce(nextVersion) || '');
33975
+ if (!fixedVersion || !semver_1.default.gte(fixedVersion, '12.0.0')) {
33976
+ return {
33977
+ metadata,
33978
+ fsApiBuilder: null,
33979
+ reason: `Detected legacy Next.js version "${nextVersion}" in package.json. Please run \`npm i next@latest\` to upgrade.`,
33980
+ };
33981
+ }
33982
+ }
33983
+ }
33984
+ if (!hasDotOutput) {
33985
+ // TODO: Read version from lockfile instead of package.json
33986
+ const vercelCliVersion = deps['vercel'];
33987
+ if (vercelCliVersion &&
33988
+ vercelCliVersion !== 'latest' &&
33989
+ vercelCliVersion !== 'canary') {
33990
+ const fixedVersion = semver_1.default.valid(semver_1.default.coerce(vercelCliVersion) || '');
33991
+ // TODO: we might want to use '24.0.0' once its released
33992
+ if (!fixedVersion || !semver_1.default.gte(fixedVersion, '23.1.3-canary.68')) {
33993
+ return {
33994
+ metadata,
33995
+ fsApiBuilder: null,
33996
+ reason: `Detected legacy Vercel CLI version "${vercelCliVersion}" in package.json. Please run \`npm i vercel@latest\` to upgrade.`,
33997
+ };
33998
+ }
33999
+ }
34000
+ }
34001
+ const frontendBuilder = builders.find(({ use }) => _1.isOfficialRuntime('next', use) ||
34002
+ _1.isOfficialRuntime('static', use) ||
34003
+ _1.isOfficialRuntime('static-build', use));
34004
+ const config = (frontendBuilder === null || frontendBuilder === void 0 ? void 0 : frontendBuilder.config) || {};
34005
+ const withTag = tag ? `@${tag}` : '';
34006
+ const fsApiBuilder = {
34007
+ use: `@vercelruntimes/file-system-api${withTag}`,
34008
+ src: '**',
34009
+ config: {
34010
+ ...config,
34011
+ fileSystemAPI: true,
34012
+ framework: config.framework || framework || null,
34013
+ projectSettings,
34014
+ hasMiddleware,
34015
+ hasDotOutput,
34016
+ },
34017
+ };
34018
+ return { metadata, fsApiBuilder, reason: null };
34019
+ }
34020
+ exports.detectFileSystemAPI = detectFileSystemAPI;
34021
+
34022
+
33907
34023
  /***/ }),
33908
34024
 
33909
34025
  /***/ 5224:
@@ -35135,7 +35251,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35135
35251
  return (mod && mod.__esModule) ? mod : { "default": mod };
35136
35252
  };
35137
35253
  Object.defineProperty(exports, "__esModule", ({ value: true }));
35138
- exports.getInputHash = 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.getIgnoreFilter = 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;
35254
+ exports.getInputHash = exports.getPlatformEnv = exports.isStaticRuntime = exports.isOfficialRuntime = exports.updateRoutesManifest = exports.updateFunctionsManifest = exports.convertRuntimeToPlugin = exports.normalizePath = exports.readConfigFile = exports.DetectorFilesystem = exports.detectFramework = exports.detectFileSystemAPI = exports.detectApiExtensions = exports.detectApiDirectory = exports.detectOutputDirectory = exports.detectBuilders = exports.getIgnoreFilter = 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;
35139
35255
  const crypto_1 = __webpack_require__(6417);
35140
35256
  const file_blob_1 = __importDefault(__webpack_require__(2397));
35141
35257
  exports.FileBlob = file_blob_1.default;
@@ -35192,6 +35308,8 @@ Object.defineProperty(exports, "detectBuilders", ({ enumerable: true, get: funct
35192
35308
  Object.defineProperty(exports, "detectOutputDirectory", ({ enumerable: true, get: function () { return detect_builders_1.detectOutputDirectory; } }));
35193
35309
  Object.defineProperty(exports, "detectApiDirectory", ({ enumerable: true, get: function () { return detect_builders_1.detectApiDirectory; } }));
35194
35310
  Object.defineProperty(exports, "detectApiExtensions", ({ enumerable: true, get: function () { return detect_builders_1.detectApiExtensions; } }));
35311
+ var detect_file_system_api_1 = __webpack_require__(1182);
35312
+ Object.defineProperty(exports, "detectFileSystemAPI", ({ enumerable: true, get: function () { return detect_file_system_api_1.detectFileSystemAPI; } }));
35195
35313
  var detect_framework_1 = __webpack_require__(5224);
35196
35314
  Object.defineProperty(exports, "detectFramework", ({ enumerable: true, get: function () { return detect_framework_1.detectFramework; } }));
35197
35315
  var filesystem_1 = __webpack_require__(461);
package/dist/types.d.ts CHANGED
@@ -21,7 +21,7 @@ export interface Files {
21
21
  export interface Config {
22
22
  [key: string]: string | string[] | boolean | number | {
23
23
  [key: string]: string;
24
- } | BuilderFunctions | undefined;
24
+ } | BuilderFunctions | ProjectSettings | undefined | null;
25
25
  maxLambdaSize?: string;
26
26
  includeFiles?: string | string[];
27
27
  excludeFiles?: string | string[];
@@ -35,11 +35,12 @@ export interface Config {
35
35
  [key: string]: string;
36
36
  };
37
37
  functions?: BuilderFunctions;
38
+ projectSettings?: ProjectSettings;
38
39
  outputDirectory?: string;
39
40
  installCommand?: string;
40
41
  buildCommand?: string;
41
42
  devCommand?: string;
42
- framework?: string;
43
+ framework?: string | null;
43
44
  nodeVersion?: string;
44
45
  }
45
46
  export interface Meta {
@@ -305,3 +306,16 @@ export interface BuilderFunctions {
305
306
  excludeFiles?: string;
306
307
  };
307
308
  }
309
+ export interface ProjectSettings {
310
+ framework?: string | null;
311
+ devCommand?: string | null;
312
+ installCommand?: string | null;
313
+ buildCommand?: string | null;
314
+ outputDirectory?: string | null;
315
+ rootDirectory?: string | null;
316
+ createdAt?: number;
317
+ autoExposeSystemEnvs?: boolean;
318
+ sourceFilesOutsideRootDirectory?: boolean;
319
+ directoryListing?: boolean;
320
+ gitForkProtection?: boolean;
321
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/build-utils",
3
- "version": "2.12.3-canary.40",
3
+ "version": "2.12.3-canary.44",
4
4
  "license": "MIT",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",
@@ -49,5 +49,5 @@
49
49
  "typescript": "4.3.4",
50
50
  "yazl": "2.4.3"
51
51
  },
52
- "gitHead": "d31ebbabe4d9533d0e98137d76eb319b01ac8b13"
52
+ "gitHead": "ecb44812263a03803157ad258c2f0f3cd0fc83ef"
53
53
  }