@vercel/build-utils 2.12.3-canary.25 → 2.12.3-canary.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,29 +1,21 @@
1
1
  import { Lambda } from './lambda';
2
- import type { BuilderFunctions, BuildOptions } from './types';
2
+ import type { BuildOptions } from './types';
3
3
  /**
4
4
  * Convert legacy Runtime to a Plugin.
5
5
  * @param buildRuntime - a legacy build() function from a Runtime
6
+ * @param packageName - the name of the package, for example `vercel-plugin-python`
6
7
  * @param ext - the file extension, for example `.py`
7
8
  */
8
9
  export declare function convertRuntimeToPlugin(buildRuntime: (options: BuildOptions) => Promise<{
9
10
  output: Lambda;
10
- }>, ext: string): ({ vercelConfig, workPath, }: {
11
- vercelConfig: {
12
- functions?: BuilderFunctions;
13
- regions?: string[];
14
- };
11
+ }>, packageName: string, ext: string): ({ workPath }: {
15
12
  workPath: string;
16
13
  }) => Promise<void>;
17
14
  /**
18
15
  * If `.output/functions-manifest.json` exists, append to the pages
19
- * property. Otherwise write a new file. This will also read `vercel.json`
20
- * and apply relevant `functions` property config.
16
+ * property. Otherwise write a new file.
21
17
  */
