corepack icon indicating copy to clipboard operation
corepack copied to clipboard

Feature request: corepack run

Open navarroaxel opened this issue 4 years ago • 26 comments

Add corepack run with the same behavior as [npm|yarn] run to reduce the necessity of a package manager to run user-defined scripts in the package.json. e.g. with this start script

"scripts": {
  "start": "node index.js"
}

we don't need a package manager to run:

$ corepack run start

Also, we should add the Corepack's wrappers (npm, pnpm and yarn) to the PATH automatically. Achieving the same effect like corepack enable but only on the context of the command execution to support scripts like the following:

"test": "npm run env:test && jest",

Without adding the package managers as global commands.

navarroaxel avatar Sep 15 '21 18:09 navarroaxel

This isn't possible. Each package manager has different implementations of run unique to their implementation details, which Corepack couldn't replicate without becoming a package manager, which is out of scope 🙂

arcanis avatar Sep 15 '21 19:09 arcanis

Maybe you can forward the args if you implement #25, then corepack run lint --fix run yarn run lint --fix or npm run lint -- --fix etc

navarroaxel avatar Sep 15 '21 19:09 navarroaxel

I believe there are two slightly different issues here. The choice of the package manager for the root command and for the commands inside scripts. The distinction is important because the commands in the script field are published to the registry and may be run by consumers, while the root command is only invoked in the context of the current package.

In particular, situations like the following are problematic as they require the consumer to have yarn on its system:

{
  "scripts": {
    "compile": "node-gyp ...",
    "download": "...",
    "postinstall": "yarn run download || yarn run compile"
  }
}

Yarn 2+ supports "package manager independent run" by aliasing run to yarn run so you can write:

{
  "scripts": {
    "compile": "node-gyp ...",
    "download": "...",
    "postinstall": "run download || run compile"
  }
}

This is the best solution for portable scripts calling other scripts in my opinion, I just hope this is adopted by npm, pnpm and other package managers.

corepack run may still be useful to initiate the first command. It would then pick the package manager from the one defined in package.json and would allow contributors to always run corepack run test regardless of the pm chosen by the maintainers.

demurgos avatar Sep 23 '21 11:09 demurgos

@demurgos the "scripts" field can not be run by consumers - it can only be run by developers of the project. Only the "bin" field are things that can be executed by consumers.

ljharb avatar Sep 23 '21 17:09 ljharb

I think @demurgos' point was that postinstall scripts find it difficult to execute scripts in a portable way across package managers (in a way that whatever package manager is used to install the package will also be called to run the scripts). The two options are currently to either hardcode yarn run / npm run, or to write an awkward reference to $npm_execpath.

Yarn supports calling run <script name> within scripts (ie without expliciting the package manager); if npm/pnpm supported that it would solve this particular problem.

As for corepack run, I'm not sure how useful it would be in practice ... you typically have to install your deps to run scripts successfully, and I suspect muscular memory would make corepack run a rarely use command 🤔

arcanis avatar Sep 23 '21 17:09 arcanis

@demurgos the "scripts" field can not be run by consumers - it can only be run by developers of the project. Only the "bin" field are things that can be executed by consumers.

