pnpm: Don't run the pre/post scripts

NOTE: It is possible to return how pre/post scripts worked by setting the enable-pre-post-scripts setting to true (since v6.1.0)


When running pnpm run foo, don’t run the prefoo script and the postfoo script.

Yarn 2 already deprecated the pre/post scripts:

In particular, we intentionally don’t support arbitrary pre and post hooks for user-defined scripts (such as prestart). This behavior, inherited from npm, caused scripts to be implicit rather than explicit, obfuscating the execution flow. It also led to surprising executions with yarn serve also running yarn preserve.

https://yarnpkg.com/advanced/lifecycle-scripts

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 40
  • Comments: 53 (16 by maintainers)

Commits related to this issue

Most upvoted comments

I don’t see much value in it. You may just add "start": "pnpm prestart && node server.js" if you need it.

It is not some action that you do frequently. Mostly you just add a script once and never change it.

May I disagree? Nearly all my scripts have been written with these “pre” & “post” hooks to deal specifically with env variables or clean actions. And I know I am not the only one. Just to be clear I honestly don’t care what yarn is doing!

I don’t see much value in it. You may just add "start": "pnpm prestart && node server.js" if you need it.

It is not some action that you do frequently. Mostly you just add a script once and never change it.

What a mess, this behavior is different to npm and (at least for me) unexpected. It would make sense to have at least a flag (like --run-scripts) to restore the default behavior.

@cdaringe

  • Again, I believe that in most area in this world, “mess” is not a rude word.
  • Free does not mean “accept and shup-up”.
  • Before trying to give advice, have a look around to see if (maybe) all what you believe should have been done, has not been done already.
  • This issue means simply that some dev are going to re-think their logic, their tools, their hopes.

And these was of course my last words here.

When working with Typescript projects, prebuild is a very common script I work with to rimraf dist and while I can add that to the build script it would be nice to allow the prebuild to work directly. I’d be happy to try and figure out how we can make this work via a config or something so we can add it back in.

@stalniy For now, you can solve this problem by adding this config to your project.

.npmrc:

enable-pre-post-scripts=true

It’s problem for me too. It’s breaking change.
I have to add this .npmrc into my every projects that use pnpm. 😅

if you need pre/post, you can get similar ergonomics + explicitness (which this changes aims to acheive) with https://github.com/mysticatea/npm-run-all (run-p/run-s)

idea about the “mess” which is for me still a somehow polite word.

In other words, “someone did something I don’t agree with, so I can be rude.” Insults and negativity–especially over something offered for free–are never productive. Consider sending patches, proposing solutions, debating pros and cons, etc.

Complaining like this rude, and helps no one. Most of us are taught this from a young age, as it has nearly global acceptance, across cultures. For reasons not yet clear to me, software engineers, in issues trackers that are not their own, feel comfortable waiving this value in favor selfishness.

Consider productive & helpful questions like:

  • <authors>, is it a design goal of yours to have parity between pnpm & npm? If so, do you see this as a violation?”
  • <authors>, do you agree or disagree that lifecycle hooks are helpful? is removing them more hurtful than helpful?”
  • <authors>, is the yarn teams’ analysis and decision justifiable, or could they be over-reacting to to edge cases?”

@cdaringe maybe I’m missing something here. Yes there was a complaint about the functionality, but it wasn’t just absurdly rude. Perhaps “mess” means something different to you than how I’m reading it. Either way, the dev did mention why they thought this (it’s different than npm and unexpected) and mentioned a possible solution (using a --run-scripts flag to possibly allow for running these scripts).

You’re right that in open source, complaining never gets anywhere, trust me, as a maintainer of NestJS and a few other packages I get that. It’s one of the reasons I mentioned at the very least this should be well documented.

pnpm stands for performant npm which is why so many of us are confused at this different functionality. If this is a design goal of pnpm, to be like npm, then this definitely would be something that is no longer aligned.

I see where the yarn team is coming from (at least in regards to running accidental scripts), so maybe instead of just pre and post we could use something like pre: and post: to help avoid preserve from being ran.

