diff --git a/.changeset/six-dingos-add.md b/.changeset/six-dingos-add.md new file mode 100644 index 000000000000..5bd4667831c2 --- /dev/null +++ b/.changeset/six-dingos-add.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix CLI node version check diff --git a/packages/astro/astro.js b/packages/astro/astro.js index b873016bcb44..1fcf633a9f28 100755 --- a/packages/astro/astro.js +++ b/packages/astro/astro.js @@ -4,7 +4,6 @@ // ISOMORPHIC FILE: NO TOP-LEVEL IMPORT/REQUIRE() ALLOWED // This file has to run as both ESM and CJS on older Node.js versions -// Assume ESM to start, and then call `require()` below once CJS is confirmed. // Needed for Stackblitz: https://github.com/stackblitz/webcontainer-core/issues/281 const CI_INSTRUCTIONS = { @@ -14,64 +13,41 @@ const CI_INSTRUCTIONS = { VERCEL: 'https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version', }; +// Hardcode supported Node.js version so we don't have to read differently in CJS & ESM. +const engines = '>=16.12.0'; +const skipSemverCheckIfAbove = 16; + /** `astro *` */ async function main() { - // Check for ESM support. - // Load the "supports-esm" package in an way that works in both ESM & CJS. - let supportsESM = - typeof require !== 'undefined' - ? require('supports-esm') - : (await import('supports-esm')).default; - - // Check for CJS->ESM named export support. - // "path-to-regexp" is a real-world package that we depend on, that only - // works in later versions of Node with advanced CJS->ESM support. - // If `import {compile} from 'path-to-regexp'` will fail, we need to know. - if (supportsESM) { - const testNamedExportsModule = await import('path-to-regexp'); - supportsESM = !!testNamedExportsModule.compile; - } - - // Preflight check complete. Enjoy! ✨ - if (supportsESM) { - return import('./dist/cli/index.js') - .then(({ cli }) => cli(process.argv)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - } - const version = process.versions.node; - - // Not supported (incomplete ESM): It's very difficult (impossible?) to load the - // dependencies below in an unknown module type. If `require` is undefined, then this file - // actually was run as ESM but one of the ESM preflight checks above failed. In that case, - // it's okay to hard-code the valid Node versions here since they will not change over time. - if (typeof require === 'undefined') { - console.error(`\nNode.js v${version} is not supported by Astro! -Please upgrade to a supported version of Node.js: ">=16.12.0"\n`); + // Fast-path for higher Node.js versions + if ((parseInt(version) || 0) <= skipSemverCheckIfAbove) { + try { + const semver = await import('semver'); + if (!semver.satisfies(version, engines)) { + await errorNodeUnsupported(); + return; + } + } catch { + await errorNodeUnsupported(); + return; + } } - // Not supported: Report the most helpful error message possible. - const pkg = require('./package.json'); - const ci = require('ci-info'); - const semver = require('semver'); - const engines = pkg.engines.node; + return import('./dist/cli/index.js') + .then(({ cli }) => cli(process.argv)) + .catch((error) => { + console.error(error); + process.exit(1); + }); +} - // TODO: Remove "semver" in Astro v1.0: This is mainly just to check our work. Once run in - // the wild for a bit without error, we can assume our engine range is correct and won't - // change over time. - const isSupported = semver.satisfies(version, engines); - if (isSupported) { - console.error(`\nNode.js v${version} is not supported by Astro! -Supported versions: ${engines}\n -Issue Detected! This Node.js version was expected to work, but failed a system check. -Please file an issue so that we can take a look: https://github.com/withastro/astro/issues/new\n`); - } else { - console.error(`\nNode.js v${version} is not supported by Astro! +async function errorNodeUnsupported() { + console.error(`\ +Node.js v${process.versions.node} is not supported by Astro! Please upgrade Node.js to a supported version: "${engines}"\n`); - } + + const ci = typeof require !== 'undefined' ? require('ci-info') : await import('ci-info'); // Special instructions for CI environments, which may have special steps needed. // This is a common issue that we can help users with proactively.