25

I'm trying to set up my monorepo with Lerna. The plan is to refactor an existing project by pulling chunks of code out which should be their own packages. I've run lerna init, and my current setup looks like this:

project/
  packages/
    new-refactored-package/
      package.json
    prior-existing-project/
      package.json
        { "dependencies" : { "new-refactored-package" : "latest" } }
  package.json
    {
      "devDependencies": {
        "lerna": "^2.0.0-rc.5"
      }
    }
  lerna.json
    {
      "lerna": "2.0.0-rc.5",
      "packages": [
        "packages/*"
      ],
      "version": "0.0.0"
    }

My understanding was that lerna bootstrap at this point is supposed to locate package1 in the project and symlink it to prior-existing-project's /node_modules/new-refactored-package/. From lerna's readme:

Bootstrap the packages in the current Lerna repo. Installs all of their dependencies and links any cross-dependencies.

When run, this command will:

  • npm install all external dependencies of each package.
  • Symlink together all Lerna packages that are dependencies of each other.
  • npm prepublish all bootstrapped packages.

However, when I run it, lerna attempts instead to npm install new-refactored-package:

npm ERR! 404 Registry returned 404 for GET on https://registry.npmjs.org/new-refactored-package

Am I misunderstanding? Do I first have to publish the depended-upon packages to npm?

NiloCK
  • 571
  • 1
  • 11
  • 33
  • I'm having the same problem too! Documentation is misleading about unpublished packages and linking. I hope somebody can help us here. – İbrahim Duran Jun 13 '17 at 21:53

5 Answers5

17

Requirements

For lerna to symlink a local package when running lerna bootstrap the local package must have a name and version that matches. Whenever lerna cannot match a dependency to a local package, it'll try to install it from the registry.

So ensure that the dependency package has a version that can be matched by the semver version in the dependant.

Example

{

  name: "@my-name/dependency",
  version: "1.2.0"
}
{
  name: "@my-name/dependant",
  dependencies: {
    "@my-name/dependency": "<VERSION>"
  }
}

@my-name/dependency will be symlinked when VERSION is 1.2.0, ^1.0.0, 1.X.X, or *. However when using ranges that does not match the local package, like 1.0.0 or ^0.0.0, it will try to resolve it in the npm registry and will show the error like 404 Not Found - GET https://registry.npmjs.org/@my-name%2fdependency - Not found.

latest

In the actual scenario that is explained in the question, the actual issue is that the version is specified as latest and while it's easy to think that latest is a generic term for the latest version available, it's actually a npm-dist-tag which is applied by default to all new releases.

If you look at packages like react (click versions), you can see that other than the latest, they also deploy releases with tags next, canary and unstable.

Your unpublished package does not have any tags, as they are applied on publication, and so latest will not match, meaning lerna will try to resolve it remotely, failing with 404.

This is noted as one of the gotchas in the Notes of the documentation for the bootstrap command.

  • When a dependency version in a package is not satisfied by a package of the same name in the repo, it will be npm installed (or yarned) like normal.
  • Dist-tags, like latest, do not satisfy semver ranges.
  • Circular dependencies result in circular symlinks which may impact your editor/IDE.

Solution

If you want to match whatever version that is available, the recommended path would be to set the version to "*". This will match any version and will thus always use the local version, given that the local package has a specified version field.

{ 
  "dependencies": { 
    "new-refactored-package" : "*"
  }
}

alpha, rc or beta

Even * will not match versions that are marked as pre-release, so if you give your local package a version like 0.0.1-alpha.0, or 1.0.0-rc.3, it will also not be locally symlinked

private: true

While it does not affect lerna bootstrap, it's worth mentioning that packages that you do not want to be published should always have private: true;. This will ensure that lerna publish does not publish it.

oBusk
  • 852
  • 2
  • 10
  • 21
  • I don't get why `npm i my-local-package@1.0.0` gives me this error then, while adding it manually to `package.json` works. – Eric Burel Feb 26 '20 at 17:32
  • 1
    @EricBurel You can't install a local package with `npm`. Adding it to the package.json and then running `lerna bootstrap` can work, if the package is available locally. But npm does not support local packages and does not benefit from lernas functionalities. To add packages that are either remote (published packages) or local (in the lerna workspace), you can use `lerna add my-local-package@1.0.0 packages/app-to-install-in` https://github.com/lerna/lerna/tree/master/commands/add – oBusk Feb 27 '20 at 18:11
  • 1
    @oBusk Does this mean that once I add a local package as a dependency, I would not be able to run `npm install` or `npm uninstall ` anymore as hey will fail with `npm ERR! 404 Not Found - GET https://registry.npmjs.org/ - Not found` – Chandu Jun 11 '20 at 18:01
  • @oBusk Looks like what running `npm uninstall` in the repo will run into issues as per https://github.com/lerna/lerna/issues/1229 :( – Chandu Jun 11 '20 at 18:08
7

lerna bootstrap will symlink packages instead of install if they are available.

In your case, I think lerna cannot find the correct version or name of the package.

Here is what I did in my project ...

project
- packages/
    - a_pkg
        - package.json {
            "name": "@scope/a_pkg",
            "version": "0.0.1",
            "private": true
            /// opt out
        }
    - b_pkg
        - package.json {
            "name": "@scope/b_pkg",
            "version": "0.0.1",
            "private": true,
            "dependencies": {
              "@scope/a_pkg": "^0"
            },
            /// opt out
        }
- package.json
- lerna.json {
    "packages": [
        "packages/*"
    ],
    /// opt out
}
kp_ping
  • 495
  • 5
  • 14
  • 3
    Are the private and version keys critical? – Larry Maccherone Aug 30 '18 at 14:07
  • 1
    The version is required in the dependency (`a_pkg`) so that lerna can assure the correct version. The dependant (`b_pkg`) does not technically require a version for bootstrap to work in this scenario. The `private: true` has effect primarily on `lerna publish` as to not try to publish the package to NPM. It is not needed for bootstrap to work. – oBusk Mar 02 '19 at 18:58
1

lerna bootstrap will check the package version you specified in package.json or package-lock.json.

Since you have not published your work, I would give a try to use --force-local on the bootstrap command.

lerna bootstrap --force-local

kitimenpolku
  • 2,604
  • 4
  • 36
  • 51
1

Try to install the dependencies using "lerna add <DEPENDENCY_NAME>" command instead of adding the dependencies manually. This will automatically add the local version(if available)

Shivang Gupta
  • 3,139
  • 1
  • 25
  • 24
-2

The package name in package.json has to match the folder name in /packages folder.

(Essentially what @kp_ping said)

Jkarttunen
  • 6,764
  • 4
  • 27
  • 29
  • 1
    The folder does not need to match the package name. The dependency must have the correct name and must have a `version` that can be matched. – oBusk Mar 02 '19 at 20:43