@netlify/build 28.1.6 → 28.1.8
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.
- package/lib/core/build.js +1 -0
- package/lib/core/feature_flags.js +3 -0
- package/lib/error/type.js +6 -0
- package/lib/log/messages/compatibility.js +30 -1
- package/lib/plugins/manifest/main.js +4 -2
- package/lib/plugins/node_version.js +5 -15
- package/lib/plugins/options.js +19 -9
- package/lib/plugins/spawn.js +2 -2
- package/lib/utils/json.js +2 -2
- package/lib/utils/omit.js +1 -3
- package/lib/utils/package.js +14 -14
- package/package.json +5 -6
package/lib/core/build.js
CHANGED
|
@@ -245,6 +245,7 @@ const initAndRunBuild = async function ({ pluginsOptions, netlifyConfig, configO
|
|
|
245
245
|
logs,
|
|
246
246
|
debug,
|
|
247
247
|
timers: timersA,
|
|
248
|
+
featureFlags,
|
|
248
249
|
});
|
|
249
250
|
try {
|
|
250
251
|
const { stepsCount, netlifyConfig: netlifyConfigA, statuses, failedPlugins, timers: timersC, configMutations, } = await runBuild({
|
|
@@ -16,4 +16,7 @@ export const DEFAULT_FEATURE_FLAGS = {
|
|
|
16
16
|
edge_functions_cache_cli: false,
|
|
17
17
|
edge_functions_produce_eszip: false,
|
|
18
18
|
edge_functions_system_logger: false,
|
|
19
|
+
// TODO: remove this flag once rolled out to everyone
|
|
20
|
+
// FF link: https://app.launchdarkly.com/default/production/features/plugins_break_builds_with_unsupported_plugin_versions/targeting
|
|
21
|
+
plugins_break_builds_with_unsupported_plugin_versions: false,
|
|
19
22
|
};
|
package/lib/error/type.js
CHANGED
|
@@ -65,6 +65,12 @@ const TYPES = {
|
|
|
65
65
|
locationType: 'buildFail',
|
|
66
66
|
severity: 'info',
|
|
67
67
|
},
|
|
68
|
+
// User package.json sets an unsupported plugin version
|
|
69
|
+
pluginUnsupportedVersion: {
|
|
70
|
+
title: 'Unsupported plugin version detected',
|
|
71
|
+
stackType: 'none',
|
|
72
|
+
severity: 'info',
|
|
73
|
+
},
|
|
68
74
|
// `build.command` non-0 exit code
|
|
69
75
|
buildCommand: {
|
|
70
76
|
title: '"build.command" failed',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import semver from 'semver';
|
|
2
|
+
import { addErrorInfo } from '../../error/info.js';
|
|
2
3
|
import { isRuntime } from '../../utils/runtime.js';
|
|
3
4
|
import { isPreviousMajor } from '../../utils/semver.js';
|
|
4
5
|
import { getPluginOrigin } from '../description.js';
|
|
@@ -56,11 +57,15 @@ const getVersionField = function ([versionFieldName, version]) {
|
|
|
56
57
|
};
|
|
57
58
|
// Print a warning message when old versions plugins are used.
|
|
58
59
|
// This can only happen when they are installed to `package.json`.
|
|
59
|
-
|
|
60
|
+
// Also throws an error if the Next runtime is >= 4.0.0 || < 4.26.0
|
|
61
|
+
export const logOutdatedPlugins = function (logs, pluginsOptions, featureFlags) {
|
|
60
62
|
const outdatedPlugins = pluginsOptions.filter(hasOutdatedVersion).map(getOutdatedPlugin);
|
|
61
63
|
if (outdatedPlugins.length === 0) {
|
|
62
64
|
return;
|
|
63
65
|
}
|
|
66
|
+
// TODO: remove feature flag once fully rolled out
|
|
67
|
+
if (featureFlags.plugins_break_builds_with_unsupported_plugin_versions)
|
|
68
|
+
throwIfUnsupportedPluginVersion(pluginsOptions.filter(hasOutdatedVersion));
|
|
64
69
|
logWarningSubHeader(logs, 'Outdated plugins');
|
|
65
70
|
logWarningArray(logs, outdatedPlugins);
|
|
66
71
|
};
|
|
@@ -99,6 +104,30 @@ export const logIncompatiblePlugins = function (logs, pluginsOptions) {
|
|
|
99
104
|
logWarningSubHeader(logs, 'Incompatible plugins');
|
|
100
105
|
logWarningArray(logs, incompatiblePlugins);
|
|
101
106
|
};
|
|
107
|
+
// Throws an error if the Next runtime is >= 4.0.0 || < 4.26.0, otherwise returns.
|
|
108
|
+
const throwIfUnsupportedPluginVersion = function (outdatedPlugins) {
|
|
109
|
+
let packageName;
|
|
110
|
+
let version;
|
|
111
|
+
let latestVersion;
|
|
112
|
+
const nextOutdatedV4Plugin = outdatedPlugins.find((plugin) => {
|
|
113
|
+
packageName = plugin.pluginPackageJson.name;
|
|
114
|
+
version = plugin.pluginPackageJson.version;
|
|
115
|
+
latestVersion = plugin.latestVersion;
|
|
116
|
+
// https://github.com/npm/node-semver#hyphen-ranges-xyz---abc
|
|
117
|
+
// semver hyphen range is inclusive 4.0.0 - 4.25.0 is same as >= 4.0.0 || < 4.26.0;
|
|
118
|
+
return (packageName === '@netlify/plugin-nextjs' &&
|
|
119
|
+
semver.satisfies(version, '4.0.0 - 4.25.0', { includePrerelease: true }));
|
|
120
|
+
});
|
|
121
|
+
if (!nextOutdatedV4Plugin) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const error = new Error(`This site cannot be built because it is using an outdated version of the Next.js Runtime: ${packageName}@${version}.
|
|
125
|
+
Versions greater than 4.26.0 are recommended. To upgrade this plugin, please update its version in "package.json" to the
|
|
126
|
+
latest version: ${latestVersion}. If you cannot use a more recent version, please contact support at https://www.netlify.com/support
|
|
127
|
+
for guidance.`);
|
|
128
|
+
addErrorInfo(error, { type: 'pluginUnsupportedVersion' });
|
|
129
|
+
throw error;
|
|
130
|
+
};
|
|
102
131
|
const hasIncompatibleVersion = function ({ pluginPackageJson: { version }, compatibleVersion, compatWarning }) {
|
|
103
132
|
return (compatWarning !== '' &&
|
|
104
133
|
version !== undefined &&
|
|
@@ -2,8 +2,10 @@ import { addPluginLoadErrorStatus } from '../../status/load_error.js';
|
|
|
2
2
|
import { checkInputs } from './check.js';
|
|
3
3
|
import { loadManifest } from './load.js';
|
|
4
4
|
import { getManifestPath } from './path.js';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Load plugin's `manifest.yml`
|
|
7
|
+
*/
|
|
8
|
+
export const useManifest = async function ({ packageName, loadedFrom, origin, inputs, }, { pluginDir, packageDir, pluginPackageJson, pluginPackageJson: { version }, debug, }) {
|
|
7
9
|
const manifestPath = await getManifestPath({ pluginDir, packageDir, packageName });
|
|
8
10
|
try {
|
|
9
11
|
const manifest = await loadManifest({ manifestPath, packageName, pluginPackageJson, loadedFrom, origin });
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { version as currentVersion
|
|
1
|
+
import { execPath, version as currentVersion } from 'process';
|
|
2
2
|
import semver from 'semver';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// This node version is minimum required to use ESModules so if the user's preferred Node.js version is below that
|
|
4
|
+
// we have to fall back to the system node version
|
|
5
|
+
const MINIMUM_REQUIRED_NODE_VERSION = '^12.20.0 || ^14.14.0 || >=16.0.0';
|
|
5
6
|
// Local plugins and `package.json`-installed plugins use user's preferred Node.js version if higher than our minimum
|
|
6
7
|
// supported version. Else default to the system Node version.
|
|
7
8
|
// Local and programmatic builds use `@netlify/build` Node.js version, which is
|
|
@@ -13,18 +14,7 @@ export const addPluginsNodeVersion = function ({ pluginsOptions, nodePath, userN
|
|
|
13
14
|
};
|
|
14
15
|
const addPluginNodeVersion = function ({ pluginOptions, pluginOptions: { loadedFrom }, currentNodeVersion, userNodeVersion, nodePath, }) {
|
|
15
16
|
return (loadedFrom === 'local' || loadedFrom === 'package.json') &&
|
|
16
|
-
semver.satisfies(userNodeVersion,
|
|
17
|
+
semver.satisfies(userNodeVersion, MINIMUM_REQUIRED_NODE_VERSION)
|
|
17
18
|
? { ...pluginOptions, nodePath, nodeVersion: userNodeVersion }
|
|
18
19
|
: { ...pluginOptions, nodePath: execPath, nodeVersion: currentNodeVersion };
|
|
19
20
|
};
|
|
20
|
-
// Ensure Node.js version is compatible with plugin's `engines.node`
|
|
21
|
-
export const checkNodeVersion = function ({ nodeVersion, packageName, pluginPackageJson: { engines: { node: pluginNodeVersionRange } = {} } = {}, }) {
|
|
22
|
-
if (pluginNodeVersionRange && !semver.satisfies(nodeVersion, pluginNodeVersionRange)) {
|
|
23
|
-
throwUserError(`The Node.js version is ${nodeVersion} but the plugin "${packageName}" requires ${pluginNodeVersionRange}`);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
const throwUserError = function (message) {
|
|
27
|
-
const error = new Error(message);
|
|
28
|
-
addErrorInfo(error, { type: 'resolveConfig' });
|
|
29
|
-
throw error;
|
|
30
|
-
};
|
package/lib/plugins/options.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { dirname } from 'path';
|
|
2
|
+
import semver from 'semver';
|
|
3
|
+
import { addErrorInfo } from '../error/info.js';
|
|
2
4
|
import { installLocalPluginsDependencies } from '../install/local.js';
|
|
3
5
|
import { measureDuration } from '../time/main.js';
|
|
4
6
|
import { ROOT_PACKAGE_JSON } from '../utils/json.js';
|
|
5
7
|
import { getPackageJson } from '../utils/package.js';
|
|
6
8
|
import { useManifest } from './manifest/main.js';
|
|
7
|
-
import { checkNodeVersion } from './node_version.js';
|
|
8
9
|
import { resolvePluginsPath } from './resolve.js';
|
|
9
10
|
// Load core plugins and user plugins
|
|
10
11
|
const tGetPluginsOptions = async function ({ pluginsOptions, netlifyConfig: { plugins }, siteInfo, buildDir, nodePath, packageJson, userNodeVersion, mode, api, logs, debug, sendStatus, testOpts, featureFlags, }) {
|
|
@@ -34,7 +35,11 @@ export const getPluginsOptions = measureDuration(tGetPluginsOptions, 'get_plugin
|
|
|
34
35
|
const loadPluginFiles = async function ({ pluginOptions, pluginOptions: { pluginPath, nodeVersion, packageName }, debug, }) {
|
|
35
36
|
const pluginDir = dirname(pluginPath);
|
|
36
37
|
const { packageDir, packageJson: pluginPackageJson } = await getPackageJson(pluginDir);
|
|
37
|
-
|
|
38
|
+
// Ensure Node.js version is compatible with plugin's `engines.node`
|
|
39
|
+
const pluginNodeVersionRange = pluginPackageJson?.engines?.node;
|
|
40
|
+
if (pluginNodeVersionRange && !semver.satisfies(nodeVersion, pluginNodeVersionRange)) {
|
|
41
|
+
throwUserError(`The Node.js version is ${nodeVersion} but the plugin "${packageName}" requires ${pluginNodeVersionRange}`);
|
|
42
|
+
}
|
|
38
43
|
const { manifest, inputs } = await useManifest(pluginOptions, { pluginDir, packageDir, pluginPackageJson, debug });
|
|
39
44
|
return { ...pluginOptions, pluginDir, packageDir, pluginPackageJson, manifest, inputs };
|
|
40
45
|
};
|
|
@@ -45,11 +50,16 @@ const isNotRedundantCorePlugin = function (pluginOptionsA, index, pluginsOptions
|
|
|
45
50
|
return (pluginOptionsA.loadedFrom !== 'core' ||
|
|
46
51
|
pluginsOptions.every((pluginOptionsB) => pluginOptionsA.manifest.name !== pluginOptionsB.manifest.name || pluginOptionsA === pluginOptionsB));
|
|
47
52
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
53
|
+
/**
|
|
54
|
+
* Retrieve information about @netlify/build when an error happens there and not
|
|
55
|
+
* in a plugin
|
|
56
|
+
*/
|
|
57
|
+
export const getSpawnInfo = () => ({
|
|
58
|
+
plugin: { packageName: ROOT_PACKAGE_JSON.name, pluginPackageJson: ROOT_PACKAGE_JSON },
|
|
59
|
+
location: { event: 'load', packageName: ROOT_PACKAGE_JSON.name, loadedFrom: 'core', origin: 'core' },
|
|
60
|
+
});
|
|
61
|
+
const throwUserError = function (message) {
|
|
62
|
+
const error = new Error(message);
|
|
63
|
+
addErrorInfo(error, { type: 'resolveConfig' });
|
|
64
|
+
throw error;
|
|
55
65
|
};
|
package/lib/plugins/spawn.js
CHANGED
|
@@ -12,10 +12,10 @@ const CHILD_MAIN_FILE = fileURLToPath(new URL('child/main.js', import.meta.url))
|
|
|
12
12
|
// (for both security and safety reasons)
|
|
13
13
|
// - logs can be buffered which allows manipulating them for log shipping,
|
|
14
14
|
// transforming and parallel plugins
|
|
15
|
-
const tStartPlugins = async function ({ pluginsOptions, buildDir, childEnv, logs, debug }) {
|
|
15
|
+
const tStartPlugins = async function ({ pluginsOptions, buildDir, childEnv, logs, debug, featureFlags }) {
|
|
16
16
|
logRuntime(logs, pluginsOptions);
|
|
17
17
|
logLoadingPlugins(logs, pluginsOptions, debug);
|
|
18
|
-
logOutdatedPlugins(logs, pluginsOptions);
|
|
18
|
+
logOutdatedPlugins(logs, pluginsOptions, featureFlags);
|
|
19
19
|
logIncompatiblePlugins(logs, pluginsOptions);
|
|
20
20
|
const childProcesses = await Promise.all(pluginsOptions.map(({ pluginDir, nodePath }) => startPlugin({ pluginDir, nodePath, buildDir, childEnv })));
|
|
21
21
|
return { childProcesses };
|
package/lib/utils/json.js
CHANGED
|
@@ -4,12 +4,12 @@ const ROOT_PACKAGE_JSON_PATH = fileURLToPath(new URL('../../package.json', impor
|
|
|
4
4
|
// TODO: Replace with dynamic `import()` once it is supported without
|
|
5
5
|
// experimental flags
|
|
6
6
|
export const importJsonFile = async function (filePath) {
|
|
7
|
-
const fileContents = await fs.readFile(filePath);
|
|
7
|
+
const fileContents = await fs.readFile(filePath, 'utf-8');
|
|
8
8
|
return JSON.parse(fileContents);
|
|
9
9
|
};
|
|
10
10
|
const importJsonFileSync = function (filePath) {
|
|
11
11
|
// Use sync I/O so it is easier to migrate to `import()` later on
|
|
12
|
-
const fileContents = readFileSync(filePath, '
|
|
12
|
+
const fileContents = readFileSync(filePath, 'utf-8');
|
|
13
13
|
return JSON.parse(fileContents);
|
|
14
14
|
};
|
|
15
15
|
export const ROOT_PACKAGE_JSON = importJsonFileSync(ROOT_PACKAGE_JSON_PATH);
|
package/lib/utils/omit.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import filterObj from 'filter-obj';
|
|
2
2
|
// lodash.omit is 1400 lines of codes. filter-obj is much smaller and simpler.
|
|
3
|
-
export const omit =
|
|
4
|
-
return filterObj(obj, (key) => !keys.includes(key));
|
|
5
|
-
};
|
|
3
|
+
export const omit = (obj, keys) => filterObj(obj, (key) => !keys.includes(key));
|
package/lib/utils/package.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { dirname } from 'path';
|
|
2
2
|
import { readPackageUp } from 'read-pkg-up';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const packageDir = dirname(path);
|
|
11
|
-
return { packageDir, packageJson };
|
|
12
|
-
};
|
|
13
|
-
const getPackageObj = async function ({ cwd, normalize = true }) {
|
|
3
|
+
/**
|
|
4
|
+
* Retrieve `package.json` from a specific directory
|
|
5
|
+
*/
|
|
6
|
+
export const getPackageJson = async function (cwd, options = {}) {
|
|
7
|
+
const result = {
|
|
8
|
+
packageJson: {},
|
|
9
|
+
};
|
|
14
10
|
try {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
const { path, packageJson } = await readPackageUp({
|
|
12
|
+
cwd,
|
|
13
|
+
...Object.assign({ normalize: true }, options),
|
|
14
|
+
});
|
|
15
|
+
result.packageJson = packageJson;
|
|
16
|
+
result.packageDir = dirname(path);
|
|
18
17
|
}
|
|
19
18
|
catch {
|
|
20
19
|
// continue regardless error
|
|
21
20
|
}
|
|
21
|
+
return result;
|
|
22
22
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/build",
|
|
3
|
-
"version": "28.1.
|
|
3
|
+
"version": "28.1.8",
|
|
4
4
|
"description": "Netlify build module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./lib/core/main.js",
|
|
@@ -67,11 +67,11 @@
|
|
|
67
67
|
"@netlify/cache-utils": "^5.0.1",
|
|
68
68
|
"@netlify/config": "^19.1.2",
|
|
69
69
|
"@netlify/edge-bundler": "^3.0.1",
|
|
70
|
-
"@netlify/functions-utils": "^5.0.
|
|
70
|
+
"@netlify/functions-utils": "^5.0.2",
|
|
71
71
|
"@netlify/git-utils": "^5.0.1",
|
|
72
72
|
"@netlify/plugins-list": "^6.50.0",
|
|
73
73
|
"@netlify/run-utils": "^5.0.1",
|
|
74
|
-
"@netlify/zip-it-and-ship-it": "^
|
|
74
|
+
"@netlify/zip-it-and-ship-it": "^8.0.0",
|
|
75
75
|
"@sindresorhus/slugify": "^2.0.0",
|
|
76
76
|
"ajv": "^8.11.0",
|
|
77
77
|
"ajv-errors": "^3.0.0",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"supports-color": "^9.0.0",
|
|
114
114
|
"tmp-promise": "^3.0.2",
|
|
115
115
|
"ts-node": "^10.6.0",
|
|
116
|
-
"typescript": "^4.
|
|
116
|
+
"typescript": "^4.8.4",
|
|
117
117
|
"update-notifier": "^5.0.0",
|
|
118
118
|
"uuid": "^8.0.0",
|
|
119
119
|
"yargs": "^17.6.0"
|
|
@@ -140,7 +140,6 @@
|
|
|
140
140
|
"process-exists": "^5.0.0",
|
|
141
141
|
"sinon": "^13.0.0",
|
|
142
142
|
"tsd": "^0.24.1",
|
|
143
|
-
"typescript": "^4.8.4",
|
|
144
143
|
"yarn": "^1.22.4"
|
|
145
144
|
},
|
|
146
145
|
"engines": {
|
|
@@ -151,5 +150,5 @@
|
|
|
151
150
|
"module": "commonjs"
|
|
152
151
|
}
|
|
153
152
|
},
|
|
154
|
-
"gitHead": "
|
|
153
|
+
"gitHead": "bc7f518bd5a212b5d6124da1390541bfa1f9018c"
|
|
155
154
|
}
|