From 93dd88ff41a5aec98891718baff503fba71775e3 Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Sun, 10 May 2026 21:41:55 -0700 Subject: [PATCH 01/10] node 22 warning --- packages/build/src/plugins/node_version.ts | 56 +++++++++++++++++++++- packages/build/src/plugins/resolve.js | 2 + packages/build/tests/plugins/tests.js | 13 +++++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/packages/build/src/plugins/node_version.ts b/packages/build/src/plugins/node_version.ts index 3a1b0224c4..402a28c2a9 100644 --- a/packages/build/src/plugins/node_version.ts +++ b/packages/build/src/plugins/node_version.ts @@ -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' @@ -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 @@ -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 @@ -66,6 +80,44 @@ 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 2, 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-14-node-js-16/136405', + )}`, + ) + + if (pluginPath) { + const pluginDir = dirname(pluginPath) + const { packageJson: pluginPackageJson } = await getPackageJson(pluginDir) + + // Ensure Node.js version is compatible with plugin's `engines.node` + const pluginNodeVersionRange = pluginPackageJson?.engines?.node + 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 { + 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 } diff --git a/packages/build/src/plugins/resolve.js b/packages/build/src/plugins/resolve.js index 08284ec5b9..23b3e7f6a9 100644 --- a/packages/build/src/plugins/resolve.js +++ b/packages/build/src/plugins/resolve.js @@ -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 }) diff --git a/packages/build/tests/plugins/tests.js b/packages/build/tests/plugins/tests.js index 2a2285f4bd..666ef925a2 100644 --- a/packages/build/tests/plugins/tests.js +++ b/packages/build/tests/plugins/tests.js @@ -111,14 +111,27 @@ 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 systemLog = await tmp.file() + 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 2, 2026 plugin "./plugin.js" will be executed with Node.js version 22.', + ), + ) + const systemLogContents = await fs.readFile(systemLog.path, 'utf8') + await systemLog.cleanup() + + t.true(systemLogContents.includes('plugin "./plugin.js" node support range does NOT include v22')) }) test('Validate --node-path exists', async (t) => { From 6ccca99a540deae1cac0061b1af423e4b79661b5 Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Sun, 10 May 2026 21:55:03 -0700 Subject: [PATCH 02/10] formatting --- packages/build/tests/plugins/tests.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/build/tests/plugins/tests.js b/packages/build/tests/plugins/tests.js index 666ef925a2..710b4289a3 100644 --- a/packages/build/tests/plugins/tests.js +++ b/packages/build/tests/plugins/tests.js @@ -124,9 +124,7 @@ test('Validate --node-path version is supported by the plugin', async (t) => { .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 2, 2026 plugin "./plugin.js" will be executed with Node.js version 22.', - ), + output.includes('Warning: Starting June 2, 2026 plugin "./plugin.js" will be executed with Node.js version 22.'), ) const systemLogContents = await fs.readFile(systemLog.path, 'utf8') await systemLog.cleanup() From 43e75ab75394ce8224afc95a41d09092cc0baafd Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Sun, 10 May 2026 21:59:35 -0700 Subject: [PATCH 03/10] lint --- packages/build/src/plugins/node_version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/build/src/plugins/node_version.ts b/packages/build/src/plugins/node_version.ts index 402a28c2a9..6e30bb0d7f 100644 --- a/packages/build/src/plugins/node_version.ts +++ b/packages/build/src/plugins/node_version.ts @@ -105,7 +105,7 @@ const addPluginNodeVersion = async function ({ const { packageJson: pluginPackageJson } = await getPackageJson(pluginDir) // Ensure Node.js version is compatible with plugin's `engines.node` - const pluginNodeVersionRange = pluginPackageJson?.engines?.node + const pluginNodeVersionRange = pluginPackageJson.engines?.node if (!pluginNodeVersionRange) { systemLog(`plugin "${packageName}" does not specify node support range`) } else if (semver.satisfies('22.12.0', pluginNodeVersionRange)) { From 1cb208d45b1ac9d65b9196694940d66a52c62e46 Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Wed, 3 Jun 2026 13:31:41 -0700 Subject: [PATCH 04/10] updated date and forum post url --- packages/build/src/plugins/node_version.ts | 6 +++--- .../build/tests/core/snapshots/tests.js.md | 4 ++-- .../build/tests/core/snapshots/tests.js.snap | Bin 5617 -> 5625 bytes .../build/tests/plugins/snapshots/tests.js.md | 2 +- .../tests/plugins/snapshots/tests.js.snap | Bin 5893 -> 5927 bytes packages/build/tests/plugins/tests.js | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/build/src/plugins/node_version.ts b/packages/build/src/plugins/node_version.ts index 6e30bb0d7f..cffa577e45 100644 --- a/packages/build/src/plugins/node_version.ts +++ b/packages/build/src/plugins/node_version.ts @@ -86,7 +86,7 @@ const addPluginNodeVersion = async function ({ ) { logWarningSubHeader( logs, - `Warning: Starting June 2, 2026 plugin "${packageName}" will be executed with Node.js version 22.`, + `Warning: Starting June 16, 2026 plugin "${packageName}" will be executed with Node.js version 22.`, ) logWarning( logs, @@ -96,7 +96,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', )}`, ) @@ -129,7 +129,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', )}`, ) diff --git a/packages/build/tests/core/snapshots/tests.js.md b/packages/build/tests/core/snapshots/tests.js.md index b960cebcb0..6549656bfa 100644 --- a/packages/build/tests/core/snapshots/tests.js.md +++ b/packages/build/tests/core/snapshots/tests.js.md @@ -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␊ - ./plugin.js@1.0.0 from netlify.toml␊ @@ -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␊ - netlify-plugin-test@1.0.0 from netlify.toml and package.json␊ diff --git a/packages/build/tests/core/snapshots/tests.js.snap b/packages/build/tests/core/snapshots/tests.js.snap index 82c05655b429f21ff74d4327d3236d24683a1863..cb32991dbe08df8b38a89c02cf76463f682c3297 100644 GIT binary patch literal 5625 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 TGxze(gF62oJQ;hZ`y2rP)nKuw literal 5617 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 Lj 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␊ - ./plugin.js@1.0.0 from netlify.toml␊ diff --git a/packages/build/tests/plugins/snapshots/tests.js.snap b/packages/build/tests/plugins/snapshots/tests.js.snap index 27ccf55412f3befc1fe9d6789b47807da0c690e2..49bcae28602cee0c1eaa4a2023d217993f808b05 100644 GIT binary patch literal 5927 zcmV+?7ue`QRzV?sCl4fQIfoo_N|1lR$jQg# zFGx;#RmCD%5~XfYB1LsqLzt0RRgcB@>b>84Rj-Oa@An+vd`+dkNse>=RcVrNP zMuz#?7~qeI?;+}=krNJVkJ!XBMr7zy^2a~7EQ1y>a&q_V*ii-uSKG{PeBb z0j7@KzeJBi+p*BRf3;u#?2p#^Q>%T1KEnaAeXoYlk3GSSR*!SzLGym|KCAJ>F$M(6 zE!+zSZA60*!><63d}32SxP0#Wv@L}|MC%sCgpyVyidNsgpkV-oQLXMPkX850aBK@m zvATyNO4>EX)u_$BP3URhw0S8nAhv@&x^H^}9Kb(Efp20$Fp0VZJ?(@+09BA>2iRm? zgLb+5#pwB7-yWbo+;{;>t%$Ep>bnjj`y3PBIl~q*Iej3_DMpVU_8glG7h-5kiARc+A8o!Th_+vh*P(Scpq_RcUK%2gI6kwr|_zc8(?{=sn6L43ucQa;3iW{cy zjvP#3;K20FY%0x5)HusvNi`b7wb_xbz;!CG3vq zy3a71^_8L@N=k^5T_*t<{ZKbgYu0jAOv1gOs8eT_j+=(=j|05)7&rV6=OT zTu=%J6LfCVA#!}vaAwSR?%Q7MC!)kP#d zhc;COH7=FJ=pu3Hhcj_W{Fnhuf2jb|pK4%QB`}HqD~C)`w-$#c@qby^6mzpMaEczJ zA*eLCT;|283>woQ3mKlJOo`NL8Lj7lvm2nT5r$>)VSYH9lZr;RLXqwJ64`*SuJ3(( ze7#WX3kA_W)`(U*qM2M?uPPO2jrA!fmZ?+<1+oby7Qbp`F0yI`jjs;1SS`JF74cz# zwW}+dYhWr8nB4s<5a@Rm1o~}_K&2y4g@9)vyeS@L^aS48enb+Sp1>=C(FLN(xq>E% zfyuy?_Z3|EfyR~9;7YfIG`M-Yvk1O)^C#8K&ozxRYxmJOQyQa-oKE|m3N%I!6g&j( zjtn}?0GplyHpd!lRs=RuPE|sjxK9hgj-*lo_N16t7yz{Zad6;4yv8(YZWvOVXTi`| zja*=K6Z9}bqcL{Ok4e`jppmIiWcsd(Oi`Ze6kxh@rU27G@A6p@FjXqcTkX<9uvA7~ z_c#Yv7!?UdMH-865ct9E(jJ6%`9r%f7=dT-`$&W;rXPX^c|JuwjQW1)Stuk7x1$wk zFi$B4Bh)cGR=ICO#VGHpE{p5zK5IRKB4bSsd<$QlR@PUH^yfNyDBVa`DVJ=q1hNvc z#&oLKZAcXpaf413TMhdQj38c}?#;lWPZTWrNMljySQOgTz#-_z1O^@Wk;Ud0(78dF z&9^;lwIg>gf&c`rR)|S++1SIGvjp6U=#+~&Qk9I+NJGw~qG`xkDf^3@v}$l05b~(m zCSVvHXnuy-0S0u4g*}+Hnt%5@3M_r?u7;&~U`ZI>`TYBC&md!Q#4=+oPQDsImwsN} ztA#?ZgwA|H7;BM7LJ=QxMse9R4#%T29nP)wM`P=X*G(gWXX;0v&rDV@n!3HnzB zLH|jw1eG4xdA`?`f~p4cDLu-GdTAPkPCJgVudvBS%g3bQ`BW&CUJIJ4KhxHN2y#Qh z*MZ=zk8SYt17^9Hzb{SCHUz>xzq*me>y0L-g~2O9K?sH3$d$BEV`of48;r!p zr4e|@u&j*o=CYTQRx=W1EOq!9Tg5T}KO0bJ;Ra902t5*xMt;EX;Y^q{-kvYX^$puW z=R@rAb3tWJTY%Yy(TpSF-aJ*X;>nvDEAnFn6VVZ0{T$1A%6$X^I|i^C_#wKI_oxHb zxY*+jB2G*Ej!7F-HHajZu}15hK`*@TN;_Ch%&QrmT+T z_HZXi73}C{@wE6I?XxztYiD=D+1-mWFVjY>v%iP>pm`JeC2gMvgZ#|4u-Z&IY&HQu zj`?KFV)8N4W8NFwHhqo7^eAa#?>09Uf2{EOQfrNTcV6 z6tPATr(mpq8NxtHB2l)N4a&iig)T&%aNp1~vCBr>=8)16X}4O2N6w*oQ_2_I#}*ah zie{Qg1AA7(?>8bl*8qCLioPO^2an>f5AU}geDLst4<6pT`XrVa2eHiDo7dEbidhfhU^l6&N~`)R)9K8G*v+y-k02H!&(=NG!e7rMwyx%qKtAZGWMrYXH}}E-@G)WqHyGKU7(C6PV^*GI;{U@fSRe4gSpH3RjVf2IJ@AAVBZl_%pv5Qo^aW3PL9 z+&n#b(s-0YSI(CF2zym?)t#Bsm$?o6N0KY^yEfMy@$+&0#&bLxvWpD&>x}gypU_*! z`OuR&0KljY2RUo{z)x6|!>^y(Ry+2EI`+SJO~>97xo>Q71HOA1TLx->tvL68J}$U< z|89WbR_WZc4Ju4z)pPITo-M(_m$XWA@l$MF!!?j4INrC}@l&}VEaGkCLK^i27v@L& z>~>=u_U0R9H^Ah3~_#$Fo}%r=ST1QPY4Tn~;!!FS@E( zOVmuBjU|gw;dShRtfME}5UPY!@lRsJ@yT}Q_tDAo?auD9cg?N}Naviz7Q$dJlr*v& z-M9W%adiJ%FQ=~5(T(I(JwI1=Yzb~|L~1@SH)_=8TT)<%gQf2RQ7({IBwc0Vu%CrA zI}yn3@{kp?k#Q!`jkf}CPZ?<$6L=_|S^{53W!yDgo!O1_tWlw}Mjs09gN*IK_xL8B z+>65B_7phnep|!oD&dq@tnAXT(z_)9YnonZb4X(>){Dh67mIZUm9&SZvICzPSsc(y zv@v0fnrsXw|?3a20sNY7F zDn2DnCrjvLi3;;yCrgyWag8508$WakOmX_8GhbH8{UYZuel*t{#?|>R-};d= zp2t}IJVtf1V8;5lw7HDZGh!wfy@s>?dX8aXs)H@D>AieKT$%mPxY3B3WQ{p&DT=9x3qw4t#Y@2t_U?d@$*?@>h^% z>^ES?we>J^?Tf4zZ+*j5T-K9sXqR>ME-T2Ys_rW5+R|KAAXf6Sf#9t6GS}`?RWGxG zStX^7alH^x_@D=et(yk9s|_&iFcjLc12i-U0{H>>VAnsx@E@VUISl% zaSpu@RAS#5q@P#$ZgbFF5n`rG5f%wz)M$@j#D z0t@u6#f)jYHU#tOHz^1&s}4W6tX;D`3tu#c6om5>dl+Pbx!#b161A>50`#`==|iB% z=~@{u^BEClZZckT_BRUt{)K+c*$RVZCZ}pZ@K~P~4x1U3lH%rwi3JDF=?7Yi2wKk* zzW`4ClrwyTA(Y2de4~vvAQ0Wy64zbB_MX59XP0x0h#7HkOMdA-NZrvhTzuI*)9r`SZ%u^4h z*z?W|$F|TA8{Cw*U^JfXZtp;ij+|iw>j5L=+@?c>FQD9Hvri8mK5C1EJ$%*}83Dnv zT^|gJ!=^$hGNKw%A0UcgC(K_^Y4l^|E0F(+M3@WYp#>1brN+2KgGK9 zzw;TxX|^;IMbtI{6hMCO6Um|5$X zOg$c6=yUI6coA{N4GCpOLc|6Ou{a3ceWfH0eUMW4wiCSqIg<{tj`!s^B-RBw;WWtu! zeJ{FDZ*tJW2El9=2GA^Bj}3(;I`lB|`)whFBy^I$l^=1sle};onoOVM&s}*;X+s>p zt1+_x%q&}zC35~?HgZZoDFZxzp@8S#YVcecc*f3br2sT>u@?hTNv9MbO|epg>Dqm= z!1UyDgfjtEnz(udhxK+6Eb92M46G|t*(Ba=oCJnPMvM(?WE`7;J))dN&h*!4zx7nX zxQQ88aSu)C@03HX%vcYW9%z_^^GpD_(o^1z0n*?Z#O~Z#E{)g^I?HC zwb`xpl3JllYHu^_X_!N7z9w_v(AWPxbo0^HyZ71=Gf$2mzW@GvZM1ta0_}i-_@MC+8NncAC&iL27>*PT;NJI}wJ7F` zEUI$saSh3v2B5PnXGIJx3!9E1EqLE>i13jx`2+@vgZG$V{ZVW=5bFDZiD5v2jb>Y0 zI|hLfw8yu79I&;fR8`w^Y!4s%A+-3!7^Uf_i_%RTyO$iGjLF0M_wVzcF7Hj7CrkQX zw8>jsIGsW7ropXD?R~R)JFA1hkMJb*Vt~E zC5Py%Fw)XFdUPG<2!({7215kd<|~ppNlg`p3G2XpwGLBP%BzB&Q_{C(xKJ^*@_eZ@ zbJx+Sy7d8m=*5SR>L|Y?o);PAzq=zhQN(j^$Z!H|SeMBa>&nUJa|OeO8pBpP`P_{p zSRDkL=-Cn`qb04xF zM@Lb}bw0FBwh@z0?-;0Wa9b6xm4k6Lv3-X5{z{>me7B&1T!`HnmQCF=QrlKA?xB8} zboqzyUwVdXo835-p#tbO)~CEj@+(w|J(+);iR+j|bn6dZN*=piYkVXgoz3I{1M9qO z2cOlq#IMv>*+~9Ye#A+(@gn&gLjC{ICc&X zp6osC9&MjIYomHvket|)(}T{*-r>P(b&CcXyudcBj|I)&Mt>lVJE6 zj=WuGyYp-psy|QH=KDi=@!oNF|FE;YFRTH(aTWg44l_Xc&u zD@Jz^#!tvS?ig!cf2Yn2hd7=}+7N#ALJ$Z4+0s1BSN=#S_ zOBhXUr{C`gciU18WId0CtZ!x6$a-WXWvPG^U z_*lXR^T#9j@J%xe7!dIl=V6jGFdDKwcnm;B_6TS(Y`Fx~a;E@@p30w)%W43GTzb^F z=yDzT+mlX`k#(~KkH_{1Zz2aH2r=s0Z4EPJm|WGx{)8?qfhXIK)j zLY|XmK*$3+jsw^B3@Y_Kia@py3C$sUH;mzd0Q$BEO+nTDQ6_m9roOz#$gxeZFPAZC zW-Zc``|xo1!eCZOJWN#5Wc}N`*G!(+#dPRf1c7B?8Fw&>C`2@J8p-!qCt1rjgf=iz zy`+F0@;E}g8b$^c0k5>88=Ux_ipcQs3oUYDfTDTzNlHk{|COdpE~00000000B+UCnPKNmd`&>FM2Et+a69u*>1eJ8ZdUT~*!l#Y{=tTDR@) z_TaHSZF`1MYg8MR8C4aY%!r(fNPjSDiH{R-Lwf)YNDCJ(E5w1*a^S!T#6N&I?GgS0 zIH8EhsLHI$D!V>1D{V)sq;`J9%gFa4-tWB^FJAt<+wqzECI8Efzr}(GA76a`5OEG6 za=%19{1Imq1cT6zdxUbrDGGU?3I0bv_dF!f=WqP<8?W7eP5%3}d$0Y*uYdA}vyX*O zx|i@ICcX!={&Rlyvp>AiKgW3tpW=uUMje2kP|b~IM{(m(<6+~WyyK~ldfWlcFBc^Bg~u0UF}oOX!{KL#B%`Ty$7M- zEk|-SX|qSTIEnn0x~eX4;$tfI2<_pB(B5IhT+BJi8@U`OK#1oX4&Kxh~X-h!`RIeJX9q{X0Xikf~%3mQoW66Oeep=d_Ti(Mf{k@Fb#81e}1 znI}2wIi}%H)Tn}P!~)2&#oPhwj2=c<&q#cRsd#WVrlJ&Z+pu?iz>X9*To!~r7T5zq z;ZeXJIQl#nI0P?_kQBjCNYk1+BdTvJxqgRK-+Gn<*JSSWMh2JORk(zMP+U$xCm>y* z%v@~9={pWs_x>&eSU~X=Y&o9F%Z%dRv!2RoJ(WaI z74%h1&z9k>B(%!&SB7k@uE%11m7K{q0$kQfDT>iW0@hnwh(gRUgmHvn=LiDk#Xjb6 zPDCGk<|2PeEbWsFLOrnvRXRdp-cBUeR>G;*$PFr9~flYVnM*@M@QFlwLZ@v z+D8`AN=GzT$?LRIj@C$@=ESlrl|q4RjETjsTDgm?TDhnEYFA6%W!J6}J}j_ybwzUv zOhp1yuy+Ll{Z0mfe#;_I=?GLI;8_T7iia5+z`L`nZB1rrX%K%f)?($g?FjXqcJKLp&V5y9}?oke| z&@0l6N@gtJC}PpwnmqtJ>ZJqBIqJ#La;RMum&LvZlnLly=(3o45Oay!$qF0FKv))7LO1et9WQ!$`m5?=NQ^oE= zs+jg0Y^vC5*k7OraeA^l1&cn;V9{fXMWtg=OsauHF&W38eU@l!bpp;2mpY$N?6nee zFM$9!Lw<~TW7*iFn6m`jN$8Y|Ic6%MFtm^}KG7`Xtd#vl2CeE!2gJ&2CLAK{BSFrv zEMOq|SnGpntNHhTGXqQCxNl);7Fg1XcQ*UJ-9dcBk65O!rN~zU=+f`2dbN=2HKa3} z6DC{CBcX(kb9!<4G!90+GwaUX=%2;b6|=uUY!&;A*6{`5A~k1nR)YQ|gP{LtSAt4+ z>?oscBdBU1U#3TMqF$Otp<%}n_7xU<^cd!K%7mU&b}eY){F$^C#36{eS_dMO3*`IQ z15898M^bZ1d*7&@%`gO}&aS2Ldaa>oVZ<>+F@-f628I^Yw?`zjCP}PaqKE?WyqxQe zWiKbKW+WP4>flqkiiHG*dYv*4*HuDBOnDrJERy(erq!Biua@Mxi1={c$5hn?H92h& zmK#QMO2oZ>lEI3nuUo8`A1kDY4%O=CNY2yTB@kp8z-r)!?#jHS4rJqE%Nrz|nm9ox z>UL|e7MWX{tH6|w%Q0ME|JRH%`X{T5s-%o0!FGZ-ZBC@XtD&0mIySe3J4UKtLpP77 z#qVgJwxQh`yJN=gZjyPKR$^OwyU_JfZ)|=U-A4~2c1Ap$ZDtHM!Xc*8CX*UdjggMD z-dL2W;{o^ryDFBh%KkFn!x4x+qA+Fx<}C33b4H#1!Y=SGpiTqk;nqMDRnTMPrnT%`nPiXz}jR zLap24oGl;fGIz;G%(tvEUCXEG-^t4OjAD@Qj%oYksBv=qwEjT~T@_pAN7$>HXU&;e zYpLA<{)6Gl?553iNBnGDzx5nXhU_B4{W@d)ka2P6I3H7!10c@XmI66%^1vUmXb!u6 zVq0t27i!r5?lld2s&n7u;s&ETG-u0z^H&+;{?A4QH|xJ!Ah=Z;_i}@Z6j}Am`$5l^ zVBi~CrJ49Cw%))skR=%2ciHgEazSgvyU2w!>T52{j`;cQMmS7o8)dhY!*m{tDzBB} zBnb>_=EgOhz(A3Ios9llz}@jAes$b3Zh$kaXX;^rh%Z&^q0!9Azf=?^0rnq3u;&KSkk!jK=7M0tkp8~>XzbpP8fr>@k{P2^NP zJJ)n<31)6WYBno3Y1C3%QXq^76>UtuHy%uKC8^CLyWq~+DY^ecPf__Kq%IHihDiL=QPHd&&={I|&x z<#62M$L+=sn*vkZe6pD@tK@!>F^nJ26vMbW|K&SB&crbq-?4Fw)yFZan*}q{zoo@8 zO3#QHWAqJ-`r8=85^cnV4Z9mQ7|ZkH@3J3hbFXaf)e1OlbFY@iU7LHg%-pM6hNE*W zEnktT<@_|`l6_*gBvim9t8BhjwtGu+$Zk~b)~JZAFL&d9Rb4Nhoz&ju{ zod)@_M9yU=v{|dIIIHceor{oh44efRVnO`8+PS}soKeiN1`P!0`zUxlrtLs_hBDash!=cYEp&L_If9S7Ag!&dX z7f8fB1v`pe^F#hs%`FHO3PL9mc+}cfvLiMJgg(iD&=U(nrGt>!ty~El8f=}O4~mlJ zg`&}rU)H(5aX-iDVX-AqXq!aPM;v^@1w=StXBZ;Ph5YCvSKq9$h20AmMuF%PY$2+Q ziV5z|{eA{{KEH2~r)=aI7=Uf$RRn*sU78bxGDHf+qEQY`L8QU0+lDD~J!E)PVU$}y zB|jZFC7qahzI^uGQUdZD%0?3onZdCTk{1F+JuEmNpp4e>1>x$nHyl&L@!{wMM|pin zND0e)26;G`>Vt&QAt?vyYSOE5sXiX@JPR$}5!704axt z$A-F^$aF{!=h*jiK>H{I+S?YiO9yQg!f7iZRs*ur9hwucQzRBiX4H#$!LqX$vm7tX z>1^T)t%92&68)J$IotfYpghvb=2}gg>Tiw?Ggk^oCEs)6VekkSO{q*50_?W&$wQ#XaIJBeRYXLpo1D*_{dER^ z|J;7&Y=us=^!Wf0`jq%so}Oz&O!0%8=8yKH)DtNR)YFC+&6Oyog6NCG+j-<}lcH#$V`AK(zxT%(gPz~B2L193`lblNx@red}njp z0jZC5gbfiNdvH!fAMgbc;L7OJqqje3>4ZIeRu56cv1!*u0{Jo&DgY&SnE3$fs>0kQ zur7!`h7e^2F~R&n3sj+s$>k=D9g$aYLd$hYU;HV1)EmX!94c;)VXhb{Wsp+fYU`am zAM?Cyx_keY(dNIj+T3dMTcpix617dDuHdg&bN;tKl{hVzW)hE@a6lAT=M=j+2K^hS z83=f36RJy(&}-3DBQl@#WzLAaZe3_3e!#4om`rW!-c52Z63zrM7qTS8Kw*LO0|uaV z%gW+?)+as&KN(A_`V?t-Gs-~J4@MzMJ7|4GJs(FP!{vGVPya82RR3d(VdNe0$rD+t3T_fzX2zKLs*9eelT~=c>IZHFkRXutu*z`89h zgrVzXjxz^?bSHD-D0bz1GKV{+Ole)0-?f-o0A`jwlMQnIa5{1tJ1GY|e~|&tzp>!C zGVmN2vy}qSp^3d1h#ESj0BMSq7EEv4FAq$QFGHLQs7A%r5;&~2hR&k(56i&1GAEnC zyS3v8Q65S*$dPg6MkEx9MgHW^Xut6+gLNNRtScSs1aAu?xoY5CrblyPT$)Cq_%_16 z;#c3q0*h~A`r4bAW9jWSOgTMd*VBAhU`=g$YrCXY=#tu-5_{@yAG=@j8F1*E|CPa^ z|M;fGp;h3}Wr*8KcGUr*VV4#_q5+Y@p(xG4n}|hYS$$%Lq`uoQ?Z*AAEf)JaGmHev zVbPqCvO7LX9I#*g{Xf77MI9f@4-84=n6<-O1DQ7jU;kj^od>Oina4+OzxUp|E!epT zu`BiIqx#!`qFyX3#qtg4!~%Luz{7@<_Xbylbi(I_Lq>h$bTelD=w709aeeBD%rqr#3`h?;m7Dq1r7=>y2 z=}YM*KIse#C}aNi!-o&mVW4``Qpu8xCY!wVNi{)Z{Q`1}uDV3oPC!KZi7Xb|Bx*rJ z@cQ63{v`?bNM}pV$5b!)u*<`9A|kFxozT@dTIpWO$<2rTPaY%qKJ##f>r5RyK^)7C zB*S?f;jdy6;TF97VkY-n&hv3kTdCj~d|@?}uXKkswWNwR!F;eS$Rnsn85hLeVxE-h zOOPWMzStPKe2gzd3u=oF_a?2W-7-sd(O03QjdAqBb&Mm3xvB=kHQB}~&ka(GjKPGh z+9+rziVc$GZpnNA2z1qvICT z#>=`i)r~vb&vx3A@5Wva*N2s0R1HVfZfkSv`A&O#_eJ_{wLer{-aTsX9c*pxX>B0S z?C%`!?LPgy{p4hKZ@c~J&WoengZ&oN)C<=&O98|8!HXM29k=EH)RxP)mv_IZ`_F^-iM}x9aUZ)w5o(5Un@bKix-tN(J z!>5|_N{VtY<<(cx+Ul?cHTB6f8EL1z8mJqSCurrTNNr=NL2726*ER+{d3m4!`U`la1J+s-CZX;0_Z{KuR~uOcS(`^g);98VWGzvW z=BkDiLglN2%axMc09iFu|3w1%T>bYcA94bK_<0BP_RW>tIrQpdF&iS9|`c7io#%7Vni_D!oyM2{gmw~sAe}ny6Sq4t5TK&L6&Xtg3;Nv3$AJQIA;Dfnt97!NjE6)94(m>dkd+;QH zge1hC!j?-ea4-QtY$$(BE~^0$en|zoXe%Ml3m|D4MFWW4*E5!HX6ESy5jiE|RN#@5 zp5zdXeP3>wmERRgv}HhgeYpeZ3>yMota8#2bmATe90i0TVdi_{fouSe-M;)b45C12 zmrx?4GGJjYdE|3uo)P-QCBnZPke1paO}Wb=z!ykrCH*qVm4@u!`h&*sjRP$D%;UiO zD&vk&E->;;7>QBYN!hXm5f*VohZkfiw^$}txybi*owtVGX(X>`^6L*|fsD~>N { .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 2, 2026 plugin "./plugin.js" will be executed with Node.js version 22.'), + output.includes('Warning: Starting June 16, 2026 plugin "./plugin.js" will be executed with Node.js version 22.'), ) const systemLogContents = await fs.readFile(systemLog.path, 'utf8') await systemLog.cleanup() From 976008df7de1d44b316d076e250f380ce44171ce Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Wed, 3 Jun 2026 14:35:18 -0700 Subject: [PATCH 05/10] feat: warn when plugin engines.node excludes v22 Restore the targeted warning from the v20 cycle (#5461): when a plugin's package.json engines.node range explicitly excludes Node.js 22, surface an actionable logWarning telling the author to upgrade, in addition to the general advance-notice warning. Also restore defensive optional chaining on pluginPackageJson. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/build/src/plugins/node_version.ts | 6 +++++- packages/build/tests/plugins/tests.js | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/build/src/plugins/node_version.ts b/packages/build/src/plugins/node_version.ts index cffa577e45..757c5e3241 100644 --- a/packages/build/src/plugins/node_version.ts +++ b/packages/build/src/plugins/node_version.ts @@ -105,12 +105,16 @@ const addPluginNodeVersion = async function ({ const { packageJson: pluginPackageJson } = await getPackageJson(pluginDir) // Ensure Node.js version is compatible with plugin's `engines.node` - const pluginNodeVersionRange = pluginPackageJson.engines?.node + const pluginNodeVersionRange = pluginPackageJson?.engines?.node 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 { diff --git a/packages/build/tests/plugins/tests.js b/packages/build/tests/plugins/tests.js index 9120d40671..631e4e1927 100644 --- a/packages/build/tests/plugins/tests.js +++ b/packages/build/tests/plugins/tests.js @@ -126,6 +126,11 @@ test('Validate --node-path version is supported by the plugin', async (t) => { 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') await systemLog.cleanup() From 54f0d32f89b4efd1793a97734bd2fbda62bd3daf Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Wed, 3 Jun 2026 15:01:19 -0700 Subject: [PATCH 06/10] remove defensive ? --- packages/build/src/plugins/node_version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/build/src/plugins/node_version.ts b/packages/build/src/plugins/node_version.ts index 757c5e3241..8a6088ebe8 100644 --- a/packages/build/src/plugins/node_version.ts +++ b/packages/build/src/plugins/node_version.ts @@ -105,7 +105,7 @@ const addPluginNodeVersion = async function ({ const { packageJson: pluginPackageJson } = await getPackageJson(pluginDir) // Ensure Node.js version is compatible with plugin's `engines.node` - const pluginNodeVersionRange = pluginPackageJson?.engines?.node + const pluginNodeVersionRange = pluginPackageJson.engines?.node if (!pluginNodeVersionRange) { systemLog(`plugin "${packageName}" does not specify node support range`) } else if (semver.satisfies('22.12.0', pluginNodeVersionRange)) { From 10ecbbadc61e92179da1761f7edd9d6c6c9226af Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Wed, 3 Jun 2026 16:11:35 -0700 Subject: [PATCH 07/10] remove path traversal --- deno.lock | 434 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 deno.lock diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000000..dd53d1fb1e --- /dev/null +++ b/deno.lock @@ -0,0 +1,434 @@ +{ + "version": "5", + "remote": { + "https://edge.netlify.com/": "fd941d61d88673d5f28aab283fb86fcc50f08a3bc80ee5470498fcfa88c65cfb", + "https://edge.netlify.com/bootstrap/config.ts": "6a2ce0e544e15e8f8883a5c18da5948e37fd0f2619f68cb31f3af53c51817025", + "https://edge.netlify.com/bootstrap/context.ts": "e97240232121e2f369f6546ce961490f34d961ea1ea54be3ff09633e3f08373f", + "https://edge.netlify.com/bootstrap/cookie.ts": "8b0baae708989ca183c6f3b4ab3d029e6abcbc2e43f93edeb0ff447b3bbc3a05", + "https://edge.netlify.com/bootstrap/edge_function.ts": "b8253e86aa83c67341f5cfedeba5049d77fbf84dcab7eceff7566b7728ae9b39", + "https://edge.netlify.com/bootstrap/globals/types.ts": "eaa6148ded3121d8dee62dd91c86e7fe76601df0f3ca8d7962243a30f4c8935f" + }, + "workspace": { + "packageJson": { + "dependencies": [ + "npm:@commitlint/cli@19", + "npm:@commitlint/config-conventional@19", + "npm:@eslint/compat@^1.2.9", + "npm:@eslint/js@^9.28.0", + "npm:@typescript-eslint/eslint-plugin@^8.34.0", + "npm:@vitest/eslint-plugin@^1.2.1", + "npm:eslint-plugin-ava@^15.0.1", + "npm:eslint-plugin-import@^2.31.0", + "npm:eslint-plugin-n@^17.19.0", + "npm:eslint@^9.28.0", + "npm:execa@^8.0.1", + "npm:lerna@^8.2.2", + "npm:oxfmt@0.46", + "npm:typescript-eslint@^8.34.0" + ] + }, + "members": { + "packages/build": { + "packageJson": { + "dependencies": [ + "npm:@bugsnag/js@8", + "npm:@netlify/blobs@^10.4.4", + "npm:@netlify/cache-utils@^6.0.5", + "npm:@netlify/config@^24.6.0", + "npm:@netlify/edge-bundler@14.10.3", + "npm:@netlify/functions-utils@^6.2.34", + "npm:@netlify/git-utils@^6.0.4", + "npm:@netlify/nock-udp@^5.0.2", + "npm:@netlify/opentelemetry-utils@^2.0.2", + "npm:@netlify/plugins-list@^6.81.6", + "npm:@netlify/run-utils@^6.0.3", + "npm:@netlify/zip-it-and-ship-it@14.7.0", + "npm:@opentelemetry/api@1.8", + "npm:@opentelemetry/sdk-trace-base@1.24", + "npm:@sindresorhus/slugify@2", + "npm:@types/node@^18.19.111", + "npm:ansi-escapes@7", + "npm:ansis@^4.1.0", + "npm:atob@^2.1.2", + "npm:ava@5", + "npm:c8@10", + "npm:clean-stack@5", + "npm:cpy-cli@5", + "npm:cpy@11", + "npm:execa@8", + "npm:fdir@^6.0.1", + "npm:figures@6", + "npm:filter-obj@6", + "npm:get-node@^14.2.1", + "npm:get-port@7", + "npm:has-ansi@6", + "npm:hot-shots@11.4.0", + "npm:ignore@7", + "npm:indent-string@5", + "npm:is-plain-obj@4", + "npm:keep-func-props@6", + "npm:log-process-errors@11", + "npm:memoize-one@6", + "npm:micro-memoize@^5.1.1", + "npm:minimatch@^10.2.4", + "npm:npm-run-all2@6", + "npm:os-name@6", + "npm:p-event@6", + "npm:p-filter@4", + "npm:p-locate@6", + "npm:p-map@7", + "npm:p-reduce@3", + "npm:package-directory@8", + "npm:path-exists@5", + "npm:pretty-ms@9", + "npm:process-exists@5", + "npm:ps-list@8", + "npm:read-package-up@11", + "npm:readdirp@4", + "npm:resolve@^2.0.0-next.5", + "npm:rfdc@^1.3.0", + "npm:safe-json-stringify@^1.2.0", + "npm:semver@^7.3.8", + "npm:string-width@7", + "npm:supports-color@10", + "npm:terminal-link@4", + "npm:tinyspy@^4.0.3", + "npm:tmp-promise@^3.0.2", + "npm:ts-node@^10.9.1", + "npm:tsd@0.33", + "npm:typescript@5", + "npm:vitest@3", + "npm:yaml@^2.8.0", + "npm:yargs@^17.6.0", + "npm:yarn@^1.22.22", + "npm:zod@^3.25.76" + ] + } + }, + "packages/build-info": { + "packageJson": { + "dependencies": [ + "npm:@bugsnag/js@8", + "npm:@iarna/toml@^2.2.5", + "npm:@playwright/test@^1.53.1", + "npm:@types/node@^18.19.111", + "npm:@types/semver@^7.3.13", + "npm:@vitest/ui@3", + "npm:dot-prop@9", + "npm:execa@8", + "npm:find-up@7", + "npm:memfs@4", + "npm:minimatch@^10.2.4", + "npm:read-pkg@9", + "npm:semver@^7.3.8", + "npm:typescript@5", + "npm:unionfs@^4.4.0", + "npm:vitest@3", + "npm:yaml@^2.8.0", + "npm:yargs@^17.6.0" + ] + } + }, + "packages/cache-utils": { + "packageJson": { + "dependencies": [ + "npm:@types/node@^18.19.111", + "npm:cpy@11", + "npm:get-stream@9", + "npm:globby@14", + "npm:junk@4", + "npm:locate-path@7", + "npm:move-file@3", + "npm:readdirp@4", + "npm:tmp-promise@3", + "npm:typescript@5", + "npm:vitest@3" + ] + } + }, + "packages/config": { + "packageJson": { + "dependencies": [ + "npm:@iarna/toml@^2.2.5", + "npm:@netlify/api@^14.0.19", + "npm:@netlify/headers-parser@^9.0.3", + "npm:@netlify/redirect-parser@^15.0.4", + "npm:@types/node@^18.19.111", + "npm:ava@5", + "npm:c8@10", + "npm:chalk@5", + "npm:cron-parser@^4.1.0", + "npm:deepmerge@^4.2.2", + "npm:dot-prop@9", + "npm:execa@8", + "npm:fast-safe-stringify@^2.0.7", + "npm:figures@6", + "npm:filter-obj@6", + "npm:find-up@7", + "npm:has-ansi@6", + "npm:indent-string@5", + "npm:is-ci@4", + "npm:is-plain-obj@4", + "npm:map-obj@5", + "npm:omit.js@^2.0.2", + "npm:p-locate@6", + "npm:path-type@6", + "npm:read-package-up@11", + "npm:tmp-promise@^3.0.2", + "npm:tomlify-j0.4@3", + "npm:typescript@5", + "npm:validate-npm-package-name@5", + "npm:yaml@^2.8.0", + "npm:yargs@^17.6.0", + "npm:zod@^4.0.5" + ] + } + }, + "packages/edge-bundler": { + "packageJson": { + "dependencies": [ + "npm:@import-maps/resolve@2", + "npm:@netlify/edge-functions-bootstrap@^3.1.0", + "npm:@sveltejs/acorn-typescript@^1.0.9", + "npm:@types/node@^18.19.111", + "npm:@types/semver@^7.3.9", + "npm:@vitest/coverage-v8@3", + "npm:acorn@^8.15.0", + "npm:ajv-errors@3", + "npm:ajv@^8.11.2", + "npm:archiver@7", + "npm:better-ajv-errors@^1.2.0", + "npm:chalk@^5.4.0", + "npm:common-path-prefix@3", + "npm:cpy@^11.1.0", + "npm:env-paths@3", + "npm:esbuild@0.28.0", + "npm:execa@8", + "npm:find-up@7", + "npm:get-port@7", + "npm:nock@14", + "npm:node-stream-zip@^1.15.0", + "npm:npm-run-all2@6", + "npm:p-retry@6", + "npm:p-wait-for@5", + "npm:parse-imports@^2.2.1", + "npm:path-key@4", + "npm:semver@^7.3.8", + "npm:tar@^7.5.12", + "npm:tmp-promise@^3.0.3", + "npm:typescript@5", + "npm:urlpattern-polyfill@8.0.2", + "npm:vitest@3" + ] + } + }, + "packages/functions-utils": { + "packageJson": { + "dependencies": [ + "npm:@netlify/zip-it-and-ship-it@14.7.0", + "npm:@types/node@^18.19.111", + "npm:cpy@11", + "npm:path-exists@5", + "npm:sort-on@6", + "npm:tmp-promise@3", + "npm:typescript@5", + "npm:vitest@3" + ] + } + }, + "packages/git-utils": { + "packageJson": { + "dependencies": [ + "npm:@types/node@^18.19.111", + "npm:execa@8", + "npm:map-obj@5", + "npm:micro-memoize@^5.1.1", + "npm:micromatch@^4.0.2", + "npm:typescript@5", + "npm:vitest@3" + ] + } + }, + "packages/headers-parser": { + "packageJson": { + "dependencies": [ + "npm:@iarna/toml@^2.2.5", + "npm:@types/node@^18.19.111", + "npm:escape-string-regexp@5", + "npm:fast-safe-stringify@^2.0.7", + "npm:is-plain-obj@4", + "npm:map-obj@5", + "npm:path-exists@5", + "npm:typescript@5", + "npm:vitest@3" + ] + } + }, + "packages/js-client": { + "packageJson": { + "dependencies": [ + "npm:@netlify/open-api@^2.52.0", + "npm:@types/node@^18.19.111", + "npm:from2-string@^1.1.0", + "npm:nock@13", + "npm:node-fetch@3", + "npm:p-wait-for@5", + "npm:picoquery@^2.5.0", + "npm:ts-node@^10.9.1", + "npm:typescript@5", + "npm:vitest@^3.2.3" + ] + } + }, + "packages/nock-udp": { + "packageJson": { + "dependencies": [ + "npm:@types/node@^18.19.111", + "npm:typescript@5", + "npm:vitest@3" + ] + } + }, + "packages/opentelemetry-sdk-setup": { + "packageJson": { + "dependencies": [ + "npm:@honeycombio/opentelemetry-node@~0.7.1", + "npm:@netlify/opentelemetry-utils@~2.0.2", + "npm:@opentelemetry/api@1.8", + "npm:@opentelemetry/core@1.24", + "npm:@opentelemetry/resources@1.24", + "npm:@opentelemetry/sdk-trace-base@1.24", + "npm:@opentelemetry/semantic-conventions@1.24", + "npm:@types/node@^18.19.111", + "npm:@vitest/ui@3", + "npm:read-package-up@11", + "npm:typescript@5", + "npm:vitest@3", + "npm:yargs-parser@^21.1.1" + ] + } + }, + "packages/opentelemetry-utils": { + "packageJson": { + "dependencies": [ + "npm:@opentelemetry/api@1.8", + "npm:@opentelemetry/sdk-trace-base@1.24", + "npm:@opentelemetry/sdk-trace-node@1.24", + "npm:@types/node@^18.19.111", + "npm:@vitest/ui@3", + "npm:typescript@5", + "npm:vitest@3" + ] + } + }, + "packages/redirect-parser": { + "packageJson": { + "dependencies": [ + "npm:@iarna/toml@^2.2.5", + "npm:@types/node@^18.19.111", + "npm:fast-safe-stringify@^2.1.1", + "npm:is-plain-obj@4", + "npm:path-exists@5", + "npm:typescript@5", + "npm:vitest@3" + ] + } + }, + "packages/run-utils": { + "packageJson": { + "dependencies": [ + "npm:@types/node@^18.19.111", + "npm:execa@8", + "npm:semver@^7.3.8", + "npm:typescript@5", + "npm:vitest@3" + ] + } + }, + "packages/testing": { + "packageJson": { + "dependencies": [ + "npm:@netlify/build@*", + "npm:@netlify/config@*", + "npm:@types/lodash-es@^4.17.6", + "npm:@types/node@^18.19.111", + "npm:ava@5", + "npm:c8@10", + "npm:cpy@11", + "npm:execa@8", + "npm:fast-safe-stringify@^2.0.7", + "npm:figures@6", + "npm:get-bin-path@11", + "npm:get-port@7", + "npm:get-stream@9", + "npm:is-plain-obj@4", + "npm:lodash-es@^4.17.21", + "npm:path-key@4", + "npm:strip-ansi@7", + "npm:tmp-promise@^3.0.2", + "npm:typescript@5" + ] + } + }, + "packages/zip-it-and-ship-it": { + "packageJson": { + "dependencies": [ + "npm:@babel/parser@^7.22.5", + "npm:@babel/types@^7.28.5", + "npm:@netlify/binary-info@1", + "npm:@netlify/serverless-functions-api@2.16.0", + "npm:@netlify/types@2.7.0", + "npm:@types/archiver@6.0.4", + "npm:@types/is-ci@3.0.4", + "npm:@types/node@20.19.33", + "npm:@types/normalize-path@3.0.2", + "npm:@types/resolve@1.20.6", + "npm:@types/semver@7.7.1", + "npm:@types/unixify@1.0.2", + "npm:@types/yargs@17.0.35", + "npm:@vercel/nft@0.29.4", + "npm:@vitest/coverage-v8@3", + "npm:archiver@7", + "npm:browserslist@4.28.1", + "npm:cardinal@2.1.1", + "npm:common-path-prefix@3", + "npm:copy-file@11", + "npm:cpy@11.1.0", + "npm:decompress@4.2.1", + "npm:deepmerge@^4.3.1", + "npm:es-module-lexer@1", + "npm:esbuild@0.28.0", + "npm:execa@8", + "npm:fast-glob@^3.3.3", + "npm:filter-obj@6", + "npm:find-up@7", + "npm:get-stream@9.0.1", + "npm:is-ci@4.1.0", + "npm:is-path-inside@4", + "npm:junk@4", + "npm:lambda-local@2.2.0", + "npm:locate-path@7", + "npm:merge-options@^3.0.4", + "npm:minimatch@^10.2.4", + "npm:normalize-path@3", + "npm:p-map@7", + "npm:path-exists@5", + "npm:precinct@12", + "npm:require-package-name@^2.0.1", + "npm:resolve@^2.0.0-next.1", + "npm:semver@^7.3.8", + "npm:source-map-support@0.5.21", + "npm:tmp-promise@^3.0.2", + "npm:toml@3", + "npm:typescript@5.9.3", + "npm:unixify@1", + "npm:urlpattern-polyfill@8.0.2", + "npm:vitest@3", + "npm:yargs@17", + "npm:zod@^3.23.8" + ] + } + } + } + } +} From 3096a8b4baf2a7fa9fa634f40f0fbdd5d5cf56ec Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Thu, 4 Jun 2026 08:29:38 -0700 Subject: [PATCH 08/10] avoid misreading site engines.node --- packages/build/src/plugins/node_version.ts | 16 +++++++++-- .../fixtures/engines_no_package/netlify.toml | 2 ++ .../fixtures/engines_no_package/package.json | 11 ++++++++ .../engines_no_package/plugins/manifest.yml | 2 ++ .../engines_no_package/plugins/plugin.js | 1 + packages/build/tests/plugins/tests.js | 28 +++++++++++++++++++ 6 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 packages/build/tests/plugins/fixtures/engines_no_package/netlify.toml create mode 100644 packages/build/tests/plugins/fixtures/engines_no_package/package.json create mode 100644 packages/build/tests/plugins/fixtures/engines_no_package/plugins/manifest.yml create mode 100644 packages/build/tests/plugins/fixtures/engines_no_package/plugins/plugin.js diff --git a/packages/build/src/plugins/node_version.ts b/packages/build/src/plugins/node_version.ts index 8a6088ebe8..0de069a201 100644 --- a/packages/build/src/plugins/node_version.ts +++ b/packages/build/src/plugins/node_version.ts @@ -102,11 +102,21 @@ const addPluginNodeVersion = async function ({ if (pluginPath) { const pluginDir = dirname(pluginPath) - const { packageJson: pluginPackageJson } = await getPackageJson(pluginDir) + 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` - const pluginNodeVersionRange = pluginPackageJson.engines?.node - if (!pluginNodeVersionRange) { + 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`) diff --git a/packages/build/tests/plugins/fixtures/engines_no_package/netlify.toml b/packages/build/tests/plugins/fixtures/engines_no_package/netlify.toml new file mode 100644 index 0000000000..13115b066d --- /dev/null +++ b/packages/build/tests/plugins/fixtures/engines_no_package/netlify.toml @@ -0,0 +1,2 @@ +[[plugins]] +package = "./plugins/plugin.js" diff --git a/packages/build/tests/plugins/fixtures/engines_no_package/package.json b/packages/build/tests/plugins/fixtures/engines_no_package/package.json new file mode 100644 index 0000000000..8aa0c5ab3f --- /dev/null +++ b/packages/build/tests/plugins/fixtures/engines_no_package/package.json @@ -0,0 +1,11 @@ +{ + "name": "test", + "version": "0.0.1", + "type": "module", + "description": "test", + "license": "MIT", + "repository": "test", + "engines": { + "node": ">=99.0.0" + } +} diff --git a/packages/build/tests/plugins/fixtures/engines_no_package/plugins/manifest.yml b/packages/build/tests/plugins/fixtures/engines_no_package/plugins/manifest.yml new file mode 100644 index 0000000000..a3512f0259 --- /dev/null +++ b/packages/build/tests/plugins/fixtures/engines_no_package/plugins/manifest.yml @@ -0,0 +1,2 @@ +name: test +inputs: [] diff --git a/packages/build/tests/plugins/fixtures/engines_no_package/plugins/plugin.js b/packages/build/tests/plugins/fixtures/engines_no_package/plugins/plugin.js new file mode 100644 index 0000000000..0603db7376 --- /dev/null +++ b/packages/build/tests/plugins/fixtures/engines_no_package/plugins/plugin.js @@ -0,0 +1 @@ +export const onPreBuild = function () {} diff --git a/packages/build/tests/plugins/tests.js b/packages/build/tests/plugins/tests.js index 631e4e1927..4656386ce9 100644 --- a/packages/build/tests/plugins/tests.js +++ b/packages/build/tests/plugins/tests.js @@ -137,6 +137,34 @@ test('Validate --node-path version is supported by the plugin', async (t) => { t.true(systemLogContents.includes('plugin "./plugin.js" node support range does NOT include v22')) }) +test('Does not attribute the site package.json engines to a local single-file plugin', async (t) => { + const systemLog = await tmp.file() + + 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') + await systemLog.cleanup() + + 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')) +}) + test('Validate --node-path exists', async (t) => { const output = await new Fixture('./fixtures/node_version_simple') .withFlags({ nodePath: '/doesNotExist' }) From 6d8f2737e2e1dbb090489644d064b93b08957758 Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Thu, 4 Jun 2026 08:57:37 -0700 Subject: [PATCH 09/10] remove deno.lock --- deno.lock | 434 ------------------------------------------------------ 1 file changed, 434 deletions(-) delete mode 100644 deno.lock diff --git a/deno.lock b/deno.lock deleted file mode 100644 index dd53d1fb1e..0000000000 --- a/deno.lock +++ /dev/null @@ -1,434 +0,0 @@ -{ - "version": "5", - "remote": { - "https://edge.netlify.com/": "fd941d61d88673d5f28aab283fb86fcc50f08a3bc80ee5470498fcfa88c65cfb", - "https://edge.netlify.com/bootstrap/config.ts": "6a2ce0e544e15e8f8883a5c18da5948e37fd0f2619f68cb31f3af53c51817025", - "https://edge.netlify.com/bootstrap/context.ts": "e97240232121e2f369f6546ce961490f34d961ea1ea54be3ff09633e3f08373f", - "https://edge.netlify.com/bootstrap/cookie.ts": "8b0baae708989ca183c6f3b4ab3d029e6abcbc2e43f93edeb0ff447b3bbc3a05", - "https://edge.netlify.com/bootstrap/edge_function.ts": "b8253e86aa83c67341f5cfedeba5049d77fbf84dcab7eceff7566b7728ae9b39", - "https://edge.netlify.com/bootstrap/globals/types.ts": "eaa6148ded3121d8dee62dd91c86e7fe76601df0f3ca8d7962243a30f4c8935f" - }, - "workspace": { - "packageJson": { - "dependencies": [ - "npm:@commitlint/cli@19", - "npm:@commitlint/config-conventional@19", - "npm:@eslint/compat@^1.2.9", - "npm:@eslint/js@^9.28.0", - "npm:@typescript-eslint/eslint-plugin@^8.34.0", - "npm:@vitest/eslint-plugin@^1.2.1", - "npm:eslint-plugin-ava@^15.0.1", - "npm:eslint-plugin-import@^2.31.0", - "npm:eslint-plugin-n@^17.19.0", - "npm:eslint@^9.28.0", - "npm:execa@^8.0.1", - "npm:lerna@^8.2.2", - "npm:oxfmt@0.46", - "npm:typescript-eslint@^8.34.0" - ] - }, - "members": { - "packages/build": { - "packageJson": { - "dependencies": [ - "npm:@bugsnag/js@8", - "npm:@netlify/blobs@^10.4.4", - "npm:@netlify/cache-utils@^6.0.5", - "npm:@netlify/config@^24.6.0", - "npm:@netlify/edge-bundler@14.10.3", - "npm:@netlify/functions-utils@^6.2.34", - "npm:@netlify/git-utils@^6.0.4", - "npm:@netlify/nock-udp@^5.0.2", - "npm:@netlify/opentelemetry-utils@^2.0.2", - "npm:@netlify/plugins-list@^6.81.6", - "npm:@netlify/run-utils@^6.0.3", - "npm:@netlify/zip-it-and-ship-it@14.7.0", - "npm:@opentelemetry/api@1.8", - "npm:@opentelemetry/sdk-trace-base@1.24", - "npm:@sindresorhus/slugify@2", - "npm:@types/node@^18.19.111", - "npm:ansi-escapes@7", - "npm:ansis@^4.1.0", - "npm:atob@^2.1.2", - "npm:ava@5", - "npm:c8@10", - "npm:clean-stack@5", - "npm:cpy-cli@5", - "npm:cpy@11", - "npm:execa@8", - "npm:fdir@^6.0.1", - "npm:figures@6", - "npm:filter-obj@6", - "npm:get-node@^14.2.1", - "npm:get-port@7", - "npm:has-ansi@6", - "npm:hot-shots@11.4.0", - "npm:ignore@7", - "npm:indent-string@5", - "npm:is-plain-obj@4", - "npm:keep-func-props@6", - "npm:log-process-errors@11", - "npm:memoize-one@6", - "npm:micro-memoize@^5.1.1", - "npm:minimatch@^10.2.4", - "npm:npm-run-all2@6", - "npm:os-name@6", - "npm:p-event@6", - "npm:p-filter@4", - "npm:p-locate@6", - "npm:p-map@7", - "npm:p-reduce@3", - "npm:package-directory@8", - "npm:path-exists@5", - "npm:pretty-ms@9", - "npm:process-exists@5", - "npm:ps-list@8", - "npm:read-package-up@11", - "npm:readdirp@4", - "npm:resolve@^2.0.0-next.5", - "npm:rfdc@^1.3.0", - "npm:safe-json-stringify@^1.2.0", - "npm:semver@^7.3.8", - "npm:string-width@7", - "npm:supports-color@10", - "npm:terminal-link@4", - "npm:tinyspy@^4.0.3", - "npm:tmp-promise@^3.0.2", - "npm:ts-node@^10.9.1", - "npm:tsd@0.33", - "npm:typescript@5", - "npm:vitest@3", - "npm:yaml@^2.8.0", - "npm:yargs@^17.6.0", - "npm:yarn@^1.22.22", - "npm:zod@^3.25.76" - ] - } - }, - "packages/build-info": { - "packageJson": { - "dependencies": [ - "npm:@bugsnag/js@8", - "npm:@iarna/toml@^2.2.5", - "npm:@playwright/test@^1.53.1", - "npm:@types/node@^18.19.111", - "npm:@types/semver@^7.3.13", - "npm:@vitest/ui@3", - "npm:dot-prop@9", - "npm:execa@8", - "npm:find-up@7", - "npm:memfs@4", - "npm:minimatch@^10.2.4", - "npm:read-pkg@9", - "npm:semver@^7.3.8", - "npm:typescript@5", - "npm:unionfs@^4.4.0", - "npm:vitest@3", - "npm:yaml@^2.8.0", - "npm:yargs@^17.6.0" - ] - } - }, - "packages/cache-utils": { - "packageJson": { - "dependencies": [ - "npm:@types/node@^18.19.111", - "npm:cpy@11", - "npm:get-stream@9", - "npm:globby@14", - "npm:junk@4", - "npm:locate-path@7", - "npm:move-file@3", - "npm:readdirp@4", - "npm:tmp-promise@3", - "npm:typescript@5", - "npm:vitest@3" - ] - } - }, - "packages/config": { - "packageJson": { - "dependencies": [ - "npm:@iarna/toml@^2.2.5", - "npm:@netlify/api@^14.0.19", - "npm:@netlify/headers-parser@^9.0.3", - "npm:@netlify/redirect-parser@^15.0.4", - "npm:@types/node@^18.19.111", - "npm:ava@5", - "npm:c8@10", - "npm:chalk@5", - "npm:cron-parser@^4.1.0", - "npm:deepmerge@^4.2.2", - "npm:dot-prop@9", - "npm:execa@8", - "npm:fast-safe-stringify@^2.0.7", - "npm:figures@6", - "npm:filter-obj@6", - "npm:find-up@7", - "npm:has-ansi@6", - "npm:indent-string@5", - "npm:is-ci@4", - "npm:is-plain-obj@4", - "npm:map-obj@5", - "npm:omit.js@^2.0.2", - "npm:p-locate@6", - "npm:path-type@6", - "npm:read-package-up@11", - "npm:tmp-promise@^3.0.2", - "npm:tomlify-j0.4@3", - "npm:typescript@5", - "npm:validate-npm-package-name@5", - "npm:yaml@^2.8.0", - "npm:yargs@^17.6.0", - "npm:zod@^4.0.5" - ] - } - }, - "packages/edge-bundler": { - "packageJson": { - "dependencies": [ - "npm:@import-maps/resolve@2", - "npm:@netlify/edge-functions-bootstrap@^3.1.0", - "npm:@sveltejs/acorn-typescript@^1.0.9", - "npm:@types/node@^18.19.111", - "npm:@types/semver@^7.3.9", - "npm:@vitest/coverage-v8@3", - "npm:acorn@^8.15.0", - "npm:ajv-errors@3", - "npm:ajv@^8.11.2", - "npm:archiver@7", - "npm:better-ajv-errors@^1.2.0", - "npm:chalk@^5.4.0", - "npm:common-path-prefix@3", - "npm:cpy@^11.1.0", - "npm:env-paths@3", - "npm:esbuild@0.28.0", - "npm:execa@8", - "npm:find-up@7", - "npm:get-port@7", - "npm:nock@14", - "npm:node-stream-zip@^1.15.0", - "npm:npm-run-all2@6", - "npm:p-retry@6", - "npm:p-wait-for@5", - "npm:parse-imports@^2.2.1", - "npm:path-key@4", - "npm:semver@^7.3.8", - "npm:tar@^7.5.12", - "npm:tmp-promise@^3.0.3", - "npm:typescript@5", - "npm:urlpattern-polyfill@8.0.2", - "npm:vitest@3" - ] - } - }, - "packages/functions-utils": { - "packageJson": { - "dependencies": [ - "npm:@netlify/zip-it-and-ship-it@14.7.0", - "npm:@types/node@^18.19.111", - "npm:cpy@11", - "npm:path-exists@5", - "npm:sort-on@6", - "npm:tmp-promise@3", - "npm:typescript@5", - "npm:vitest@3" - ] - } - }, - "packages/git-utils": { - "packageJson": { - "dependencies": [ - "npm:@types/node@^18.19.111", - "npm:execa@8", - "npm:map-obj@5", - "npm:micro-memoize@^5.1.1", - "npm:micromatch@^4.0.2", - "npm:typescript@5", - "npm:vitest@3" - ] - } - }, - "packages/headers-parser": { - "packageJson": { - "dependencies": [ - "npm:@iarna/toml@^2.2.5", - "npm:@types/node@^18.19.111", - "npm:escape-string-regexp@5", - "npm:fast-safe-stringify@^2.0.7", - "npm:is-plain-obj@4", - "npm:map-obj@5", - "npm:path-exists@5", - "npm:typescript@5", - "npm:vitest@3" - ] - } - }, - "packages/js-client": { - "packageJson": { - "dependencies": [ - "npm:@netlify/open-api@^2.52.0", - "npm:@types/node@^18.19.111", - "npm:from2-string@^1.1.0", - "npm:nock@13", - "npm:node-fetch@3", - "npm:p-wait-for@5", - "npm:picoquery@^2.5.0", - "npm:ts-node@^10.9.1", - "npm:typescript@5", - "npm:vitest@^3.2.3" - ] - } - }, - "packages/nock-udp": { - "packageJson": { - "dependencies": [ - "npm:@types/node@^18.19.111", - "npm:typescript@5", - "npm:vitest@3" - ] - } - }, - "packages/opentelemetry-sdk-setup": { - "packageJson": { - "dependencies": [ - "npm:@honeycombio/opentelemetry-node@~0.7.1", - "npm:@netlify/opentelemetry-utils@~2.0.2", - "npm:@opentelemetry/api@1.8", - "npm:@opentelemetry/core@1.24", - "npm:@opentelemetry/resources@1.24", - "npm:@opentelemetry/sdk-trace-base@1.24", - "npm:@opentelemetry/semantic-conventions@1.24", - "npm:@types/node@^18.19.111", - "npm:@vitest/ui@3", - "npm:read-package-up@11", - "npm:typescript@5", - "npm:vitest@3", - "npm:yargs-parser@^21.1.1" - ] - } - }, - "packages/opentelemetry-utils": { - "packageJson": { - "dependencies": [ - "npm:@opentelemetry/api@1.8", - "npm:@opentelemetry/sdk-trace-base@1.24", - "npm:@opentelemetry/sdk-trace-node@1.24", - "npm:@types/node@^18.19.111", - "npm:@vitest/ui@3", - "npm:typescript@5", - "npm:vitest@3" - ] - } - }, - "packages/redirect-parser": { - "packageJson": { - "dependencies": [ - "npm:@iarna/toml@^2.2.5", - "npm:@types/node@^18.19.111", - "npm:fast-safe-stringify@^2.1.1", - "npm:is-plain-obj@4", - "npm:path-exists@5", - "npm:typescript@5", - "npm:vitest@3" - ] - } - }, - "packages/run-utils": { - "packageJson": { - "dependencies": [ - "npm:@types/node@^18.19.111", - "npm:execa@8", - "npm:semver@^7.3.8", - "npm:typescript@5", - "npm:vitest@3" - ] - } - }, - "packages/testing": { - "packageJson": { - "dependencies": [ - "npm:@netlify/build@*", - "npm:@netlify/config@*", - "npm:@types/lodash-es@^4.17.6", - "npm:@types/node@^18.19.111", - "npm:ava@5", - "npm:c8@10", - "npm:cpy@11", - "npm:execa@8", - "npm:fast-safe-stringify@^2.0.7", - "npm:figures@6", - "npm:get-bin-path@11", - "npm:get-port@7", - "npm:get-stream@9", - "npm:is-plain-obj@4", - "npm:lodash-es@^4.17.21", - "npm:path-key@4", - "npm:strip-ansi@7", - "npm:tmp-promise@^3.0.2", - "npm:typescript@5" - ] - } - }, - "packages/zip-it-and-ship-it": { - "packageJson": { - "dependencies": [ - "npm:@babel/parser@^7.22.5", - "npm:@babel/types@^7.28.5", - "npm:@netlify/binary-info@1", - "npm:@netlify/serverless-functions-api@2.16.0", - "npm:@netlify/types@2.7.0", - "npm:@types/archiver@6.0.4", - "npm:@types/is-ci@3.0.4", - "npm:@types/node@20.19.33", - "npm:@types/normalize-path@3.0.2", - "npm:@types/resolve@1.20.6", - "npm:@types/semver@7.7.1", - "npm:@types/unixify@1.0.2", - "npm:@types/yargs@17.0.35", - "npm:@vercel/nft@0.29.4", - "npm:@vitest/coverage-v8@3", - "npm:archiver@7", - "npm:browserslist@4.28.1", - "npm:cardinal@2.1.1", - "npm:common-path-prefix@3", - "npm:copy-file@11", - "npm:cpy@11.1.0", - "npm:decompress@4.2.1", - "npm:deepmerge@^4.3.1", - "npm:es-module-lexer@1", - "npm:esbuild@0.28.0", - "npm:execa@8", - "npm:fast-glob@^3.3.3", - "npm:filter-obj@6", - "npm:find-up@7", - "npm:get-stream@9.0.1", - "npm:is-ci@4.1.0", - "npm:is-path-inside@4", - "npm:junk@4", - "npm:lambda-local@2.2.0", - "npm:locate-path@7", - "npm:merge-options@^3.0.4", - "npm:minimatch@^10.2.4", - "npm:normalize-path@3", - "npm:p-map@7", - "npm:path-exists@5", - "npm:precinct@12", - "npm:require-package-name@^2.0.1", - "npm:resolve@^2.0.0-next.1", - "npm:semver@^7.3.8", - "npm:source-map-support@0.5.21", - "npm:tmp-promise@^3.0.2", - "npm:toml@3", - "npm:typescript@5.9.3", - "npm:unixify@1", - "npm:urlpattern-polyfill@8.0.2", - "npm:vitest@3", - "npm:yargs@17", - "npm:zod@^3.23.8" - ] - } - } - } - } -} From fa1d3f35a49fa20b0a9aa492a0959616561b7a7b Mon Sep 17 00:00:00 2001 From: Chris Shiohama Date: Tue, 9 Jun 2026 09:35:03 -0700 Subject: [PATCH 10/10] move cleanup to finally block --- packages/build/tests/plugins/tests.js | 96 ++++++++++++++------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/packages/build/tests/plugins/tests.js b/packages/build/tests/plugins/tests.js index 4656386ce9..9520e8b567 100644 --- a/packages/build/tests/plugins/tests.js +++ b/packages/build/tests/plugins/tests.js @@ -113,56 +113,62 @@ 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 systemLog = await tmp.file() - 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') - await systemLog.cleanup() - - t.true(systemLogContents.includes('plugin "./plugin.js" node support range does NOT include v22')) + 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() - 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') - await systemLog.cleanup() - - 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')) + 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) => {