Charlie
Charlie

Reputation: 1756

Yarn + Lerna + Angular Libs = broken publishing?

reHi!

Here's the deal, we have a monorepo. We're using Lerna & Yarn with a bunch of Angular Libraries.

In every package.json for the packages/libraries, we have something like:

"prepublishOnly": "yarn build <library name goes here>"

The way Yarn works for workspaces is yarn install, does what it does. Because we use workspaces, it creates symlinks to the packages. For example, if we have a package called @foo/bar, in the top-level node_modules, we would have node_modules/@foo/bar be a symlink to libs/foo-bar.

Yarn Workspaces is all fine and dandy, except the stuff in node_modules/@foo/bar isn't ready to be published. First, we need to build the package using Angular CLI's compiler.

We accomplish that with the already mentioned prepublishOnly script in package.json.

What works is when all the packages need are to be built. The flow goes:

  1. yarn install - Does the symlink/workspace magic.
  2. lerna publish --contents dist - Does the monorepo magic. Lerna will see that all the packages have had modifications, and run the prepublishOnly across all the packages. This way, what's in node_modules/@foo will be "legit" NPM packages (the output of Angular CLI building the libraries)

The problem is when a single library has a modification.

  1. yarn install - Does the symlink/workspace magic. All the things in node_modules/@foo will be symlinks to libs/<package-name> which, at this point, are source files. Not NPM packages
  2. lerna publish --contents dist - Starts... and goes "Oh, only Package A has changed. So let me run against it." Lerna will fail due to the other packages inside node_modules NOT being legit NPM packages.

I need to figure out how to either:

  1. Always build all the packages when doing a publish OR
  2. Somehow use packages from the NPM registry during the build process

I feel like I'm missing something simple somewhere.

If there are examples I can give to help explain, please ask.

Upvotes: 1

Views: 1044

Answers (1)

Amit
Amit

Reputation: 843

Always build all the packages when doing a publish

in your root package.json (considering you've lerna as a dev dependency)

{
 "scripts": {
     "publish-ci": "lerna run build && lerna publish --content dist"
  }
}

in your library1 packages

 "scripts": {
     "build": "yarn build library1"
  }

now you can run yarn publish-ci on your root folder and everything will be built and published.

you could also use lerna publish --from-package flag to publish only changed package.

Somehow use packages from the NPM registry during the build process

Here, you need lerna to only changed packages, not for publishing, a hacky way to get this is

in your root package.json

{
 "scripts": {
     "publish-ci": "node custom.publish.js"
  }
}

in custom.publish.js


var { execSync } = require("child_process");
let packagesChangedString = execSync("yarn lerna changed --toposort --json --loglevel silent").toString();
let packageChanged = JSON.parse(packagesChangedString.substring(packagesChangedString.indexOf("["), packagesChangedString.lastIndexOf("]") + 1));
console.log(packageChanged);

packageChanged.forEach(changed => {
    // exec npm publish manually without using lerna for publishing. 
    execSync("cd " + changed.location + " && npm publish" );

});

Upvotes: 3

Related Questions