Managing Large Frontend Projects with Lerna

I. Introduction

Recently, I planned to develop a modular frontend library at the business level that supports arbitrary plug-and-play combination. It's a large project requiring multi-person maintenance.
Previous projects were business-level single-package projects, simple and easy to manage. However, when the codebase of a large project expands dramatically, management becomes troublesome. To facilitate code sharing, the codebase needs to be split into independent packages.
I researched the lerna library, which fits our scenario; babel uses this tool for management.

II. Lerna Basics

Initialization:

$ npm i -g lerna
$ mkdir lerna-repo && cd $_ $ lerna init 

The generated file structure is as follows:

lerna-repo/ packages/ package.json
  lerna.json 

By default, packages/ holds the multi-package locations.

Management Modes

When managing projects with lerna, you can choose between two modes.
The default is Fixed mode; initializing with `lerna init` defaults to Fixed mode, and you can use `lerna init --independent` for Independent mode.
In Fixed mode, all packages under `packages/` share a single version number, automatically binding all packages to the same version (the version field in lerna.json), so if any package is updated, the shared version number changes.
Independent mode allows each package to have its own version number; when using `lerna publish`, you can specify operations for each package individually and update only certain packages. In this mode, set the version field in lerna.json to `independent`.

Common Commands

  1. Start
    lerna bootstrap
    Remember to use this command instead of `npm install`. It uses npm by default; if you need yarn or cnpm, refer to 4.
    This command performs the following:
  • Run `npm install` in each package.
  • Based on the `dependencies` and `devDependencies` in each package's package.json, use symlinks to establish references under the `node_modules` of each package.
  • Run `npm run prepublish` in each package.
  • Run `npm run prepare` in each package.
  1. Add Dependencies
lerna add <pkg> [globs..] /*
Add a dependency to matched packages

位置: pkg Package name to add as a dependency [字符串] [必需] globs Optional package directory globs to match [数组] [默认值: []] */

For example, the directory structure is:

lerna-repo/ packages/ super-page-component/ package.json
      index.ts
      node_modules/ ... other-xxx-module/ package.json
  lerna.json 
</div>
<p>
    Add the dependency `vue-class-component` to `super-page-component`. The scope should be the package name in package.json, not the folder name. To install across all packages, omit the scope.
</p>
<div class="_2Uzcx_">
    <button class="VJbwyy" type="button" aria-label="复制代码"><i aria-label="icon: copy" class="anticon anticon-copy"><svg viewbox="64 64 896 896" focusable="false" class="" data-icon="copy" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"></path></svg></i></button> 
lerna add vue-class-component --scope=@mfelibs/superpage-component 
</div>
<ol start="3">
    <li>
        Publish<br />

lerna publish is used to publish updates, publishing to npm by default; refer to 4 for modifications. Running this command performs the following steps:

  • Run lerna updated to determine which packages need to be published
  • If necessary, update the version in lerna.json
  • Update the version field in package.json of all updated packages
  • Update dependencies in all updated packages
  • Create a git commit or tag for the new version
  • Publish packages to npm

Additionally, this command has many parameters, such as --skip-git to skip creating git commit/tag, and --skip-npm to skip publishing to npm.

  1. Specify Registry
    If your project is not managed with npm, like ours, you can specify the registry for each command.
    For example, use yarn: lerna bootstrap --npm-client=yarn
    For publishing with cnpm: lerna publish --npm-client=cnpm (note: invalid in version 3.x)

III. Encountered Issues

1. Symlink Issue

As mentioned earlier, step 2 of lerna bootstrap uses symlinks to establish references under each package's node_modules.

If our package contains webpack, the loaders may have issues.

Suppose there is a package pkg1 under packages, which depends on another package pkg2.
After running lerna bootstrap, a symlink to pkg2 appears under pkg1/node_modules.

The problem we encountered was that in pkg2 there is a TypeScript file being exported. When pkg1 imports it, it never hits the correct loader.

If using webpack-based tools to compile and run pkg1, because webpack loaders determine paths based on the real path by default, the path for pkg2 becomes [project root]/package/pkg2, not [project root]/package/pkg1/node_modules/pkg2.

As a result, if the source code in pkg2 needs to pass through pkg1's loaders (e.g., TypeScript in pkg2 via pkg1's ts-loader), special configuration is required. This differs significantly from real scenarios without symlinks.
Moreover, many configurations (like postcssrc, babelrc, eslintrc, etc.) are resolved relative to the file being processed.

For example, to compile [project root]/package/pkg2/src/Report.ts with babel, it searches for babelrc configuration in the following directory order:

[project root]/package/pkg2/src/ [project root]/package/pkg2/ [project root]/package/ [project root]/ ... 
</div>
<p>
    At this point, one would likely want the configuration to be searched in the `[project root]/package/pkg1/` directory.
</p>
<p>
    Therefore, it is desirable for webpack loaders to resolve paths based on the symlink location (include/exclude configuration) rather than the real file path.
</p>
<p>
    Therefore, you need to configure webpack's <code>resolve.symlinks</code> to solve this issue. See the <a href="https://webpack.js.org/configuration/resolve/#resolve-symlinks" target="_blank" rel="nofollow">official documentation</a>.
</p>
<h4>
    2. Specifying cnpm Registry Invalid
</h4>
<p>
    According to the <a href="https://github.com/lerna/lerna/issues/942" target="_blank" rel="nofollow">GitHub issue</a>, the publish command does not accept registry parameters; only the npm registry can be used.<br />

However, the old version 2.x supported it, but my tests up to version 3.3.2 show that lerna 3.x does not work with the cnpm registry for publishing.

评论

暂无评论。

登录后可发表评论。