@vercel/build-utils 2.12.3-canary.8 → 2.13.1-canary.1

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.
@@ -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/fs/glob.js CHANGED
@@ -8,6 +8,7 @@ const assert_1 = __importDefault(require("assert"));
8
8
  const glob_1 = __importDefault(require("glob"));
9
9
  const util_1 = require("util");
10
10
  const fs_extra_1 = require("fs-extra");
11
+ const normalize_path_1 = require("./normalize-path");
11
12
  const file_fs_ref_1 = __importDefault(require("../file-fs-ref"));
12
13
  const vanillaGlob = util_1.promisify(glob_1.default);
13
14
  async function glob(pattern, opts, mountpoint) {
@@ -31,7 +32,7 @@ async function glob(pattern, opts, mountpoint) {
31
32
  options.dot = true;
32
33
  const files = await vanillaGlob(pattern, options);
33
34
  for (const relativePath of files) {
34
- const fsPath = path_1.default.join(options.cwd, relativePath).replace(/\\/g, '/');
35
+ const fsPath = normalize_path_1.normalizePath(path_1.default.join(options.cwd, relativePath));
35
36
  let stat = options.statCache[fsPath];
36
37
  assert_1.default(stat, `statCache does not contain value for ${relativePath} (resolved to ${fsPath})`);
37
38
  const isSymlink = options.symlinks[fsPath];
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Convert Windows separators to Unix separators.
3
+ */
4
+ export declare function normalizePath(p: string): string;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizePath = void 0;
4
+ const isWin = process.platform === 'win32';
5
+ /**
6
+ * Convert Windows separators to Unix separators.
7
+ */
8
+ function normalizePath(p) {
9
+ return isWin ? p.replace(/\\/g, '/') : p;
10
+ }
11
+ exports.normalizePath = normalizePath;
@@ -57,6 +57,22 @@ export declare function getNodeVersion(destPath: string, _nodeVersion?: string,
57
57
  export declare function scanParentDirs(destPath: string, readPackageJson?: boolean): Promise<ScanParentDirsResult>;
58
58
  export declare function walkParentDirs({ base, start, filename, }: WalkParentDirsProps): Promise<string | null>;
59
59
  export declare function runNpmInstall(destPath: string, args?: string[], spawnOpts?: SpawnOptions, meta?: Meta, nodeVersion?: NodeVersion): Promise<void>;
60
+ export declare function getEnvForPackageManager({ cliType, lockfileVersion, nodeVersion, env, }: {
61
+ cliType: CliType;
62
+ lockfileVersion: number | undefined;
63
+ nodeVersion: NodeVersion | undefined;
64
+ env: {
65
+ [x: string]: string | undefined;
66
+ };
67
+ }): {
68
+ [x: string]: string | undefined;
69
+ };
70
+ export declare function runCustomInstallCommand({ destPath, installCommand, nodeVersion, spawnOpts, }: {
71
+ destPath: string;
72
+ installCommand: string;
73
+ nodeVersion: NodeVersion;
74
+ spawnOpts?: SpawnOptions;
75
+ }): Promise<void>;
60
76
  export declare function runPackageJsonScript(destPath: string, scriptNames: string | Iterable<string>, spawnOpts?: SpawnOptions): Promise<boolean>;
61
77
  export declare function runBundleInstall(destPath: string, args?: string[], spawnOpts?: SpawnOptions, meta?: Meta): Promise<void>;
62
78
  export declare function runPipInstall(destPath: string, args?: string[], spawnOpts?: SpawnOptions, meta?: Meta): Promise<void>;
@@ -3,7 +3,7 @@ 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.installDependencies = exports.getScriptName = exports.runPipInstall = exports.runBundleInstall = exports.runPackageJsonScript = exports.runNpmInstall = exports.walkParentDirs = exports.scanParentDirs = exports.getNodeVersion = exports.getSpawnOptions = exports.runShellScript = exports.getNodeBinPath = exports.execCommand = exports.spawnCommand = exports.execAsync = exports.spawnAsync = void 0;
6
+ exports.installDependencies = exports.getScriptName = exports.runPipInstall = exports.runBundleInstall = exports.runPackageJsonScript = exports.runCustomInstallCommand = exports.getEnvForPackageManager = exports.runNpmInstall = exports.walkParentDirs = exports.scanParentDirs = exports.getNodeVersion = exports.getSpawnOptions = exports.runShellScript = exports.getNodeBinPath = exports.execCommand = exports.spawnCommand = exports.execAsync = exports.spawnAsync = void 0;
7
7
  const assert_1 = __importDefault(require("assert"));
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
9
  const path_1 = __importDefault(require("path"));
@@ -221,36 +221,66 @@ async function runNpmInstall(destPath, args = [], spawnOpts, meta, nodeVersion)
221
221
  const opts = { cwd: destPath, ...spawnOpts };
222
222
  const env = opts.env ? { ...opts.env } : { ...process.env };
223
223
  delete env.NODE_ENV;
224
- opts.env = env;
224
+ opts.env = getEnvForPackageManager({
225
+ cliType,
226
+ lockfileVersion,
227
+ nodeVersion,
228
+ env,
229
+ });
225
230
  let commandArgs;
226
231
  if (cliType === 'npm') {
227
232
  opts.prettyCommand = 'npm install';
228
233
  commandArgs = args
229
234
  .filter(a => a !== '--prefer-offline')
230
235
  .concat(['install', '--no-audit', '--unsafe-perm']);
231
- // If the lockfile version is 2 or greater and the node version is less than 16 than we will force npm7 to be used
236
+ }
237
+ else {
238
+ opts.prettyCommand = 'yarn install';
239
+ commandArgs = ['install', ...args];
240
+ }
241
+ if (process.env.NPM_ONLY_PRODUCTION) {
242
+ commandArgs.push('--production');
243
+ }
244
+ return spawnAsync(cliType, commandArgs, opts);
245
+ }
246
+ exports.runNpmInstall = runNpmInstall;
247
+ function getEnvForPackageManager({ cliType, lockfileVersion, nodeVersion, env, }) {
248
+ const newEnv = { ...env };
249
+ if (cliType === 'npm') {
232
250
  if (typeof lockfileVersion === 'number' &&
233
251
  lockfileVersion >= 2 &&
234
252
  ((nodeVersion === null || nodeVersion === void 0 ? void 0 : nodeVersion.major) || 0) < 16) {
235
253
  // Ensure that npm 7 is at the beginning of the `$PATH`
236
- env.PATH = `/node16/bin-npm7:${env.PATH}`;
254
+ newEnv.PATH = `/node16/bin-npm7:${env.PATH}`;
237
255
  console.log('Detected `package-lock.json` generated by npm 7...');
238
256
  }
239
257
  }
240
258
  else {
241
- opts.prettyCommand = 'yarn install';
242
- commandArgs = ['install', ...args];
243
259
  // Yarn v2 PnP mode may be activated, so force "node-modules" linker style
244
260
  if (!env.YARN_NODE_LINKER) {
245
- env.YARN_NODE_LINKER = 'node-modules';
261
+ newEnv.YARN_NODE_LINKER = 'node-modules';
246
262
  }
247
263
  }
248
- if (process.env.NPM_ONLY_PRODUCTION) {
249
- commandArgs.push('--production');
250
- }
251
- return spawnAsync(cliType, commandArgs, opts);
264
+ return newEnv;
252
265
  }
253
- exports.runNpmInstall = runNpmInstall;
266
+ exports.getEnvForPackageManager = getEnvForPackageManager;
267
+ async function runCustomInstallCommand({ destPath, installCommand, nodeVersion, spawnOpts, }) {
268
+ console.log(`Running "install" command: \`${installCommand}\`...`);
269
+ const { cliType, lockfileVersion } = await scanParentDirs(destPath);
270
+ const env = getEnvForPackageManager({
271
+ cliType,
272
+ lockfileVersion,
273
+ nodeVersion,
274
+ env: (spawnOpts === null || spawnOpts === void 0 ? void 0 : spawnOpts.env) || {},
275
+ });
276
+ debug_1.default(`Running with $PATH:`, (env === null || env === void 0 ? void 0 : env.PATH) || '');
277
+ await execCommand(installCommand, {
278
+ ...spawnOpts,
279
+ env,
280
+ cwd: destPath,
281
+ });
282
+ }
283
+ exports.runCustomInstallCommand = runCustomInstallCommand;
254
284
  async function runPackageJsonScript(destPath, scriptNames, spawnOpts) {
255
285
  assert_1.default(path_1.default.isAbsolute(destPath));
256
286
  const { packageJson, cliType, lockfileVersion } = await scanParentDirs(destPath, true);
@@ -259,21 +289,24 @@ async function runPackageJsonScript(destPath, scriptNames, spawnOpts) {
259
289
  return false;
260
290
  debug_1.default('Running user script...');
261
291
  const runScriptTime = Date.now();
262
- const opts = { cwd: destPath, ...spawnOpts };
263
- const env = (opts.env = { ...process.env, ...opts.env });
292
+ const opts = {
293
+ cwd: destPath,
294
+ ...spawnOpts,
295
+ env: getEnvForPackageManager({
296
+ cliType,
297
+ lockfileVersion,
298
+ nodeVersion: undefined,
299
+ env: {
300
+ ...process.env,
301
+ ...spawnOpts === null || spawnOpts === void 0 ? void 0 : spawnOpts.env,
302
+ },
303
+ }),
304
+ };
264
305
  if (cliType === 'npm') {
265
306
  opts.prettyCommand = `npm run ${scriptName}`;
266
- if (typeof lockfileVersion === 'number' && lockfileVersion >= 2) {
267
- // Ensure that npm 7 is at the beginning of the `$PATH`
268
- env.PATH = `/node16/bin-npm7:${env.PATH}`;
269
- }
270
307
  }
271
308
  else {
272
309
  opts.prettyCommand = `yarn run ${scriptName}`;
273
- // Yarn v2 PnP mode may be activated, so force "node-modules" linker style
274
- if (!env.YARN_NODE_LINKER) {
275
- env.YARN_NODE_LINKER = 'node-modules';
276
- }
277
310
  }
278
311
  console.log(`Running "${opts.prettyCommand}"`);
279
312
  await spawnAsync(cliType, ['run', scriptName], opts);
@@ -0,0 +1 @@
1
+ export default function (downloadPath: string, rootDirectory?: string | undefined): Promise<(p: string) => any>;
@@ -0,0 +1,59 @@
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
+ const path_1 = __importDefault(require("path"));
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const ignore_1 = __importDefault(require("ignore"));
9
+ function isCodedError(error) {
10
+ return (error !== null &&
11
+ error !== undefined &&
12
+ error.code !== undefined);
13
+ }
14
+ function clearRelative(s) {
15
+ return s.replace(/(\n|^)\.\//g, '$1');
16
+ }
17
+ async function default_1(downloadPath, rootDirectory) {
18
+ const readFile = async (p) => {
19
+ try {
20
+ return await fs_extra_1.default.readFile(p, 'utf8');
21
+ }
22
+ catch (error) {
23
+ if (error.code === 'ENOENT' ||
24
+ (error instanceof Error && error.message.includes('ENOENT'))) {
25
+ return undefined;
26
+ }
27
+ throw error;
28
+ }
29
+ };
30
+ const vercelIgnorePath = path_1.default.join(downloadPath, rootDirectory || '', '.vercelignore');
31
+ const nowIgnorePath = path_1.default.join(downloadPath, rootDirectory || '', '.nowignore');
32
+ const ignoreContents = [];
33
+ try {
34
+ ignoreContents.push(...(await Promise.all([readFile(vercelIgnorePath), readFile(nowIgnorePath)])).filter(Boolean));
35
+ }
36
+ catch (error) {
37
+ if (isCodedError(error) && error.code === 'ENOTDIR') {
38
+ console.log(`Warning: Cannot read ignore file from ${vercelIgnorePath}`);
39
+ }
40
+ else {
41
+ throw error;
42
+ }
43
+ }
44
+ if (ignoreContents.length === 2) {
45
+ throw new Error('Cannot use both a `.vercelignore` and `.nowignore` file. Please delete the `.nowignore` file.');
46
+ }
47
+ if (ignoreContents.length === 0) {
48
+ return () => false;
49
+ }
50
+ const ignoreFilter = ignore_1.default().add(clearRelative(ignoreContents[0]));
51
+ return function (p) {
52
+ // we should not ignore now.json and vercel.json if it asked to.
53
+ // we depend on these files for building the app with sourceless
54
+ if (p === 'now.json' || p === 'vercel.json')
55
+ return false;
56
+ return ignoreFilter.test(p).ignored;
57
+ };
58
+ }
59
+ exports.default = default_1;
package/dist/index.d.ts CHANGED
@@ -7,16 +7,20 @@ import download, { DownloadedFiles, isSymbolicLink } from './fs/download';
7
7
  import getWriteableDirectory from './fs/get-writable-directory';
8
8
  import glob, { GlobOptions } from './fs/glob';
9
9
  import rename from './fs/rename';
10
- import { execAsync, spawnAsync, execCommand, spawnCommand, walkParentDirs, getScriptName, installDependencies, runPackageJsonScript, runNpmInstall, runBundleInstall, runPipInstall, runShellScript, getNodeVersion, getSpawnOptions, getNodeBinPath, scanParentDirs } from './fs/run-user-scripts';
10
+ import { execAsync, spawnAsync, execCommand, spawnCommand, walkParentDirs, getScriptName, installDependencies, runPackageJsonScript, runNpmInstall, runBundleInstall, runPipInstall, runShellScript, runCustomInstallCommand, getEnvForPackageManager, getNodeVersion, getSpawnOptions, getNodeBinPath, scanParentDirs } from './fs/run-user-scripts';
11
11
  import { getLatestNodeVersion, getDiscontinuedNodeVersions } from './fs/node-version';
12
12
  import streamToBuffer from './fs/stream-to-buffer';
13
13
  import shouldServe from './should-serve';
14
14
  import debug from './debug';
15
- 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, };
15
+ import getIgnoreFilter from './get-ignore-filter';
16
+ 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, runCustomInstallCommand, getEnvForPackageManager, getNodeVersion, getLatestNodeVersion, getDiscontinuedNodeVersions, getSpawnOptions, streamToBuffer, shouldServe, debug, isSymbolicLink, getLambdaOptionsFromFunction, scanParentDirs, getIgnoreFilter, };
16
17
  export { detectBuilders, detectOutputDirectory, detectApiDirectory, detectApiExtensions, } from './detect-builders';
18
+ export { detectFileSystemAPI } from './detect-file-system-api';
17
19
  export { detectFramework } from './detect-framework';
18
20
  export { DetectorFilesystem } from './detectors/filesystem';
19
21
  export { readConfigFile } from './fs/read-config-file';
22
+ export { normalizePath } from './fs/normalize-path';
23
+ export { _experimental_convertRuntimeToPlugin, _experimental_updateFunctionsManifest, _experimental_updateRoutesManifest, } from './convert-runtime-to-plugin';
20
24
  export * from './schemas';
21
25
  export * from './types';
22
26
  export * from './errors';