As I mentioned in my first comment on this thread I do find great use in these lifecycle scripts, and have been chasing why I’ve been having CI failures recently until I found that these scripts are no longer running. Now, it’s my fault for using @latest of the pnpm GitHub Action, but it was still a shock to see to say the least.

configure enable-pre-post-scripts=true in .npmrc is work for me

v6.1.0 has the enable-pre-post-scripts setting and it is added to the docs: https://pnpm.io/cli/run

For what it’s worth, this might be something good to document on the pnpm run page, as, up until v6 to my knowledge, pnpm mirrored npm’s functionality, including running pre/post scripts of custom commands. As npm v7 still supports this, it’s definitely a bit divergent from what I thought the functionality would be. I know the mention is in the changelog, but it would be nice to have it documented in the docs page as well, to help make it clearer. Just my two cents.

Ouch! This has seriously exploded me in the face today after migrating from npm to pnpm. I was expecting pnpm to be compatible with npm…

ok, could this be solved with another prefix, like pre_serve (or pre:serve or pre.serve)? the feature is very useful, I think its removal doesn’t help real users.

There are few points which make me confused:

  1. Removal of this hooks doesn’t improve codebase. I looked through the PR, just few lines were removed. So, maintenance of the project is not affected in any way
  2. Removal of this hooks force a lot of people to change a lot of packages (thankfully pnpm has a config option but yarn doesn’t!).
  3. Removal of this hooks makes my scripts to be harder to reason about

There are 2 very big pain points. And the question is whose this problem? Who will feel this pain? The answer - developers, open source contributors. I’m as a one of those guys, need to go over my 14 open source node projects and rewrite all scripts in package.json (that have been working for years without changes!), make them more uglier and unclear for no good reason (because in English there is “preserve” word which may conflict with “preserve” hook). I will spend this time doing stupid thing which will not help me to progress, improve my libs, provide better support, contribute to open source and help companies and people around to solve real problems.

As I said thankfully, I don’t use yarn and pnpm has an option. But this is a road in the wrong direction.

The real solution to this issue is to change hook names and use some extra symbol to separate hook name and script name. Even though it may conflict with existing hooks in some codebase but impact will be much slower in comparison to complete removal of this feature. There is a big difference in renaming hook vs merging scripts together and ensure that you put all symbols in place.

P.S.: Sorry for criticizing this task, I like pnpm very much, you guys do amazing work! Except of this change 😃

@zkochan unfortunately this change effects several very large monorepos for us. We have several deployment and smoke/e2e test scripts that run during github workflows which depend on pre/post scripts running, which kick off recursive scripts. the setup is complicated but elegant and took a long time to setup.

We’re in desperate need of an option to allow the legacy behavior.

What a mess …

@davidenke, wrong attitude. you realize this software is offered to you for free? this unhelpful feedback is not welcome.

@jmcdo29, here’s a demo of a setup that could work for you.

{
  "scripts": {
    "compile": "tsc index.ts",
    "serve": "node index.js",
    "start": "run-s compile serve"
  },
  "devDependencies": {
    "npm-run-all": "^4.1.5",
    "typescript": "^4.2.4"
  }
}

to see it in action, you can copy and paste the following command right into your terminal and run it! (assumes you are on a mac or *nix system)

export DIR=/tmp/pnpm-scripts-demo \
 && rm -rf $DIR && mkdir -p $DIR \
 && cd $DIR \
 && echo "console.log(1+2)" > index.ts \
 && echo '{"scripts":{"compile":"tsc index.ts","serve":"node index.js","start":"run-s compile serve"},"devDependencies":{"npm-run-all":"^4.1.5","typescript":"^4.2.4"}}' > package.json \
 && pnpm install \
 && pnpm start
$ pnpm start

@ start /private/tmp/pnpm-scripts-demo
> run-s compile serve


> @ compile /private/tmp/pnpm-scripts-demo
> tsc index.ts


