[discussion] How to handle devEngines.packageManager when one package manager calls another one for some commands
Currently the only way to ignore the devEngines check is to use the --force flag: https://github.com/npm/cli/blob/d006583e20731e8ae55cee94c3b7bd23cbd6f2d0/lib/base-cmd.js#L174
But that flag makes npm print a warning and also changes other things.
pnpm is passing through some commands to npm CLI (like dist-tag, publish, view, etc). So, we want to be able to run npm from pnpm, even if devEngines is set to pnpm only. Alternatively, we could recommend users to put both npm and pnpm to devEngines but then they might accidentally run "npm install" instead of "pnpm install".
Maybe you could add support for an environment variable that pnpm could set, when running npm CLI.
So this is a case where npm is being ran from pnpm for one of the various subtasks that it delegates to npm? Very interesting. Is there some sort of well-defined environment variable that pnpm sets so that npm can reliably know that we are actually "being ran from pnpm"?
Is there some sort of well-defined environment variable that pnpm sets so that npm can reliably know that we are actually "being ran from pnpm"?
Sort of? https://github.com/pnpm/pnpm/blob/0748be2424b83cd988e5414883437c10ba657ec1/exec/run-npm/src/index.ts#L17
Overloading a COREPACK_ variable probably wouldn’t be wise though…
It looks like pnpm sets npm_config_user_agent in the environment, which npm will pick up and set as its own user-agent config
~ $ set -x npm_config_user_agent 'gar/test 1.0.0 neat'
~ $ npm config get user-agent
gar/test 1.0.0 neat
It looks like pnpm sets
npm_config_user_agentin the environment, which npm will pick up and set as its own user-agent config
Do you really want to hardcode in pnpm support? What if, for example, Vlt decided to shell out? Or pnpm updated their UA? IDK, maybe it’s a stable enough list it’s fine, but it feels fragile.
I don't think it's outside the realm of practicality to parse the user-agent in a way that informs the packageManager portion of the engines check. Your points are valid though, in that this is being coupled to something without any really well defined spec, but more of a history of pretty stable patterns. It would be much more robust to have this be well defined.
This currently seems like an intractable problem due to the following unanswered questions (and there may be more.
- What is a reproducible canonical way to know what package manager name and version is being used?
- What does it actually mean to limit packageManager in devEngines?
- If package manager A shells out to package manager B for operations, does the version of package manager B matter?
Ignoring these warnings is dangerous, but without any kind of roadmap to have a better way to know if they need to be displayed, my fear is that this is the direction folks will go. ~I am going to close this issue not because it is not real, but because this is a much larger question than we can track in this context.~ I am going to move this issue to our rfc repo, which is where larger discussions like this take place.
One solution could be that we update the devEngines spec to isolate for certain commands. Unfortunately npm will currently exit with an error if it encounters invalid properties in the devEngines field.
"devEngines": {
"os": {
"other": "thing",
"name": "windows",
"onFail": "error"
}
}
~/D/s/devEngines $ npm ls
npm error Invalid property "other" for "os"
npm error A complete log of this run can be found in: /Users/wraithgar/.npm/_logs/2025-08-21T20_37_08_222Z-debug-0.log
~/D/s/devEngines $ npm pkg get devEngines
npm error Invalid property "other" for "os"
npm error A complete log of this run can be found in: /Users/wraithgar/.npm/_logs/2025-08-21T20_37_16_915Z-debug-0.log
It also seems like a huge burden to require package maintainers to know or care about this very edge case part of the ecosystem. A difficult problem to solve, for sure.
Maybe instead of shelling out to npm at all, pnpm could use the same packages npm uses internally? npm might not even exist on the system.
Maybe instead of shelling out to npm at all, pnpm could use the same packages npm uses internally? npm might not even exist on the system.
I suggested that here, but it was quickly rejected because a bunch of npm only exists in the CLI, for example, OIDC is only implemented in the client. It’s too significant of a maintenance burden for pnpm (+ others) to fork npm to get at shared code, when there’s a canonical implementation sitting on every Node.js user’s computer.
Seems like the better solution then is for npm to extract more things into packages to encourage reuse?
OIDC is only implemented in the client
We definitely have plans to isolate that code into a separate package, the initial implementation didn't do that because we didn't know where the boundaries would be and it was quicker to implement inline for now and separate later.
I agree that it would be better to implement all commands inside pnpm natively but I don't think we'll be able to do that in the near future.
It is probably not justified to add this scoping syntax to devEngines.packageManager only because of pnpm.
I guess pnpm could automatically add npm to devEngines before running npm.
I’m still a little lost as to why an environment variable that just tells npm to ignore devEngines isn’t viable. Because people might abuse it? Because it puts pressure on pnpm to implement it themselves?
It’s all good and well to want a good solution, but it feels like we’re letting perfect be the enemy of good enough here. Couldn’t we do the simple fix now and restructure npm later?
Such an environment variable would also allow devEngines to be used properly in CI (where it's a subset of engines), so I think it'd be a good idea. (Without such a mechanism, devEngines isn't useful for projects, only applications, which was the only point of proposing it)
I just ran into a situation where this would be a nice to have as well. --force works in this case, but could be very problematic in other cases. I think a --ignore-dev-engines option would be the best UX (and it's associated config env var).
Just to make sure we're thinking through all the possible iterations here. There is an engine-strict flag that npm has which is the equivalent to the fail config for devEngines.
I'm not sure a similar flag would work here because we're talking about suppressing warnings, not toggling a failure mode.
--force is absolutely the last resort here. It's a giant hammer that ignores ALL warnings and safeguards.
Thinking more broadly I could also see a suggestion being made that devEngines be extended for context. For example reification vs search vs account administration. This seems like a LOT of work for both the package manager and the user to work out. Adding a flag (with sufficient "do not use this if you are not a package manager" warnings in the config description) does seem like a sensible approach.
Having said all that I think ignore is probably a less helpful declarative in the config name than something more specific for dev engines warnings themselves, which would default to true. But I'm not super firm on that, and don't want to be the dev who bogs this down in bike shedding.
Regardless of what it's called, a config flag that says "only look at engines, not devEngines" would solve the indicated use cases.
Good point, ignoring devEngines altogether is likely more clear than trying to say "just don't log the warnings"
There's definitely a missing config here; ex.
--loglevel=<silent|error|warn|notice|http|info|verbose|silly> controls logging...
--engine-strict=<true|false> controls whether to gate/error on failed checks...
--engine-types=<all|prod|dev|none> is, hypothetically (bikesheds aside), a config which would define the fields to check
--force removes all protections/ignores all checks (ie. the nuclear option)
Just a thought/suggestion...
Engines checks are also relevant for commands like npm run and probably every other command so we need to think beyond just reification here. Again I'll point out the problem with that I said above, it's a HUGE ask for developers
For example reification vs search vs account administration. This seems like a LOT of work for both the package manager and the user to work out.