22
- export declare function updateFunctionsManifest({ vercelConfig, workPath, pages, }: {
23
- vercelConfig: {
24
- functions?: BuilderFunctions;
25
- regions?: string[];
26
- };
18
+ export declare function updateFunctionsManifest({ workPath, pages, }: {
27
19
  workPath: string;
28
20
  pages: {
29
21
  [key: string]: any;
@@ -9,34 +9,65 @@ 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
11
  const lambda_1 = require("./lambda");
12
- const minimatch_1 = __importDefault(require("minimatch"));
12
+ const _1 = require(".");
13
+ // `.output` was already created by the Build Command, so we have
14
+ // to ensure its contents don't get bundled into the Lambda. Similarily,
15
+ // we don't want to bundle anything from `.vercel` either. Lastly,
16
+ // Builders/Runtimes didn't have `vercel.json` or `now.json`.
17
+ const ignoredPaths = ['.output', '.vercel', 'vercel.json', 'now.json'];
18
+ const shouldIgnorePath = (file, ignoreFilter, ignoreFile) => {
19
+ const isNative = ignoredPaths.some(item => {
20
+ return file.startsWith(item);
21
+ });
22
+ if (!ignoreFile) {
23
+ return isNative;
24
+ }
25
+ return isNative || ignoreFilter(file);
26
+ };
13
27
  /**
14
28
  * Convert legacy Runtime to a Plugin.
15
29
  * @param buildRuntime - a legacy build() function from a Runtime
30
+ * @param packageName - the name of the package, for example `vercel-plugin-python`
16
31
  * @param ext - the file extension, for example `.py`
17
32
  */
18
- function convertRuntimeToPlugin(buildRuntime, ext) {
33
+ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
19
34
  // This `build()` signature should match `plugin.build()` signature in `vercel build`.
20
- return async function build({ vercelConfig, workPath, }) {
35
+ return async function build({ workPath }) {
21
36
  const opts = { cwd: workPath };
22
37
  const files = await glob_1.default('**', opts);
23
- delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
24
- const entrypoints = await glob_1.default(`api/**/*${ext}`, opts);
38
+ // We also don't want to provide any files to Runtimes that were ignored
39
+ // through `.vercelignore` or `.nowignore`, because the Build Step does the same.
40
+ const ignoreFilter = await _1.getIgnoreFilter(workPath);
41
+ // We're not passing this as an `ignore` filter to the `glob` function above,
42
+ // so that we can re-use exactly the same `getIgnoreFilter` method that the
43
+ // Build Step uses (literally the same code). Note that this exclusion only applies
44
+ // when deploying. Locally, another exclusion further below is needed.
45
+ for (const file in files) {
46
+ if (shouldIgnorePath(file, ignoreFilter, true)) {
47
+ delete files[file];
48
+ }
49
+ }
50
+ const entrypointPattern = `api/**/*${ext}`;
51
+ const entrypoints = await glob_1.default(entrypointPattern, opts);
25
52
  const pages = {};
26
- const { functions = {} } = vercelConfig;
27
- const traceDir = path_1.join(workPath, '.output', 'runtime-traced-files');
53
+ const pluginName = packageName.replace('vercel-plugin-', '');
54
+ const traceDir = path_1.join(workPath, `.output`, `inputs`,
55
+ // Legacy Runtimes can only provide API Routes, so that's
56
+ // why we can use this prefix for all of them. Here, we have to
57
+ // make sure to not use a cryptic hash name, because people
58
+ // need to be able to easily inspect the output.
59
+ `api-routes-${pluginName}`);
28
60
  await fs_extra_1.default.ensureDir(traceDir);
29
61
  for (const entrypoint of Object.keys(entrypoints)) {
30
- const key = Object.keys(functions).find(src => src === entrypoint || minimatch_1.default(entrypoint, src)) || '';
31
- const config = functions[key] || {};
32
62
  const { output } = await buildRuntime({
33
63
  files,
34
64
  entrypoint,
35
65
  workPath,
36
66
  config: {
37
67
  zeroConfig: true,
38
- includeFiles: config.includeFiles,
39
- excludeFiles: config.excludeFiles,
68
+ },
69
+ meta: {
70
+ avoidTopLevelInstall: true,
40
71
  },
41
72
  });
42
73
  pages[entrypoint] = {
@@ -46,10 +77,18 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
46
77
  maxDuration: output.maxDuration,
47
78
  environment: output.environment,
48
79
  allowQuery: output.allowQuery,
49
- //regions: output.regions,
50
80
  };
51
81
  // @ts-ignore This symbol is a private API
52
82
  const lambdaFiles = output[lambda_1.FILES_SYMBOL];
83
+ // When deploying, the `files` that are passed to the Legacy Runtimes already
84
+ // have certain files that are ignored stripped, but locally, that list of
85
+ // files isn't used by the Legacy Runtimes, so we need to apply the filters
86
+ // to the outputs that they are returning instead.
87
+ for (const file in lambdaFiles) {
88
+ if (shouldIgnorePath(file, ignoreFilter, false)) {
89
+ delete files[file];
90
+ }
91
+ }
53
92
  const entry = path_1.join(workPath, '.output', 'server', 'pages', entrypoint);
54
93
  await fs_extra_1.default.ensureDir(path_1.dirname(entry));
55
94
  await linkOrCopy(files[entrypoint].fsPath, entry);
@@ -79,7 +118,7 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
79
118
  await fs_extra_1.default.ensureDir(path_1.dirname(nft));
80
119
  await fs_extra_1.default.writeFile(nft, json);
81
120
  }
82
- await updateFunctionsManifest({ vercelConfig, workPath, pages });
121
+ await updateFunctionsManifest({ workPath, pages });
83
122
  };
84
123
  }
85
124
  exports.convertRuntimeToPlugin = convertRuntimeToPlugin;
@@ -107,10 +146,9 @@ async function readJson(filePath) {
107
146
  }
108
147
  /**
109
148
  * If `.output/functions-manifest.json` exists, append to the pages
110
- * property. Otherwise write a new file. This will also read `vercel.json`
111
- * and apply relevant `functions` property config.
149
+ * property. Otherwise write a new file.
112
150
  */
113
- async function updateFunctionsManifest({ vercelConfig, workPath, pages, }) {
151
+ async function updateFunctionsManifest({ workPath, pages, }) {
114
152
  const functionsManifestPath = path_1.join(workPath, '.output', 'functions-manifest.json');
115
153
  const functionsManifest = await readJson(functionsManifestPath);
116
154
  if (!functionsManifest.version)
@@ -118,16 +156,7 @@ async function updateFunctionsManifest({ vercelConfig, workPath, pages, }) {
118
156
  if (!functionsManifest.pages)
119
157
  functionsManifest.pages = {};
120
158
  for (const [pageKey, pageConfig] of Object.entries(pages)) {
121
- const fnConfig = await lambda_1.getLambdaOptionsFromFunction({
122
- sourceFile: pageKey,
123
- config: vercelConfig,
124
- });
125
- functionsManifest.pages[pageKey] = {
126
- ...pageConfig,
127
- memory: fnConfig.memory || pageConfig.memory,
128
- maxDuration: fnConfig.maxDuration || pageConfig.maxDuration,
129
- regions: vercelConfig.regions || pageConfig.regions,
130
- };
159
+ functionsManifest.pages[pageKey] = { ...pageConfig };
131
160
  }
132
161
  await fs_extra_1.default.writeFile(functionsManifestPath, JSON.stringify(functionsManifest));
133
162
  }
@@ -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
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import FileBlob from './file-blob';
2
3
  import FileFsRef from './file-fs-ref';
3
4
  import FileRef from './file-ref';
@@ -12,7 +13,8 @@ import { getLatestNodeVersion, getDiscontinuedNodeVersions } from './fs/node-ver
12
13
  import streamToBuffer from './fs/stream-to-buffer';
13
14
  import shouldServe from './should-serve';
14
15
  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, };
16
+ import getIgnoreFilter from './get-ignore-filter';
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, };
16
18
  export { detectBuilders, detectOutputDirectory, detectApiDirectory, detectApiExtensions, } from './detect-builders';
17
19
  export { detectFramework } from './detect-framework';
18
20
  export { DetectorFilesystem } from './detectors/filesystem';
@@ -32,3 +34,8 @@ export declare const isStaticRuntime: (name?: string | undefined) => boolean;
32
34
  * Throws an error if *both* env vars are defined.
33
35
  */
34
36
  export declare const getPlatformEnv: (name: string) => string | undefined;
37
+ /**
38
+ * Helper function for generating file or directories names in `.output/inputs`
39
+ * for dependencies of files provided to the File System API.
40
+ */
41
+ export declare const getInputHash: (source: Buffer | string) => string;
package/dist/index.js CHANGED
@@ -7657,6 +7657,476 @@ IconvLiteDecoderStream.prototype.collect = function(cb) {
7657
7657
 
7658
7658
 
7659
7659
 
7660
+ /***/ }),
7661
+
7662
+ /***/ 3556:
7663
+ /***/ ((module) => {
7664
+
7665
+ // A simple implementation of make-array
7666
+ function make_array (subject) {
7667
+ return Array.isArray(subject)
7668
+ ? subject
7669
+ : [subject]
7670
+ }
7671
+
7672
+ const REGEX_BLANK_LINE = /^\s+$/
7673
+ const REGEX_LEADING_EXCAPED_EXCLAMATION = /^\\!/
7674
+ const REGEX_LEADING_EXCAPED_HASH = /^\\#/
7675
+ const SLASH = '/'
7676
+ const KEY_IGNORE = typeof Symbol !== 'undefined'
7677
+ ? Symbol.for('node-ignore')
7678
+ /* istanbul ignore next */
7679
+ : 'node-ignore'
7680
+
7681
+ const define = (object, key, value) =>
7682
+ Object.defineProperty(object, key, {value})
7683
+
7684
+ const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g
7685
+
7686
+ // Sanitize the range of a regular expression
7687
+ // The cases are complicated, see test cases for details
7688
+ const sanitizeRange = range => range.replace(
7689
+ REGEX_REGEXP_RANGE,
7690
+ (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
7691
+ ? match
7692
+ // Invalid range (out of order) which is ok for gitignore rules but
7693
+ // fatal for JavaScript regular expression, so eliminate it.
7694
+ : ''
7695
+ )
7696
+
7697
+ // > If the pattern ends with a slash,
7698
+ // > it is removed for the purpose of the following description,
7699
+ // > but it would only find a match with a directory.
7700
+ // > In other words, foo/ will match a directory foo and paths underneath it,
7701
+ // > but will not match a regular file or a symbolic link foo
7702
+ // > (this is consistent with the way how pathspec works in general in Git).
7703
+ // '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
7704
+ // -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
7705
+ // you could use option `mark: true` with `glob`
7706
+
7707
+ // '`foo/`' should not continue with the '`..`'
7708
+ const DEFAULT_REPLACER_PREFIX = [
7709
+
7710
+ // > Trailing spaces are ignored unless they are quoted with backslash ("\")
7711
+ [
7712
+ // (a\ ) -> (a )
7713
+ // (a ) -> (a)
7714
+ // (a \ ) -> (a )
7715
+ /\\?\s+$/,
7716
+ match => match.indexOf('\\') === 0
7717
+ ? ' '
7718
+ : ''
7719
+ ],
7720
+
7721
+ // replace (\ ) with ' '
7722
+ [
7723
+ /\\\s/g,
7724
+ () => ' '
7725
+ ],
7726
+
7727
+ // Escape metacharacters
7728
+ // which is written down by users but means special for regular expressions.
7729
+
7730
+ // > There are 12 characters with special meanings:
7731
+ // > - the backslash \,
7732
+ // > - the caret ^,
7733
+ // > - the dollar sign $,
7734
+ // > - the period or dot .,
7735
+ // > - the vertical bar or pipe symbol |,
7736
+ // > - the question mark ?,
7737
+ // > - the asterisk or star *,
7738
+ // > - the plus sign +,
7739
+ // > - the opening parenthesis (,
7740
+ // > - the closing parenthesis ),
7741
+ // > - and the opening square bracket [,
7742
+ // > - the opening curly brace {,
7743
+ // > These special characters are often called "metacharacters".
7744
+ [
7745
+ /[\\^$.|*+(){]/g,
7746
+ match => `\\${match}`
7747
+ ],
7748
+
7749
+ [
7750
+ // > [abc] matches any character inside the brackets
7751
+ // > (in this case a, b, or c);
7752
+ /\[([^\]/]*)($|\])/g,
7753
+ (match, p1, p2) => p2 === ']'
7754
+ ? `[${sanitizeRange(p1)}]`
7755
+ : `\\${match}`
7756
+ ],
7757
+
7758
+ [
7759
+ // > a question mark (?) matches a single character
7760
+ /(?!\\)\?/g,
7761
+ () => '[^/]'
7762
+ ],
7763
+
7764
+ // leading slash
7765
+ [
7766
+
7767
+ // > A leading slash matches the beginning of the pathname.
7768
+ // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
7769
+ // A leading slash matches the beginning of the pathname
7770
+ /^\//,
7771
+ () => '^'
7772
+ ],
7773
+
7774
+ // replace special metacharacter slash after the leading slash
7775
+ [
7776
+ /\//g,
7777
+ () => '\\/'
7778
+ ],
7779
+
7780
+ [
7781
+ // > A leading "**" followed by a slash means match in all directories.
7782
+ // > For example, "**/foo" matches file or directory "foo" anywhere,
7783
+ // > the same as pattern "foo".
7784
+ // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
7785
+ // > under directory "foo".
7786
+ // Notice that the '*'s have been replaced as '\\*'
7787
+ /^\^*\\\*\\\*\\\//,
7788
+
7789
+ // '**/foo' <-> 'foo'
7790
+ () => '^(?:.*\\/)?'
7791
+ ]
7792
+ ]
7793
+
7794
+ const DEFAULT_REPLACER_SUFFIX = [
7795
+ // starting
7796
+ [
7797
+ // there will be no leading '/'
7798
+ // (which has been replaced by section "leading slash")
7799
+ // If starts with '**', adding a '^' to the regular expression also works
7800
+ /^(?=[^^])/,
7801
+ function startingReplacer () {
7802
+ return !/\/(?!$)/.test(this)
7803
+ // > If the pattern does not contain a slash /,
7804
+ // > Git treats it as a shell glob pattern
7805
+ // Actually, if there is only a trailing slash,
7806
+ // git also treats it as a shell glob pattern
7807
+ ? '(?:^|\\/)'
7808
+
7809
+ // > Otherwise, Git treats the pattern as a shell glob suitable for
7810
+ // > consumption by fnmatch(3)
7811
+ : '^'
7812
+ }
7813
+ ],
7814
+
7815
+ // two globstars
7816
+ [
7817
+ // Use lookahead assertions so that we could match more than one `'/**'`
7818
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
7819
+
7820
+ // Zero, one or several directories
7821
+ // should not use '*', or it will be replaced by the next replacer
7822
+
7823
+ // Check if it is not the last `'/**'`
7824
+ (match, index, str) => index + 6 < str.length
7825
+
7826
+ // case: /**/
7827
+ // > A slash followed by two consecutive asterisks then a slash matches
7828
+ // > zero or more directories.
7829
+ // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
7830
+ // '/**/'
7831
+ ? '(?:\\/[^\\/]+)*'
7832
+
7833
+ // case: /**
7834
+ // > A trailing `"/**"` matches everything inside.
7835
+
7836
+ // #21: everything inside but it should not include the current folder
7837
+ : '\\/.+'
7838
+ ],
7839
+
7840
+ // intermediate wildcards
7841
+ [
7842
+ // Never replace escaped '*'
7843
+ // ignore rule '\*' will match the path '*'
7844
+
7845
+ // 'abc.*/' -> go
7846
+ // 'abc.*' -> skip this rule
7847
+ /(^|[^\\]+)\\\*(?=.+)/g,
7848
+
7849
+ // '*.js' matches '.js'
7850
+ // '*.js' doesn't match 'abc'
7851
+ (match, p1) => `${p1}[^\\/]*`
7852
+ ],
7853
+
7854
+ // trailing wildcard
7855
+ [
7856
+ /(\^|\\\/)?\\\*$/,
7857
+ (match, p1) => {
7858
+ const prefix = p1
7859
+ // '\^':
7860
+ // '/*' does not match ''
7861
+ // '/*' does not match everything
7862
+
7863
+ // '\\\/':
7864
+ // 'abc/*' does not match 'abc/'
7865
+ ? `${p1}[^/]+`
7866
+
7867
+ // 'a*' matches 'a'
7868
+ // 'a*' matches 'aa'
7869
+ : '[^/]*'
7870
+
7871
+ return `${prefix}(?=$|\\/$)`
7872
+ }
7873
+ ],
7874
+
7875
+ [
7876
+ // unescape
7877
+ /\\\\\\/g,
7878
+ () => '\\'
7879
+ ]
7880
+ ]
7881
+
7882
+ const POSITIVE_REPLACERS = [
7883
+ ...DEFAULT_REPLACER_PREFIX,
7884
+
7885
+ // 'f'
7886
+ // matches
7887
+ // - /f(end)
7888
+ // - /f/
7889
+ // - (start)f(end)
7890
+ // - (start)f/
7891
+ // doesn't match
7892
+ // - oof
7893
+ // - foo
7894
+ // pseudo:
7895
+ // -> (^|/)f(/|$)
7896
+
7897
+ // ending
7898
+ [
7899
+ // 'js' will not match 'js.'
7900
+ // 'ab' will not match 'abc'
7901
+ /(?:[^*/])$/,
7902
+
7903
+ // 'js*' will not match 'a.js'
7904
+ // 'js/' will not match 'a.js'
7905
+ // 'js' will match 'a.js' and 'a.js/'
7906
+ match => `${match}(?=$|\\/)`
7907
+ ],
7908
+
7909
+ ...DEFAULT_REPLACER_SUFFIX
7910
+ ]
7911
+
7912
+ const NEGATIVE_REPLACERS = [
7913
+ ...DEFAULT_REPLACER_PREFIX,
7914
+
7915
+ // #24, #38
7916
+ // The MISSING rule of [gitignore docs](https://git-scm.com/docs/gitignore)
7917
+ // A negative pattern without a trailing wildcard should not
7918
+ // re-include the things inside that directory.
7919
+
7920
+ // eg:
7921
+ // ['node_modules/*', '!node_modules']
7922
+ // should ignore `node_modules/a.js`
7923
+ [
7924
+ /(?:[^*])$/,
7925
+ match => `${match}(?=$|\\/$)`
7926
+ ],
7927
+
7928
+ ...DEFAULT_REPLACER_SUFFIX
7929
+ ]
7930
+
7931
+ // A simple cache, because an ignore rule only has only one certain meaning
7932
+ const cache = Object.create(null)
7933
+
7934
+ // @param {pattern}
7935
+ const make_regex = (pattern, negative, ignorecase) => {
7936
+ const r = cache[pattern]
7937
+ if (r) {
7938
+ return r
7939
+ }
7940
+
7941
+ const replacers = negative
7942
+ ? NEGATIVE_REPLACERS
7943
+ : POSITIVE_REPLACERS
7944
+
7945
+ const source = replacers.reduce(
7946
+ (prev, current) => prev.replace(current[0], current[1].bind(pattern)),
7947
+ pattern
7948
+ )
7949
+
7950
+ return cache[pattern] = ignorecase
7951
+ ? new RegExp(source, 'i')
7952
+ : new RegExp(source)
7953
+ }
7954
+
7955
+ // > A blank line matches no files, so it can serve as a separator for readability.
7956
+ const checkPattern = pattern => pattern
7957
+ && typeof pattern === 'string'
7958
+ && !REGEX_BLANK_LINE.test(pattern)
7959
+
7960
+ // > A line starting with # serves as a comment.
7961
+ && pattern.indexOf('#') !== 0
7962
+
7963
+ const createRule = (pattern, ignorecase) => {
7964
+ const origin = pattern
7965
+ let negative = false
7966
+
7967
+ // > An optional prefix "!" which negates the pattern;
7968
+ if (pattern.indexOf('!') === 0) {
7969
+ negative = true
7970
+ pattern = pattern.substr(1)
7971
+ }
7972
+
7973
+ pattern = pattern
7974
+ // > Put a backslash ("\") in front of the first "!" for patterns that
7975
+ // > begin with a literal "!", for example, `"\!important!.txt"`.
7976
+ .replace(REGEX_LEADING_EXCAPED_EXCLAMATION, '!')
7977
+ // > Put a backslash ("\") in front of the first hash for patterns that
7978
+ // > begin with a hash.
7979
+ .replace(REGEX_LEADING_EXCAPED_HASH, '#')
7980
+
7981
+ const regex = make_regex(pattern, negative, ignorecase)
7982
+
7983
+ return {
7984
+ origin,
7985
+ pattern,
7986
+ negative,
7987
+ regex
7988
+ }
7989
+ }
7990
+
7991
+ class IgnoreBase {
7992
+ constructor ({
7993
+ ignorecase = true
7994
+ } = {}) {
7995
+ this._rules = []
7996
+ this._ignorecase = ignorecase
7997
+ define(this, KEY_IGNORE, true)
7998
+ this._initCache()
7999
+ }
8000
+
8001
+ _initCache () {
8002
+ this._cache = Object.create(null)
8003
+ }
8004
+
8005
+ // @param {Array.<string>|string|Ignore} pattern
8006
+ add (pattern) {
8007
+ this._added = false
8008
+
8009
+ if (typeof pattern === 'string') {
8010
+ pattern = pattern.split(/\r?\n/g)
8011
+ }
8012
+
8013
+ make_array(pattern).forEach(this._addPattern, this)
8014
+
8015
+ // Some rules have just added to the ignore,
8016
+ // making the behavior changed.
8017
+ if (this._added) {
8018
+ this._initCache()
8019
+ }
8020
+
8021
+ return this
8022
+ }
8023
+
8024
+ // legacy
8025
+ addPattern (pattern) {
8026
+ return this.add(pattern)
8027
+ }
8028
+
8029
+ _addPattern (pattern) {
8030
+ // #32
8031
+ if (pattern && pattern[KEY_IGNORE]) {
8032
+ this._rules = this._rules.concat(pattern._rules)
8033
+ this._added = true
8034
+ return
8035
+ }
8036
+
8037
+ if (checkPattern(pattern)) {
8038
+ const rule = createRule(pattern, this._ignorecase)
8039
+ this._added = true
8040
+ this._rules.push(rule)
8041
+ }
8042
+ }
8043
+
8044
+ filter (paths) {
8045
+ return make_array(paths).filter(path => this._filter(path))
8046
+ }
8047
+
8048
+ createFilter () {
8049
+ return path => this._filter(path)
8050
+ }
8051
+
8052
+ ignores (path) {
8053
+ return !this._filter(path)
8054
+ }
8055
+
8056
+ // @returns `Boolean` true if the `path` is NOT ignored
8057
+ _filter (path, slices) {
8058
+ if (!path) {
8059
+ return false
8060
+ }
8061
+
8062
+ if (path in this._cache) {
8063
+ return this._cache[path]
8064
+ }
8065
+
8066
+ if (!slices) {
8067
+ // path/to/a.js
8068
+ // ['path', 'to', 'a.js']
8069
+ slices = path.split(SLASH)
8070
+ }
8071
+
8072
+ slices.pop()
8073
+
8074
+ return this._cache[path] = slices.length
8075
+ // > It is not possible to re-include a file if a parent directory of
8076
+ // > that file is excluded.
8077
+ // If the path contains a parent directory, check the parent first
8078
+ ? this._filter(slices.join(SLASH) + SLASH, slices)
8079
+ && this._test(path)
8080
+
8081
+ // Or only test the path
8082
+ : this._test(path)
8083
+ }
8084
+
8085
+ // @returns {Boolean} true if a file is NOT ignored
8086
+ _test (path) {
8087
+ // Explicitly define variable type by setting matched to `0`
8088
+ let matched = 0
8089
+
8090
+ this._rules.forEach(rule => {
8091
+ // if matched = true, then we only test negative rules
8092
+ // if matched = false, then we test non-negative rules
8093
+ if (!(matched ^ rule.negative)) {
8094
+ matched = rule.negative ^ rule.regex.test(path)
8095
+ }
8096
+ })
8097
+
8098
+ return !matched
8099
+ }
8100
+ }
8101
+
8102
+ // Windows
8103
+ // --------------------------------------------------------------
8104
+ /* istanbul ignore if */
8105
+ if (
8106
+ // Detect `process` so that it can run in browsers.
8107
+ typeof process !== 'undefined'
8108
+ && (
8109
+ process.env && process.env.IGNORE_TEST_WIN32
8110
+ || process.platform === 'win32'
8111
+ )
8112
+ ) {
8113
+ const filter = IgnoreBase.prototype._filter
8114
+
8115
+ /* eslint no-control-regex: "off" */
8116
+ const make_posix = str => /^\\\\\?\\/.test(str)
8117
+ || /[^\x00-\x80]+/.test(str)
8118
+ ? str
8119
+ : str.replace(/\\/g, '/')
8120
+
8121
+ IgnoreBase.prototype._filter = function filterWin32 (path, slices) {
8122
+ path = make_posix(path)
8123
+ return filter.call(this, path, slices)
8124
+ }
8125
+ }
8126
+
8127
+ module.exports = options => new IgnoreBase(options)
8128
+
8129
+
7660
8130
  /***/ }),
7661
8131
 
7662
8132
  /***/ 9442:
@@ -32286,34 +32756,65 @@ const path_1 = __webpack_require__(5622);
32286
32756
  const glob_1 = __importDefault(__webpack_require__(4240));
32287
32757
  const normalize_path_1 = __webpack_require__(6261);
32288
32758
  const lambda_1 = __webpack_require__(6721);
32289
- const minimatch_1 = __importDefault(__webpack_require__(9566));
32759
+ const _1 = __webpack_require__(2855);
32760
+ // `.output` was already created by the Build Command, so we have
32761
+ // to ensure its contents don't get bundled into the Lambda. Similarily,
32762
+ // we don't want to bundle anything from `.vercel` either. Lastly,
32763
+ // Builders/Runtimes didn't have `vercel.json` or `now.json`.
32764
+ const ignoredPaths = ['.output', '.vercel', 'vercel.json', 'now.json'];
32765
+ const shouldIgnorePath = (file, ignoreFilter, ignoreFile) => {
32766
+ const isNative = ignoredPaths.some(item => {
32767
+ return file.startsWith(item);
32768
+ });
32769
+ if (!ignoreFile) {
32770
+ return isNative;
32771
+ }
32772
+ return isNative || ignoreFilter(file);
32773
+ };
32290
32774
  /**
32291
32775
  * Convert legacy Runtime to a Plugin.
32292
32776
  * @param buildRuntime - a legacy build() function from a Runtime
32777
+ * @param packageName - the name of the package, for example `vercel-plugin-python`
32293
32778
  * @param ext - the file extension, for example `.py`
32294
32779
  */
32295
- function convertRuntimeToPlugin(buildRuntime, ext) {
32780
+ function convertRuntimeToPlugin(buildRuntime, packageName, ext) {
32296
32781
  // This `build()` signature should match `plugin.build()` signature in `vercel build`.
32297
- return async function build({ vercelConfig, workPath, }) {
32782
+ return async function build({ workPath }) {
32298
32783
  const opts = { cwd: workPath };
32299
32784
  const files = await glob_1.default('**', opts);
32300
- delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
32301
- const entrypoints = await glob_1.default(`api/**/*${ext}`, opts);
32785
+ // We also don't want to provide any files to Runtimes that were ignored
32786
+ // through `.vercelignore` or `.nowignore`, because the Build Step does the same.
32787
+ const ignoreFilter = await _1.getIgnoreFilter(workPath);
32788
+ // We're not passing this as an `ignore` filter to the `glob` function above,
32789
+ // so that we can re-use exactly the same `getIgnoreFilter` method that the
32790
+ // Build Step uses (literally the same code). Note that this exclusion only applies
32791
+ // when deploying. Locally, another exclusion further below is needed.
32792
+ for (const file in files) {
32793
+ if (shouldIgnorePath(file, ignoreFilter, true)) {
32794
+ delete files[file];
32795
+ }
32796
+ }
32797
+ const entrypointPattern = `api/**/*${ext}`;
32798
+ const entrypoints = await glob_1.default(entrypointPattern, opts);
32302
32799
  const pages = {};
32303
- const { functions = {} } = vercelConfig;
32304
- const traceDir = path_1.join(workPath, '.output', 'runtime-traced-files');
32800
+ const pluginName = packageName.replace('vercel-plugin-', '');
32801
+ const traceDir = path_1.join(workPath, `.output`, `inputs`,
32802
+ // Legacy Runtimes can only provide API Routes, so that's
32803
+ // why we can use this prefix for all of them. Here, we have to
32804
+ // make sure to not use a cryptic hash name, because people
32805
+ // need to be able to easily inspect the output.
32806
+ `api-routes-${pluginName}`);
32305
32807
  await fs_extra_1.default.ensureDir(traceDir);
32306
32808
  for (const entrypoint of Object.keys(entrypoints)) {
32307
- const key = Object.keys(functions).find(src => src === entrypoint || minimatch_1.default(entrypoint, src)) || '';
32308
- const config = functions[key] || {};
32309
32809
  const { output } = await buildRuntime({
32310
32810
  files,
32311
32811
  entrypoint,
32312
32812
  workPath,
32313
32813
  config: {
32314
32814
  zeroConfig: true,
32315
- includeFiles: config.includeFiles,
32316
- excludeFiles: config.excludeFiles,
32815
+ },
32816
+ meta: {
32817
+ avoidTopLevelInstall: true,
32317
32818
  },
32318
32819
  });
32319
32820
  pages[entrypoint] = {
@@ -32323,10 +32824,18 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
32323
32824
  maxDuration: output.maxDuration,
32324
32825
  environment: output.environment,
32325
32826
  allowQuery: output.allowQuery,
32326
- //regions: output.regions,
32327
32827
  };
32328
32828
  // @ts-ignore This symbol is a private API
32329
32829
  const lambdaFiles = output[lambda_1.FILES_SYMBOL];
32830
+ // When deploying, the `files` that are passed to the Legacy Runtimes already
32831
+ // have certain files that are ignored stripped, but locally, that list of
32832
+ // files isn't used by the Legacy Runtimes, so we need to apply the filters
32833
+ // to the outputs that they are returning instead.
32834
+ for (const file in lambdaFiles) {
32835
+ if (shouldIgnorePath(file, ignoreFilter, false)) {
32836
+ delete files[file];
32837
+ }
32838
+ }
32330
32839
  const entry = path_1.join(workPath, '.output', 'server', 'pages', entrypoint);
32331
32840
  await fs_extra_1.default.ensureDir(path_1.dirname(entry));
32332
32841
  await linkOrCopy(files[entrypoint].fsPath, entry);
@@ -32356,7 +32865,7 @@ function convertRuntimeToPlugin(buildRuntime, ext) {
32356
32865
  await fs_extra_1.default.ensureDir(path_1.dirname(nft));
32357
32866
  await fs_extra_1.default.writeFile(nft, json);
32358
32867
  }
32359
- await updateFunctionsManifest({ vercelConfig, workPath, pages });
32868
+ await updateFunctionsManifest({ workPath, pages });
32360
32869
  };
32361
32870
  }
32362
32871
  exports.convertRuntimeToPlugin = convertRuntimeToPlugin;
@@ -32384,10 +32893,9 @@ async function readJson(filePath) {
32384
32893
  }
32385
32894
  /**
32386
32895
  * If `.output/functions-manifest.json` exists, append to the pages
32387
- * property. Otherwise write a new file. This will also read `vercel.json`
32388
- * and apply relevant `functions` property config.
32896
+ * property. Otherwise write a new file.
32389
32897
  */
32390
- async function updateFunctionsManifest({ vercelConfig, workPath, pages, }) {
32898
+ async function updateFunctionsManifest({ workPath, pages, }) {
32391
32899
  const functionsManifestPath = path_1.join(workPath, '.output', 'functions-manifest.json');
32392
32900
  const functionsManifest = await readJson(functionsManifestPath);
32393
32901
  if (!functionsManifest.version)
@@ -32395,16 +32903,7 @@ async function updateFunctionsManifest({ vercelConfig, workPath, pages, }) {
32395
32903
  if (!functionsManifest.pages)
32396
32904
  functionsManifest.pages = {};
32397
32905
  for (const [pageKey, pageConfig] of Object.entries(pages)) {
32398
- const fnConfig = await lambda_1.getLambdaOptionsFromFunction({
32399
- sourceFile: pageKey,
32400
- config: vercelConfig,
32401
- });
32402
- functionsManifest.pages[pageKey] = {
32403
- ...pageConfig,
32404
- memory: fnConfig.memory || pageConfig.memory,
32405
- maxDuration: fnConfig.maxDuration || pageConfig.maxDuration,
32406
- regions: vercelConfig.regions || pageConfig.regions,
32407
- };
32906
+ functionsManifest.pages[pageKey] = { ...pageConfig };
32408
32907
  }
32409
32908
  await fs_extra_1.default.writeFile(functionsManifestPath, JSON.stringify(functionsManifest));
32410
32909
  }
@@ -34440,6 +34939,73 @@ function streamToBuffer(stream) {
34440
34939
  exports.default = streamToBuffer;
34441
34940
 
34442
34941
 
34942
+ /***/ }),
34943
+
34944
+ /***/ 1148:
34945
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
34946
+
34947
+ "use strict";
34948
+
34949
+ var __importDefault = (this && this.__importDefault) || function (mod) {
34950
+ return (mod && mod.__esModule) ? mod : { "default": mod };
34951
+ };
34952
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
34953
+ const path_1 = __importDefault(__webpack_require__(5622));
34954
+ const fs_extra_1 = __importDefault(__webpack_require__(5392));
34955
+ const ignore_1 = __importDefault(__webpack_require__(3556));
34956
+ function isCodedError(error) {
34957
+ return (error !== null &&
34958
+ error !== undefined &&
34959
+ error.code !== undefined);
34960
+ }
34961
+ function clearRelative(s) {
34962
+ return s.replace(/(\n|^)\.\//g, '$1');
34963
+ }
34964
+ async function default_1(downloadPath, rootDirectory) {
34965
+ const readFile = async (p) => {
34966
+ try {
34967
+ return await fs_extra_1.default.readFile(p, 'utf8');
34968
+ }
34969
+ catch (error) {
34970
+ if (error.code === 'ENOENT' ||
34971
+ (error instanceof Error && error.message.includes('ENOENT'))) {
34972
+ return undefined;
34973
+ }
34974
+ throw error;
34975
+ }
34976
+ };
34977
+ const vercelIgnorePath = path_1.default.join(downloadPath, rootDirectory || '', '.vercelignore');
34978
+ const nowIgnorePath = path_1.default.join(downloadPath, rootDirectory || '', '.nowignore');
34979
+ const ignoreContents = [];
34980
+ try {
34981
+ ignoreContents.push(...(await Promise.all([readFile(vercelIgnorePath), readFile(nowIgnorePath)])).filter(Boolean));
34982
+ }
34983
+ catch (error) {
34984
+ if (isCodedError(error) && error.code === 'ENOTDIR') {
34985
+ console.log(`Warning: Cannot read ignore file from ${vercelIgnorePath}`);
34986
+ }
34987
+ else {
34988
+ throw error;
34989
+ }
34990
+ }
34991
+ if (ignoreContents.length === 2) {
34992
+ throw new Error('Cannot use both a `.vercelignore` and `.nowignore` file. Please delete the `.nowignore` file.');
34993
+ }
34994
+ if (ignoreContents.length === 0) {
34995
+ return () => false;
34996
+ }
34997
+ const ignoreFilter = ignore_1.default().add(clearRelative(ignoreContents[0]));
34998
+ return function (p) {
34999
+ // we should not ignore now.json and vercel.json if it asked to.
35000
+ // we depend on these files for building the app with sourceless
35001
+ if (p === 'now.json' || p === 'vercel.json')
35002
+ return false;
35003
+ return ignoreFilter.test(p).ignored;
35004
+ };
35005
+ }
35006
+ exports.default = default_1;
35007
+
35008
+
34443
35009
  /***/ }),
34444
35010
 
34445
35011
  /***/ 2855:
@@ -34473,7 +35039,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
34473
35039
  return (mod && mod.__esModule) ? mod : { "default": mod };
34474
35040
  };
34475
35041
  Object.defineProperty(exports, "__esModule", ({ value: true }));
34476
- exports.getPlatformEnv = exports.isStaticRuntime = exports.isOfficialRuntime = exports.updateRoutesManifest = exports.updateFunctionsManifest = exports.convertRuntimeToPlugin = exports.normalizePath = exports.readConfigFile = exports.DetectorFilesystem = exports.detectFramework = exports.detectApiExtensions = exports.detectApiDirectory = exports.detectOutputDirectory = exports.detectBuilders = exports.scanParentDirs = exports.getLambdaOptionsFromFunction = exports.isSymbolicLink = exports.debug = exports.shouldServe = exports.streamToBuffer = exports.getSpawnOptions = exports.getDiscontinuedNodeVersions = exports.getLatestNodeVersion = exports.getNodeVersion = exports.runShellScript = exports.runPipInstall = exports.runBundleInstall = exports.runNpmInstall = exports.getNodeBinPath = exports.walkParentDirs = exports.spawnCommand = exports.execCommand = exports.runPackageJsonScript = exports.installDependencies = exports.getScriptName = exports.spawnAsync = exports.execAsync = exports.rename = exports.glob = exports.getWriteableDirectory = exports.download = exports.Prerender = exports.createLambda = exports.Lambda = exports.FileRef = exports.FileFsRef = exports.FileBlob = void 0;
35042
+ 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;
35043
+ const crypto_1 = __webpack_require__(6417);
34477
35044
  const file_blob_1 = __importDefault(__webpack_require__(2397));
34478
35045
  exports.FileBlob = file_blob_1.default;
34479
35046
  const file_fs_ref_1 = __importDefault(__webpack_require__(9331));
@@ -34522,6 +35089,8 @@ const should_serve_1 = __importDefault(__webpack_require__(2564));
34522
35089
  exports.shouldServe = should_serve_1.default;
34523
35090
  const debug_1 = __importDefault(__webpack_require__(1868));
34524
35091
  exports.debug = debug_1.default;
35092
+ const get_ignore_filter_1 = __importDefault(__webpack_require__(1148));
35093
+ exports.getIgnoreFilter = get_ignore_filter_1.default;
34525
35094
  var detect_builders_1 = __webpack_require__(4246);
34526
35095
  Object.defineProperty(exports, "detectBuilders", ({ enumerable: true, get: function () { return detect_builders_1.detectBuilders; } }));
34527
35096
  Object.defineProperty(exports, "detectOutputDirectory", ({ enumerable: true, get: function () { return detect_builders_1.detectOutputDirectory; } }));
@@ -34581,6 +35150,14 @@ const getPlatformEnv = (name) => {
34581
35150
  return n;
34582
35151
  };
34583
35152
  exports.getPlatformEnv = getPlatformEnv;
35153
+ /**
35154
+ * Helper function for generating file or directories names in `.output/inputs`
35155
+ * for dependencies of files provided to the File System API.
35156
+ */
35157
+ const getInputHash = (source) => {
35158
+ return crypto_1.createHash('sha1').update(source).digest('hex');
35159
+ };
35160
+ exports.getInputHash = getInputHash;
34584
35161
 
34585
35162
 
34586
35163
  /***/ }),
@@ -34970,6 +35547,14 @@ module.exports = require("constants");
34970
35547
 
34971
35548
  /***/ }),
34972
35549
 
35550
+ /***/ 6417:
35551
+ /***/ ((module) => {
35552
+
35553
+ "use strict";
35554
+ module.exports = require("crypto");
35555
+
35556
+ /***/ }),
35557
+
34973
35558
  /***/ 8614:
34974
35559
  /***/ ((module) => {
34975
35560
 
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.25",
3
+ "version": "2.12.3-canary.29",
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.15",
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": "6b865ff753ad0e51b284dac173e7e32953de3298"
52
+ "gitHead": "9b3aa41f2e8d40b560969305af8cf26dcd6da8c9"
53
53
  }