lerna: npm uninstall throws 404 for unpublished siblings

Take the following project dependency structure

.
├── packages
    ├── module-a
        ├── dependency-a           (3rd party and published on npm)
        └── module-b               (sym link dependency from a `lerna bootstrap`)
    └── module-b                   (private/not published on npm)

cd’ing into module-a and attempting to uninstall dependency-a:

npm uninstall dependency-a

Gives the following error:

npm ERR! code E404
npm ERR! 404 Not Found: module-b@v1.0.0

For some reason (guessing clean up), NPM re-evaluates the dependent packages on a uninstall which blow’s away any sym links resulting in this 404?

Potentially related to: https://github.com/npm/npm/issues/17025

Expected Behavior

I would expect dependency-a to uninstall and module-a packages.json/lock files to update.

Current Behavior

npm uninstall behaviour is exited and the module is not removed.

Possible Solution

Open an issue with NPM? Find or document a work around.

Your Environment

Executable Version
lerna --version 5.4.2
npm --version 5.4.2
node --version 8.7.0
OS Version
macOS Sierra 10.12.6

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 19
  • Comments: 15 (7 by maintainers)

Commits related to this issue

Most upvoted comments

@aweber1 Thanks, that’s good to know. I kinda dry-coded my bullet points 😅

I’m planning on implementing lerna remove and lerna upgrade, since lerna is now a monorepo itself and the pain is felt directly.

When working with lerna-managed packages, running npm commands directly in a managed directory (packages/* be default) is not guaranteed to work. It is not an npm bug, either, because lerna bootstrap is doing special things to the package.json when installing (to workaround the local packages, basically).

Currently, removing a dependency in a lerna-managed package requires manual editing of package.json and re-running lerna bootstrap from the root. (contrary to the best practice, which is indeed npm rm/uninstall)

In the future I hope to add a lerna remove subcommand that streamlines this process.

@jennasalau My apologies, I forgot a step. I have been using --hoist so long, I forgot the patterns when npm actually installs stuff in every package directory (which is pretty slow compared to lerna bootstrap --hoist, even with zippy npm these days).

Anyway: Run lerna clean --yes --scope module-a before re-running lerna bootstrap.

The normal behavior of npm recognizing changes to package.json and manipulating package-lock.json to match will be restored when lerna clean nukes the node_modules. Turns out we have an over-eager optimization in lerna bootstrap that is attempting to save time avoiding a “no-op” install, and we’re not respecting npm’s ability to also make that judgement (and remove the deleted dependency).

Once again, my apologies. Thank you for having the patience to document a workaround! We (I, actually) could really use help implementing the lerna upgrade <dependencyName> and lerna remove <dependency> subcommands. Even documentation would be appreciated.

Teaching people in a team to run multiple command to just remove one package is an extra overhead 😢. Is it in plan to implement this anytime soon? @evocateur

If we stopped mangling the package.json files so badly during bootstrap, we would have a better story. Also, supporting the relative file: protocol (which would require resolution during publish) would make it all “just work”, for the most part.

To instruct folks that manually editing package.json files is the “best practice” for modifying dependencies goes against my strongly-held belief that you should always use the various npm subcommands when doing those sorts of things. I realize it is certainly possible to manually edit and get (mostly) what you expect in a normal npm package scenario, but in my experience it is a lot more brittle and prone to breakage. Lerna definitely changes the context significantly.

Here are the bullet points, such as they are:

To remove • Edit the target package.json, removing the dependency • Run lerna bootstrap in the root

To add • If hoisting, npm i <dependency> in root • Edit the target package.json, adding the dependency (if hoisting, just copy the line from the modified root package.json) • Run lerna bootstrap in the root

To upgrade (semver major): • If hoisting, npm i <dependency>@latest in root • Edit the target package.json files, upgrading the dependency version (if hoisting, just copy the line from the modified root package.json) • Run lerna bootstrap in the root

To update (when package-locked): • Same as semver major upgrade, except when hoisting, call npm up <dependency> in root instead of install.

On Jan 24, 2018, at 06:37, Thomas Lefebvre notifications@github.com wrote:

@evocateur I’d be happy to help improving the documentation. I’ve been facing similar issues when removing or upgrading a dependency comes into the game. Although, I’m not sure what are the current best practices for removing and upgrading. If you could provide some bullet point of what needs to be done, I can come up with some documentation for them.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

Manually linking local packages has become a pain.

The stale bot seems to be disregarding my commands. Such is the inevitable end with robots, it seems.

Coming back to lerna + npm from yarn workspaces this is a rather big surprise. Uninstalling/installing in packages seems like such a core task of a mono repo manager.

Most annoying is to have to update package.json files manually, lookup version numbers, etc. npm install does this automatically for you but using lerna renders this unusable

Thanks for the bullet points! For the “remove”, however, I ended up having to do this:

(using example module name “my-module”)

  • Edit the my-module/package.json, removing the dependency from either devDependencies or dependencies
  • lerna clean --yes --scope my-module
  • lerna bootstrap in the root

using v2.9.0 and not using hoisting

Hmm you would also have to manually remove it from the package-lock.json file and the bloody reference gets entwined everywhere. Its actually a nightmare removing dependencies from a lerna managed package. I also think you should make it more clear in your docs that we are not meant to be using npm in managed packages. I had no idea.

Anyway, as a work around for anyone else experiencing this issue:

You need to temporarily change the sibling references in package.json to use the local path feature. So given the dependency example I gave above:

Change module-a package.json (The one you want to uninstall a dependency on)

From:

{
    "name": "module-a",
    "version": "1.0.0",
    "dependencies": {
        "dependency-a": "^1.0.0",
        "module-b": "^1.0.0",
    }
}

To:

{
    "name": "module-a",
    "version": "1.0.0",
    "dependencies": {
        "dependency-a": "^1.0.0",
        "module-b": "file:../module-b",
    }
}

Then run the local npm uninstall. After which you must change it back otherwise lerna will hang.

@kamleshchandnani Well, there is a “help wanted” tag on this issue. I don’t have many spare cycles these days, so we’re at the mercy of folks pitching in to help implement these commands.