mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 17:28:08 +00:00
* fix(cli): refuse an unsupported Node.js release before linking The dispatcher imported the compiled runtime before checking the runtime range, so a release outside `engines.node` failed while linking a module that imports `createZstdDecompress`. Callers saw a module-linking stack trace from every command, including `--version` and `doctor`, instead of the refusal the installer and `engines` already promise. Check `engines.node` from the package read the dispatcher already performs, before the first compiled import. The helper has no imports of its own: the dispatcher must admit the runtime before any dependency can be resolved. Fixes #9296 * fix(cli): ship and align runtime admission * fix(ci): cover packaged runtime admission helper * test(cli): assert Node admission with structured range results Export satisfiesNodeRange and cover caret, minimum, union, and non-release versions. The dispatcher proves the compiled runtime never loads by the absence of its marker file, and compares stderr to the helper's own message. --------- Co-authored-by: 2SinceJuly <202121119+2SinceJuly@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
55 lines
2.2 KiB
JavaScript
55 lines
2.2 KiB
JavaScript
/**
|
|
* Reject a Node.js release the compiled runtime cannot link against.
|
|
*
|
|
* The dispatcher imports compiled modules before it can report anything, and
|
|
* those modules import Node APIs that some releases do not export. Checking the
|
|
* published range here keeps that failure a readable refusal instead of a
|
|
* module-linking stack trace.
|
|
*
|
|
* `range` is the package's own `engines.node`, so the dispatcher and the
|
|
* installer cannot drift apart. This module deliberately has no imports: the
|
|
* dispatcher must admit the runtime before any dependency, including `semver`,
|
|
* can be resolved.
|
|
*/
|
|
export const unsupportedRuntimeMessage = (range, version) => {
|
|
if (typeof range !== "string" || range.length === 0) return undefined;
|
|
if (satisfiesNodeRange(range, version)) return undefined;
|
|
return (
|
|
`REA requires ${supportedReleaseProse(range)}; this process runs Node.js ${version}. ` +
|
|
"Install a supported release, then retry."
|
|
);
|
|
};
|
|
|
|
/** Human-readable release families for an `engines.node` range. */
|
|
export const supportedReleaseProse = (range) =>
|
|
`Node.js ${range
|
|
.split("||")
|
|
.map((clause) => {
|
|
const floor = /^(\^|>=)\s*(\d+\.\d+\.\d+)$/.exec(clause.trim());
|
|
if (floor === null) return clause.trim();
|
|
return floor[1] === ">="
|
|
? `${floor[2]}+`
|
|
: `${floor[2].split(".")[0]}.x (>=${floor[2]})`;
|
|
})
|
|
.join(", ")}`;
|
|
|
|
const VERSION = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
const FLOOR = /^(>=|\^)\s*(\d+)\.(\d+)\.(\d+)$/;
|
|
|
|
/** Every clause of the published range is `^major.minor.patch` or `>=`. */
|
|
export const satisfiesNodeRange = (range, version) => {
|
|
const found = VERSION.exec(version);
|
|
// A prerelease or a non-release spelling is never an admitted release.
|
|
if (found === null) return false;
|
|
const [, major, minor, patch] = found;
|
|
return range.split("||").some((clause) => {
|
|
const floor = FLOOR.exec(clause.trim());
|
|
if (floor === null) return false;
|
|
const [, operator, floorMajor, floorMinor, floorPatch] = floor;
|
|
if (Number(major) !== Number(floorMajor))
|
|
return operator === ">=" && Number(major) > Number(floorMajor);
|
|
if (minor !== floorMinor) return Number(minor) > Number(floorMinor);
|
|
return Number(patch) >= Number(floorPatch);
|
|
});
|
|
};
|