With the increasing of team building and related businesses, more and more NPM packages need to be maintained by multiple people. As each project is related, it has to switch between multiple editors and debug through NPM link, which restricts the efficiency of development. Is there a way to solve the current pain point? The answer is Monorepo!
Bytedance’s internal encyclopedia entry defines Monorepo as follows:
Monorepo is a software development strategy that stores code for multiple projects in a repository.
At present, Lerna, as a multi-package manager of JavaScript projects, has been relatively mature and verified by modern enterprises. Therefore, a Lerna-based Monorepo management environment will be built step by step in the next step, hoping to help everyone to land in the business of each department and achieve cost reduction and efficiency improvement.
In my experience, Monorepo will significantly increase developer pleasure, so get started!
The main structure of this article is as follows, friends can eat as needed:
1. Why Lerna
If Monorepo can be defined as a policy, it must be a solution that can solve problems. The multi-package management mode of Monorepo based on Lerna implementation can solve the following problems (advantages) :
- flat: Under the same warehouse (project), the unified management and maintenance of multiple
package
- concentrated: in the root directory
node_modules/
Folder to maintain allpackage
Tripartite dependence of - To simplify the: Execute commands uniformly according to file changes, send packages as required, automatically upgrade version, write back warehouse, type
tag
- Efficiency: Interdependent projects can be directly correlated, avoiding developers switching between multiple warehouses
Of course, after a long time of use of Lerna, some problems are also exposed in the production environment, such as:
- Invalid build: Every time before the package will be on all
package
To build - Invalid rely on: All packages are installed each time you send them
package
The dependency of - Ghost rely on:Phantom dependenciesIn dependent ascension (
hoist
)
The problems listed here do not mean that Lerna should be abandoned, but that we should be clear about the advantages and disadvantages of the technical scheme and make some choices based on the actual situation of the project. The disadvantages mentioned above are not elegant in construction, but do not affect Lerna as a feasible Monorepo scheme.
Initialize a Monorepo project
We’ll build a clean lerna-based Monorepo project from zero to one, complete with ESlint validation for the team collaboration specification, Prettier automatic formatting, and git commit Message specification.
2.1 Initializing the project structure
First, install Lerna globally:
yarn global add lerna
// or
npm install lerna -g
Copy the code
Then create a new project directory and initialize a basic structure using Lerna
mkdir dyboy-lerna-project
cd dyboy-lerna-project/
lerna init --independent
Copy the code
After doing this, we have the following file directory structure:
. ├ ─ ─ lerna. Json// Lerna configuration file├ ─ ─ package. Json// A description file for the current project└ ─ ─ packages /// The folder where all packages are stored
Copy the code
When Lerna initializes the project, it appends a –independent parameter, which means to use independent mode.
In Lerna, there are two modes:
- Fixed modeAll:
package
The version number of each update is the same, and the package is issued in full - Stand-alone mode: each
package
The version number is independent and does not affect each other. Each update will be delivered as required
Generally, we will choose the independent mode to avoid frequent package delivery under multiple packages, especially under some projects with frequent business changes, the package delivery pressure is terrible 😱.
2.2 Lerna + Yarn Workspaces
Lerna uses NPM as the default package manager, but using YARN as the default package manager for Lerna is more recommended.
Workspaces was already supported in Yarn 1.0, and its benefits and relationship to Lerna can be found in this article at the time: WorkSpaces
The combination of Yarn Workspaces makes Lerna scheme complement its shortcomings like adding wings to a tiger.
First, modify the lerna.json configuration to the following:
{
"version": "independent"."npmClient": "yarn"."useWorkspaces": true
}
Copy the code
Then specify the workspaces field in the package.json file:
+ "workspaces": ["packages/*"],
Copy the code
After that, regardless of which folder we run Yarn, all dependencies in packages/ will be analyzed and installed to node_modules/ in the root directory.
2.3 ESlint + Prettier + Commit Rules
The above rules are uniform in any project. The configuration process is described in the previous article and will not be described here.
For details on how to initialize the configuration rules, see Steps 5 to 7 in “Build a TS+Rollup Initial Development Environment”.
After the above configuration, our project is roughly initialized!
2.4 NPM team account
Because sending packets requires an account, Monorepo managed several or dozens of packages at the same time, and all needed to maintain sending packets.
If you use your personal account to send a package to the Registry established by the company, the warehouse will become a “ghost warehouse” in case the student quits his job.
Of course, we can ask the Registry maintainer inside the company to change the corresponding package directly, but it is always a troublesome thing.
To do this, you can apply for a public account for the team, create a permission token through the NPM Token create, and put it in the.npmrc file in the project root directory.
After which no matter which developer maintenance, will default to use the team account to send package updates.
The final initialized project file structure is as follows:
Version release
As mentioned earlier, Lerna can centrally manage all packages, so we can specify shortcut instructions directly in the package.json file in the root directory to deliver packages on demand
Note: Lerna defaults to ignoring private packages with “private”: true set in package.json.
3.1 Project package compilation
Before the release of a new package version, it is generally needed to package and compile the product, in Monorepo under the release of multiple packages, certainly need to be packaged first.
(1). Learn Run
With the run command provided by Lerna, it is possible to have all projects with directives defined in package.json -> scripts execute this command before the package is sent
For example, run lerna exec build
Json -> scripts if build is defined, execute if it is, otherwise skip (run NPM run build on all packages that contain build).
One problem with this approach is that all pcKages are built before each package is shipped, increasing the time it takes to pack and publish.
Is there a more elegant way?
(2).npm Scripts lifecycle
The custom scripts field in the package.json file contains the default two life cycles pre and POST
By executing NPM run build, NPM run prebuid is automatically executed first, then NPM run build, and then NPM run postbuild.
In addition to the custom commands in package.json -> scripts, there are some scripts that come with NPM, such as NPM publish.
The life cycle of the NPM publish command includes:
- prepublishOnly
- prepare
- prepublish
- publish
- postpublish
Prepare will not be executed on NPM publish –dry-run.
Note: NPM 6.x and 7.x versions have different life cycles. The above version is 6.x. Considering the difference between 6.x and 7.x, it is recommended to place the action before sending the package into the prepublishOnly command.
For more information, see: scripts – NPM 6.x official documentation & Scripts – NPM 7.x Official documentation
(3). According to the need to Build
Json -> scripts for projects that need to be built at package time. Json -> scripts:
"scripts": {
"build": "rollup -c"."prepublishOnly": "yarn build"
}
Copy the code
When NPM publish is performed, the yarn Build in prepublishOnly is executed, the project is compiled and packaged, and then the package is published.
In this way, on demand, the package can be very elegant, smooth done!
3.2 Project outsourcing
At the package delivery stage, we add something to the package.json file in the root directory:
"scripts": {
"release": "lerna publish"."release:beta": "lerna publish --canary --pre-dist-tag=beta --preid beta --yes"
},
Copy the code
Yarn release Is used to release the official version
Yarn relase: Beta is used to release a test version for testing during development
Json -> name specifies the name of the package.json -> name field in the root directory. The default value is root. @dyboy/root to let other developers know that this is a scoped Monorepo project, even though the name field does nothing.
conclusion
The mental cost of the Monorepo project constructed based on Lerna is not high, but it requires us to have a certain understanding and grasp of the process, life cycle, NPM Scripts and other knowledge. It requires the builder to be able to find common requirements and constraint norms in process and management, so as to provide a solution to reduce cost and improve efficiency for the team.
This paper describes a solution of Monorepo based on the construction process. In front-end engineering, the builder also needs to consider whether there is room for optimization and consider details. For example, write a README. Md document that is easy to understand, think about how to make it easier for newcomers, try to solve problems in the process and actively explore new Monorepo technology solutions such as Rush, PNPM…
TODO: Can Monorepo automatically send the official version of the package? Interested friends can follow the follow-up (wechat official account: DYBOY) share!