> @ serve /private/tmp/pnpm-scripts-demo
> node index.js

3

Adding pre: and post: sounds fine.

Makes sense

but prepostinstall will not be executed. And postpostinstall as well.

oh guys… You are making such a big problem out of nothing. This hooks worked for years and nobody cared. Don’t want to use them - don’t use them. Strategy like “put everything into single line with &&`” works but makes readability harder.

Previously, I had this:

{
    "prerelease": "npm run lint && npm test && NODE_ENV=production npm run build",
    "release": "dx semantic-release"
}

and now I need to do that:

{
   "release": "npm run lint && npm test && NODE_ENV=production npm run build && dx semantic-release"
}

So, now there is no clear understanding of what is the main command to doing. This is especially hard when we have post hooks and add them at the end. Unfortunately

So, what that script is actually doing is not obvious anymore! I think this change is ridiculous, and enable-pre-post-scripts must be true by default.

I could rewrite this to make it cleaner:

{
   "prerelease": "npm run lint && npm test && NODE_ENV=production npm run build",
   "release": "npm run prerelese && dx semantic-release && npm run postrelease"
   "postrelease": "some commands"
}

But… What’s a point, what are we fixing by this?

I just ran into this and was glad to see that an enable-pre-post-scripts configuration setting has been added for ongoing compatibility with npm behavior, even if it’s no longer pnpm’s default behavior.

IMO, keeping pnpm be highly compatible with npm—more or less a drop-in replacement —is very attractive and facilitates adoption of pnpm. That was certainly why I felt comfortable moving to pnpm.

You might consider updating the pnpm vs npm page to mention this difference in default behavior.

@beenotung point is moot now that 6.1.0 has the new option.

Thanks again @zkochan

@cdaringe I’m sorry to share @jmcdo29’s idea about the “mess” which is for me still a somehow polite word. We are here part of this community and any of us can be surprised with such a change. Even so, “npm-run-all” is not exactly s solution: it’s not maintained anymore and have some pretty bad bugs which are going to be a pain for this “unneeded” but forced transition.

Is there are a way to enable it again? I think this is useful feature in some cases

I think it will make less surprises if pnpm switch the configuration from enable-pre-post-script to disable-pre-post-script.

Configuration settings are set via the .npmrc file

no, the standard lifecycle scripts like postinstall or prepublishOnly are not affected.

I would like to note that it would be a breaking change for anyone that had scripts named with those prefixes or other purposes. so that should probably be an opt-in

@zkochan: For me it is unclear how to set the enable-pre-post-scripts setting. Do you have an example?

It seems that these scripts won’t be re-enabled anytime soon, which is a shame, but understandable.

It depends on the community. For now, it seems like it is not a huge deal.

I can make a PR for this if you’d like. Something along the lines of

You can edit this page I guess https://pnpm.io/cli/run

Would you say that it’s pnpm’s advice to use something like npm-run-all

npm-run-all is good for things like running commands in parallel. Sure it can also run commands in sequence but is it better than just writing something like "test": "pnpm pretest && jest"?

With npm-run-all it would be an overkill:

"scripts": {
  "jest": "jest",
  "pretest": "<some command>.",
  "test": "run-s pretest jest"
}

So for something complex I would indeed recommend a dedicated tool but for simple scripts pnpm should be enough

was not personally offended

Good to hear. I don’t think there was an intent to offend here.

We for sure need to document it prominently

I can make a PR for this if you’d like. Something along the lines of

Pnpm does not run pre/post scripts for custom scripts. This leads to more explicit scripts and less vague execution flow.

Or we can just take what yarn said and drop it into the run page.

our philosophy of being strict and explicit

Definitely a fair reasoning. Thanks for taking some time to answer more on this thread.

Would you say that it’s pnpm’s advice to use something like npm-run-all? It seems that these scripts won’t be re-enabled anytime soon, which is a shame, but understandable.

I understand it could work, was just hoping to not have to add another dev dependency and work with yet another package. But if that’s what I have to do then so be it.