Is your feature request related to a problem? Please describe.
Currently there is no standard way for the author of an app to specify which versions of node are expected/required. Author's are left to their own devices for ways to enforce this.
Describe the solution you'd like
The [hopefully] obvious approach to this is to leverage the engines field in package.json. It already serves much the same purpose for NPM. Node, however, ignores this field entirely.
Node should inspect the applicable package.json file (the same file it already looks to for main and exports fields) to see if a engines.node version is defined. If so, it should apply the standard semver semantics to determine if the current version is incompatible with the engines version. If so, it should throw Error('Invalid node version. Current version is ${process.version}, but ${package.json path} engines.node requires ${package.json engines.node}.')
While this won't be catchable via try-catch (error will occur outside context of user script), it should be trappable via process.on('uncaughtException').
Describe alternatives you've considered
The runtime-engine-check module provides similar behavior
Edit: Some supporting material:
Node should inspect the applicable package.json file (the same file it already looks to for
mainandexportsfields)
I didn't comment on it at the time but I fully expected the addition of new properties to package.json to result in claims that "you inspect X, therefore you should inspect Y" and here we are. :-/
FWIW, the main property is widely regarded as a design error that can't be removed because of backwards compatibility. Node.js should have been agnostic to the package format but mistakes were made...
Throwing an exception when the engines field is incompatible is IMO a no-go because some module authors (including yours truly) use it as a support hint, not as a hard limit.
Printing a warning _might_ be acceptable but since end users have little control over their second-level dependencies, warnings get annoying fast and it's only a matter of time before someone asks for a switch to turn them off again.
Performance is an issue: parsing package.json files isn't cheap. The module loader goes to great lengths to avoid having to do it.
Parsing semver strings is another issue. We'd have to bundle a semver parser or write our own.
Honestly, I see more downsides than upsides.
I totally understand wanting this, but I'm reluctant to see it in core. @bnoordhuis lays out some good reasons. I would add:
We should (probably) discourage version-sniffing where possible. In some cases, it is not possible to avoid. But where possible, feature detection should be preferred. In addition to just general "let the polyfills polyfill", this also seems to align with the approach of JavaScript in the browser.
For people that really want to prevent their apps from running on older versions of Node.js, this is already available in code:
if (+process.versions.node.split('.')[0] < 12) {
throw new Error('Sorry, this app requires Node.js 12.x or later');
}
process.versions was added in Node.js 0.2.0 (or so the docs say) so that should be pretty robust. It's not as elegant as a JSON field, perhaps, but sometimes less magic is better.
Thanks for taking the time to respond. Given that node only looks for a top-level package.json file when pointed at a directory, which I assume is not a particularly common use case (and something I'd failed to consider), your pushback makes sense.
but since end users have little control over their second-level dependencies, warnings get annoying fast
To be clear, the intent here was that this apply only to the top-level package.json file that sits alongside the script being run. The idea being that this would help teams of developers insure they're all running the expected version of node.
Speaking of which (for anyone stumbling across this while looking for similar functionality), it seems like the best options for this currently are:
--engine-strict. This will cause NPM (but not Node) to fail when there's a node version mismatch
Most helpful comment
I didn't comment on it at the time but I fully expected the addition of new properties to package.json to result in claims that "you inspect X, therefore you should inspect Y" and here we are. :-/
FWIW, the
mainproperty is widely regarded as a design error that can't be removed because of backwards compatibility. Node.js should have been agnostic to the package format but mistakes were made...Throwing an exception when the
enginesfield is incompatible is IMO a no-go because some module authors (including yours truly) use it as a support hint, not as a hard limit.Printing a warning _might_ be acceptable but since end users have little control over their second-level dependencies, warnings get annoying fast and it's only a matter of time before someone asks for a switch to turn them off again.
Performance is an issue: parsing package.json files isn't cheap. The module loader goes to great lengths to avoid having to do it.
Parsing semver strings is another issue. We'd have to bundle a semver parser or write our own.
Honestly, I see more downsides than upsides.