Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 69 additions & 3 deletions packages/build/src/plugins/node_version.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { dirname } from 'path'
import { execPath, version as currentVersion } from 'process'

import semver from 'semver'
import link from 'terminal-link'

import { logWarning, logWarningSubHeader } from '../log/logger.js'
import { getPackageJson } from '../utils/package.js'

export type PluginsLoadedFrom = 'auto_install' | 'local' | 'package.json'

Expand All @@ -22,6 +24,7 @@ export type PluginsOptions = {
* If the users preferred Node.js version is below that we have to fall back to the system node version
*/
const MINIMUM_REQUIRED_NODE_VERSION = '>=18.14.0'
const UPCOMING_MINIMUM_REQUIRED_NODE_VERSION = '>=22.12.0'

/**
* Local plugins and `package.json`-installed plugins use user's preferred Node.js version if higher than our minimum
Expand All @@ -30,28 +33,39 @@ const MINIMUM_REQUIRED_NODE_VERSION = '>=18.14.0'
* usually the system's Node.js version.
* If the user Node version does not satisfy our supported engine range use our own system Node version
*/
export const addPluginsNodeVersion = function ({ pluginsOptions, nodePath, userNodeVersion, logs }) {
export const addPluginsNodeVersion = function ({
featureFlags,
pluginsOptions,
nodePath,
userNodeVersion,
logs,
systemLog,
}) {
const currentNodeVersion = semver.clean(currentVersion)
return Promise.all(
pluginsOptions.map((pluginOptions) =>
addPluginNodeVersion({
featureFlags,
pluginOptions,
currentNodeVersion,
userNodeVersion,
nodePath,
logs,
systemLog,
}),
),
)
}

const addPluginNodeVersion = async function ({
featureFlags,
pluginOptions,
pluginOptions: { loadedFrom, packageName },
pluginOptions: { loadedFrom, packageName, pluginPath },
currentNodeVersion,
userNodeVersion,
nodePath,
logs,
systemLog,
}: {
pluginOptions: PluginsOptions
[key: string]: any
Expand All @@ -66,6 +80,58 @@ const addPluginNodeVersion = async function ({
return systemNode
}

if (
featureFlags.build_warn_upcoming_system_version_change &&
!semver.satisfies(userNodeVersion, UPCOMING_MINIMUM_REQUIRED_NODE_VERSION)
) {
logWarningSubHeader(
logs,
`Warning: Starting June 16, 2026 plugin "${packageName}" will be executed with Node.js version 22.`,
)
logWarning(
logs,
` We're upgrading our system Node.js minimum on that day, which means the plugin cannot be executed with your defined Node.js version ${userNodeVersion}.

Please make sure your plugin supports being run on Node.js 22.

Read more about our minimum required version in our ${link(
'forums announcement',
'https://answers.netlify.com/t/build-plugins-end-of-support-for-node-js-18-node-js-20/162662',
)}`,
)

if (pluginPath) {
const pluginDir = dirname(pluginPath)
const { packageJson: pluginPackageJson, packageDir } = await getPackageJson(pluginDir)

// `getPackageJson` walks up to the nearest `package.json`. For a `package.json`-installed
// plugin that's the plugin's own manifest, but for a local single-file plugin
// (e.g. `./plugins/foo.js`) it resolves an ancestor β€” typically the *site's*
// `package.json`, whose `engines.node` describes the site rather than the plugin. Only
// trust the resolved range when the manifest belongs to the plugin: an installed package,
// or a local plugin shipping its own `package.json` alongside its entry file.
const pluginOwnsPackageJson = loadedFrom === 'package.json' || packageDir === pluginDir
const pluginNodeVersionRange = pluginOwnsPackageJson ? pluginPackageJson.engines?.node : undefined

// Ensure Node.js version is compatible with plugin's `engines.node`
if (!pluginOwnsPackageJson) {
systemLog(`plugin "${packageName}" node support range could not be determined (no own package.json)`)
} else if (!pluginNodeVersionRange) {
systemLog(`plugin "${packageName}" does not specify node support range`)
} else if (semver.satisfies('22.12.0', pluginNodeVersionRange)) {
systemLog(`plugin "${packageName}" node support range includes v22`)
} else {
logWarning(
logs,
` In its package.json, the plugin "${packageName}" declares a Node.js version range ("${pluginNodeVersionRange}") that does not include Node.js 22. Please upgrade the plugin so it can be run on Node.js 22.`,
)
systemLog(`plugin "${packageName}" node support range does NOT include v22`)
}
} else {
systemLog(`plugin "${packageName}" pluginPath not available`)
}
}

if (semver.satisfies(userNodeVersion, MINIMUM_REQUIRED_NODE_VERSION)) {
return userNode
}
Expand All @@ -77,7 +143,7 @@ const addPluginNodeVersion = async function ({

Read more about our minimum required version in our ${link(
'forums announcement',
'https://answers.netlify.com/t/build-plugins-end-of-support-for-node-js-14-node-js-16/136405',
'https://answers.netlify.com/t/build-plugins-end-of-support-for-node-js-18-node-js-20/162662',
)}`,
)

Expand Down
2 changes: 2 additions & 0 deletions packages/build/src/plugins/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ export const resolvePluginsPath = async function ({
pluginsOptions.map((pluginOptions) => resolvePluginPath({ pluginOptions, buildDir, packagePath, autoPluginsDir })),
)
const pluginsOptionsB = await addPluginsNodeVersion({
featureFlags,
pluginsOptions: pluginsOptionsA,
nodePath,
userNodeVersion,
logs,
systemLog,
})

const pluginsOptionsC = await addPinnedVersions({ pluginsOptions: pluginsOptionsB, api, siteInfo, sendStatus })
Expand Down
4 changes: 2 additions & 2 deletions packages/build/tests/core/snapshots/tests.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -1173,7 +1173,7 @@ Generated by [AVA](https://avajs.dev).
> Warning: ./plugin.js will be executed with Node.js version 1.0.0␊
The plugin cannot be executed with your defined Node.js version 1.0.0␊
␊
Read more about our minimum required version in our forums announcement (https://answers.netlify.com/t/build-plugins-end-of-support-for-node-js-14-node-js-16/136405)␊
Read more about our minimum required version in our forums announcement (https://answers.netlify.com/t/build-plugins-end-of-support-for-node-js-18-node-js-20/162662)␊
␊
> Loading plugins␊
- ./[email protected] from netlify.toml␊
Expand Down Expand Up @@ -1232,7 +1232,7 @@ Generated by [AVA](https://avajs.dev).
> Warning: netlify-plugin-test will be executed with Node.js version 1.0.0␊
The plugin cannot be executed with your defined Node.js version 1.0.0␊
␊
Read more about our minimum required version in our forums announcement (https://answers.netlify.com/t/build-plugins-end-of-support-for-node-js-14-node-js-16/136405)␊
Read more about our minimum required version in our forums announcement (https://answers.netlify.com/t/build-plugins-end-of-support-for-node-js-18-node-js-20/162662)␊
␊
> Loading plugins␊
- [email protected] from netlify.toml and package.json␊
Expand Down
Binary file modified packages/build/tests/core/snapshots/tests.js.snap
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[[plugins]]
package = "./plugins/plugin.js"
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "test",
"version": "0.0.1",
"type": "module",
"description": "test",
"license": "MIT",
"repository": "test",
"engines": {
"node": ">=99.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name: test
inputs: []
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const onPreBuild = function () {}
2 changes: 1 addition & 1 deletion packages/build/tests/plugins/snapshots/tests.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ Generated by [AVA](https://avajs.dev).
> Warning: ./plugin.js will be executed with Node.js version 1.0.0␊
The plugin cannot be executed with your defined Node.js version 1.0.0␊
␊
Read more about our minimum required version in our forums announcement (https://answers.netlify.com/t/build-plugins-end-of-support-for-node-js-14-node-js-16/136405)␊
Read more about our minimum required version in our forums announcement (https://answers.netlify.com/t/build-plugins-end-of-support-for-node-js-18-node-js-20/162662)␊
␊
> Loading plugins␊
- ./[email protected] from netlify.toml␊
Expand Down
Binary file modified packages/build/tests/plugins/snapshots/tests.js.snap
Binary file not shown.
66 changes: 58 additions & 8 deletions packages/build/tests/plugins/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,64 @@ test('Validate --node-path unsupported version does not fail when no plugins are
})

test('Validate --node-path version is supported by the plugin', async (t) => {
const nodePath = getNodePath('16.14.0')
const output = await new Fixture('./fixtures/engines')
.withFlags({
nodePath,
debug: false,
})
.runWithBuild()
t.true(normalizeOutput(output).includes('The Node.js version is 1.0.0 but the plugin "./plugin.js" requires >=1.0.0'))
const systemLog = await tmp.file()

try {
const nodePath = getNodePath('16.14.0')
const output = await new Fixture('./fixtures/engines')
.withFlags({
nodePath,
featureFlags: { build_warn_upcoming_system_version_change: true },
systemLogFile: systemLog.fd,
debug: false,
})
.runWithBuild()
t.true(
normalizeOutput(output).includes('The Node.js version is 1.0.0 but the plugin "./plugin.js" requires >=1.0.0'),
)
t.true(
output.includes('Warning: Starting June 16, 2026 plugin "./plugin.js" will be executed with Node.js version 22.'),
)
t.true(
output.includes(
'the plugin "./plugin.js" declares a Node.js version range (">=99.0.0") that does not include Node.js 22',
),
)
const systemLogContents = await fs.readFile(systemLog.path, 'utf8')
t.true(systemLogContents.includes('plugin "./plugin.js" node support range does NOT include v22'))
} finally {
await systemLog.cleanup()
}
})

test('Does not attribute the site package.json engines to a local single-file plugin', async (t) => {
const systemLog = await tmp.file()

try {
const nodePath = getNodePath('16.14.0')
const output = await new Fixture('./fixtures/engines_no_package')
.withFlags({
nodePath,
featureFlags: { build_warn_upcoming_system_version_change: true },
systemLogFile: systemLog.fd,
debug: false,
})
.runWithBuild()
// The general advance-notice warning still fires for any sub-v22 local plugin
t.true(
output.includes(
'Warning: Starting June 16, 2026 plugin "./plugins/plugin.js" will be executed with Node.js version 22.',
),
)
// But the targeted "engines exclude v22" warning must NOT fire: the only package.json
// reachable by walking up is the site's, not the plugin's.
t.false(output.includes('declares a Node.js version range'))
const systemLogContents = await fs.readFile(systemLog.path, 'utf8')
t.true(systemLogContents.includes('node support range could not be determined (no own package.json)'))
t.false(systemLogContents.includes('node support range does NOT include v22'))
} finally {
await systemLog.cleanup()
}
})

test('Validate --node-path exists', async (t) => {
Expand Down
Loading