Some of the the scripts fields are definitely executed by package consumers (so you don't control their environment). These are mostly lifecycle scripts. install/postinstall are probably the best example because they are supported by all package managers: when a consumer installs a package, these scripts are always executed. I believe there are other fields such as prepare when using git:// dependencies, or the deprecated prepublish.

Even if I find run <script name> to be a cleaner solution to handle these cases, it requires support by the major package managers to be truly portable. corepack run <script name> could provide a portable solution without requiring extra work from package managers.

demurgos avatar Sep 24 '21 00:09 demurgos

Ha, good point, i wasn’t thinking about lifecycle scripts.

ljharb avatar Sep 24 '21 00:09 ljharb

I'm not sure how useful it would be in practice ... you typically have to install your deps to run scripts successfully, and I suspect muscular memory would make corepack run a rarely use command 🤔

I believe there are some legitimate usecases. Muscle memory works when you use the same package manager in all the projects you are working on but that is not always the case.

https://github.com/antfu/ni Seems to be somewhat popular to address some of this, not the best data point but it shows a bit of community thirst for something like this.

Would also help when writing a sharable CI config in orgs where they use multiple package managers. Simplifying running commands / installing deps / etc.

donferi avatar Nov 01 '21 18:11 donferi

Also checkout https://github.com/egoist/dum which solves most of these use cases.

I would like a feature like this, although I'm not sure this is the responsibility of corepack. Or rather, I wouldn't want it exposed as a public command through corepack cli.

Ideally, this feature would be exposed like node --run <scriptname>.

styfle avatar Jun 10 '22 16:06 styfle

Ideally, this feature would be exposed like node --run <scriptname>

Running scripts depend on knowledge unique to each package manager, so it couldn't be in Node (at best it perhaps could be in Corepack since it could then delegate to the right package manager, but even that could lead into conflict problems).

arcanis avatar Jun 10 '22 17:06 arcanis

What I mean is that I don't think the user should be interacting with the corepack cli (it could still be implemented in corepack under the hood though). For example, if corepack is enabled by default (#104), then users won't really ever use corepack cli so it would feel strange to suddenly introduce it to run scripts.

styfle avatar Jun 10 '22 18:06 styfle

What I mean is that I don't think the user should be interacting with the corepack cli (it could still be implemented in corepack under the hood though). For example, if corepack is enabled by default (#104), then users won't really ever use corepack cli so it would feel strange to suddenly introduce it to run scripts.

Adding a feature to corepack CLI doesn't force anyone to use it though, it may make that feature harder to discover, but that doesn't seem to be a good enough reason for rejecting it on that basis alone.

FWIW, it happened to me more than once to try to run e.g. corepack add … instead of corepack yarn add …, and I suppose that Corepack could have made the smart thing to redirect any unknown command to whatever package manager defined in the package.json. Yarn does have something similar, if it receives a command it doesn't know, it guesses if that's a script defined in package.json or a bin in node_modules. @arcanis has this pattern ever been problematic for Yarn?

aduh95 avatar Jun 10 '22 18:06 aduh95

@arcanis has this pattern ever been problematic for Yarn?

The situation is different - Yarn is a single program, so even if there's a conflict between its commands and the user scripts, those problem tends to be easily spotted (commands and scripts tend to do very different things).

In Corepack, we are abstracting multiple package managers, each with their own different behaviours (for example yarn run build:foo will run the build:foo script regardless of the workspace that declares it, whereas npm run build:foo will run it exclusively in the current workspace). While the semantics are often similar, the details aren't, and I feel like the subtle differences in behaviour could be extremely confusing for our users and painful for us as maintainers.

For this reason, I agree with @styfle that the corepack commands shouldn't be used in place of the individual package manager commands (which echoes my comment in https://github.com/nodejs/corepack/pull/100#discussion_r866678520), and rather than corepack yarn add ... you should just call yarn add ... (or npm add ... or pnpm add ...). It removes ambiguity, and lets each package manager own their full UX.

Adding a feature to corepack CLI doesn't force anyone to use it though

I don't agree with this - the JS community is large, and many many many people would misinterpret the very existence of such an optional feature in Node as meaning "it's official, you don't need to specify the package manager name anymore" - then they would get pissed when it doesn't work as they expect from one project to the other, or because it doesn't work for all commands, or [...] - eventually blaming it on either Corepack or package manager authors.

arcanis avatar Jun 10 '22 18:06 arcanis

Adding a feature to corepack CLI doesn't force anyone to use it though

I don't agree with this - the JS community is large, and many many many people would misinterpret the very existence of such an optional feature as meaning "you don't need to specify the package manager name" - then get pissed when it doesn't work as they expect from one project to the other, or because it doesn't work for all commands, or [...] - eventually blaming it on either Corepack or package manager authors.

To clarify, I agree that "we don't see a way to make it work without breaking users expectations" is a perfectly valid reason for not implementing it. I just wanted to point out that "I don't think the user should be interacting with the corepack cli" is not imo, because folks who don't want to use corepack cli won't be forced to use it whether or not we add features to it.

aduh95 avatar Jun 10 '22 19:06 aduh95

Just wanted to mention another package https://github.com/BendingBender/yarpm as a solution to the package.json scripts issue.

I liked that the readme for yarpm makes it clear that it is not trying to be a package manager abstraction layer and that package authors must make sure that their scripts are compatible with all package managers if they want to use yarpm in their package.json scripts. This seems important for package authors to know, that you don't just get compatibility with all package managers for free (to arcanis' point above).

x11x avatar Jul 05 '22 10:07 x11x

It sounds like the path forward here is to create a npm RFC for the run shorthand that yarn supports (mentioned above).

Are there any yarn docs that the npm team can reference to make sure it works the same way if/when they implement it?

styfle avatar Oct 13 '22 23:10 styfle

Adding a new top-level binary is likely to be a nonstarter; npx is already regretted.

ljharb avatar Oct 14 '22 03:10 ljharb

I should add a note about it to our documentation, for sure.

Adding a new top-level binary is likely to be a nonstarter; npx is already regretted.

This isn't a top-level binary in the same way you may think about it.

It's only available inside scripts commands, so it doesn't pollute the global user namespace (in terms of implementation, I suppose npm would add a run bin to node_modules/.bin).

arcanis avatar Oct 14 '22 05:10 arcanis

What would be the point of that? run as an alias to npm run?

ljharb avatar Oct 14 '22 06:10 ljharb

Make install scripts able to cross-refer to other scripts without having to hardcode package manager names (or use $npm_execpath, which practically noone does).

arcanis avatar Oct 14 '22 07:10 arcanis

(or use $npm_execpath, which practically noone does).

TIL, that would indeed fix the issue! I suspect most package.json authors are not aware of this.

aduh95 avatar Oct 14 '22 14:10 aduh95

I must have missed that above!

I tried $npm_execpath with the example above and it works today 🤩

{
  "scripts": {
    "compile": "echo 'compile done'",
    "download": "echo 'download done'",
    "postinstall": "$npm_execpath run download && $npm_execpath run compile"
  }
}
  • npm run postinstall - ✅ works
  • yarn run postinstall - ✅ works
  • pnpm run postinstall - ✅ works

styfle avatar Oct 14 '22 19:10 styfle

Oh no, I just realized that $npm_execpath doesn't work on Windows 🤦

That explains why no one would use it for postinstall.

styfle avatar Oct 14 '22 21:10 styfle

Although it doesn't solve the underlying portability problem, it actually works for Yarn Modern even on Windows.

arcanis avatar Oct 14 '22 21:10 arcanis

Looks like this might be solved with node run or perhaps node --run

  • https://github.com/nodejs/node/pull/52190
  • https://github.com/nodejs/node/pull/46534

styfle avatar Apr 04 '24 17:04 styfle