This is the third day of my participation in the August More text Challenge. For details, see:August is more challenging
1. Monorepo management
For students who maintain multiple packages (with similar functions), they will encounter a multiple choice question: should these packages be maintained in one warehouse or maintained separately in multiple warehouses? Multirepo is more traditional, where each package is managed with a separate repository. Monorepo is a way of managing project code by managing multiple modules/packages in a project repository (REPo), rather than the common practice of having a repo per module.
There are many large open source projects that use this approach, such as Babel, React, Meteor, Ember, Angular,Jest, Umijs, Vue, creation-React-app, React-Router, etc. Almost all the warehouses we are familiar with without exception adopt the monorepo method. It can be seen that the content of the first level directory of these projects is mainly scaffolding, and the main content is managed in packages directory, which is divided into multiple packages.
The directory structure is as follows:
├ ─ ─ packages | ├ ─ ─ two packages pkg1 | | ├ ─ ─ package. The json | ├ ─ ─ pkg2 | | ├ ─ ─ package. The json ├ ─ ─ package. The jsonCopy the code
The main benefits of Monorepo are unified workflow and Code Sharing. For example, if I want to look at the code of a Pacakge and understand a piece of logic, I don’t need to find its repo, I just go to the current repo. When a requirement changes multiple Pacakge, you do not need to go to each repo to change, test, release, or NPM link. Instead, you can change the pacakge in the current repo, test, and release the pacakge in the same way. You can manage (build, test, release) multiple packages by simply building a set of scaffolding.
A picture is worth a thousand words:
Of course, which management style is better is a matter of opinion. While the former allows for diversification (projects can have their own build tools, dependency management strategies, and unit testing methods), the latter wants to centralize management and reduce the communication costs associated with differences between projects.
While dismantling a molecular repository and dismantling a molecular NPM package is a natural way to isolate a project, there is no more efficient way to debug when repository contents are associated than to keep the source code together.
Combining the practical scenarios and business needs of the shop-Service portal, the natural MonoRepo! An ideal development environment can be abstracted like this:
“You only care about the business code, you can reuse directly across the business regardless of how you reuse it, and everything is in the source code when you debug it.”
In a front-end development environment, multiple Git repos and multiple NPMS are ideal for this resistance, resulting in reuse concerns about version numbers and debugging requiring NPM links. These are MonoRepo’s greatest strengths.
The use of relevant tools mentioned above is the protagonist of today’s Lerna! Lerna is the most well-known Monorepo management tool in the industry with complete functions.
2.lerna
Lerna is a tool for managing multiple NPM modules. It is a project that Babel uses to maintain its own Monorepo and open source. Optimize the workflow for maintaining multiple packages to solve the problem that multiple packages depend on each other and that publishing requires manual maintenance of multiple packages.
2.1 installation
A global installation is recommended because the LERna command is often used
npm i -g lerna
Copy the code
2.2 Initializing the project
lerna init
Copy the code
Package. json & lerna.json:
/ / package. Json {" name ":" root ", "private" : true, / / private and will not be released, is to manage the whole project, and to publish to NPM decoupling "devDependencies" : {" lerna ": "^ 3.15.0"}} / / lerna. Json {" packages ": [" packages / *"], "version" : "0.0.0"}Copy the code
2.3 Creating an NPM package
Add two packages
lerna create @mo-demo/cli
lerna create @mo-demo/cli-shared-utils
Copy the code
2.4 Adding Module Dependencies
Add dependent modules to the respective package
Lerna add Semver --scope @mo-demo/cli-shared-utils // yes @mo-demo/cli-shared-utils adds semver module lerna add @mo-demo/cli-shared-utils --scope @mo-demo/cli // adds dependencies between internal modulesCopy the code
2.5 release
lerna publish
Copy the code
2.6 Dependency Package Management
Steps 1-5 have covered the entire life cycle of Lerna, but when we maintain the project, we need to install dependencies for each package after the new repository code is pulled down.
We also found in step 4 lerna add that the packages installed for a package are placed in the node_modules directory of the package directory. In this way, packages that multiple packages depend on will be installed multiple times by multiple packages, and node_modules will be maintained under each package, which is not clean. So we use –hoist to raise the dependencies under each package to the project root to reduce installation and management costs.
lerna bootstrap –hoist
To save the trouble of having to enter the –hoist parameter every time, you can set it in lerna.json:
{" packages ": [" packages / *"], "command" : {" bootstrap ": {" hoist" : true}}, "version" : "0.0.1 - alpha. 0"}Copy the code
After the configuration, if the dependencies have already been installed in each package, we just need to clean up the installed dependencies:
lerna clean
Copy the code
Then run lerna bootstrap to see that the package’s dependencies are installed in the root directory of node_modules.
3. Lerna + monorepo practice
Lerna is not responsible for building, testing and other tasks. It proposes a directory mode of centralized management of package and provides a set of automated management program, so that developers do not need to go deep into specific components to maintain content, and they can control the whole project in the root directory. Based on NPM scripts, users can complete component construction in a good way. Code formatting and other operations. Let’s take a look at best practices for building Monorepo projects based on Lerna in combination with other tools.
The most common Monorepo solution is the Workspaces feature of Lerna and Yarn, based on the Monorepo workflow of Lerna and Yarn workspace. Since FUNCTIONS of YARN and LERNA overlap, we use yarn to deal with dependency problems and LERNA to deal with release problems. Do with YARN what can be done with YARN
3.1 yarn workspace
3.1.1 Setting up the Environment Common projects: After the clone project is installed, use YARN install to set up the project. Sometimes, postinstall hooks are required for automatic compilation or other Settings.
Monorepo: There are dependencies between libraries. For example, A depends on B. Therefore, we usually need to link B to node_module of A
Solution: In the workspace, yarn install automatically helps troubleshoot installation and link problems
Yarn install # is equivalent to lerna bootstrap --npm-client yarn --use-workspacesCopy the code
3.1.2 Cleaning the Environment In the case of dependencies or engineering chaos, clean up dependencies
Normal projects: simply remove node_modules and the compiled artifacts.
Monorepo: Not only do you need to remove root’s node_modules build artifacts, but you also need to remove each package’s node_modules and build artifacts
Solution: Use lerna clean to remove all node_modules, and use Yarn workspaces run clean to perform all package cleanup
# lerna clean clean up all the node_modules yarn machine-specific run clean # perform all installation package of the clean operating 3.1.3 | delete rely on common project: Yarn Add and YARN Remove are used to solve the installation and removal of dependency libraries
Monorepo: Generally, there are three scenarios
To install dependencies on a package:
yarn workspace packageB add packageA
Copy the code
Install packageA as a dependency of packageB
Install dependencies for all packages: Use Yarn workspaces add lodash to install dependencies for all packages
Install dependencies on root: Common development tools such as typescript are installed on root. We use yarn add-w-d typescript to install dependencies on root
The corresponding three scenarios have the following deletion dependencies
yarn workspace packageB remove packageA
yarn workspaces remove lodash
yarn remove -W -D typescript
Copy the code
3.1.4 Project construction
Common project: Create a build NPM script and use YARN build to complete the project construction
Monorepo: What distinguishes a normal project is that there are dependencies between packages, such as packageB that can only be built after packageA is built, otherwise it will be wrong, which actually requires us to build in a topological order.
We can build our own topological collation rules. Unfortunately, the Yarn workspace does not support topological collation commands, even though the RFC has been accepted. Fortunately, LERNA supports topological collation, and the –sort parameter controls the execution of commands in topological collation
lerna run --stream --sort build
Copy the code
3.1.5 After the completion of the project test, version release is involved, which generally involves the following steps
Conditional validation: such as whether validation tests are passed, whether uncommitted code exists, and whether release operations are performed on the main branch
Version_bump: The version number needs to be updated when the version is released. In this case, how to update the version number is a problem.
Generate Changelog: In order to easily view the functions solved by each version of each package, we need to generate a changelog for each package to facilitate users to view the functional changes of each version.
Generate Git Tags: It is usually necessary to create a Git tag for each version to facilitate subsequent rollback and troubleshooting
Git releases: Each release requires a separate COMMIT record to mark the milestone
Release the NPM package: After you release Git, you need to release the updated version to NPM for external users
We found it cumbersome and error-prone to perform these operations manually, but fortunately LERNA was able to help us solve these problems
Yarn does not officially support the release process. It only needs to provide the package management tool. Therefore, lerNA is required to support this part
Lerna provides the Publish and Version functions to support version upgrade and release. The publish function can either include version work or simply publish.
3.3 Publishing Automatically generated Logs
With the previous specification submissions in place, automatic log generation comes naturally. Take a closer look at what lerna Publish does:
3.3.1
Find the packages that have changed since the last release
Prompt the developer to determine the version number to release
Update the version field of package.json in all updated packages
Update the dependency version numbers in packages that depend on the updated package
Update the version field in lerna.json
Commit the above changes with a tag
Push to a Git repository
3.3.2 Use NPM Publish to push new releases to NPM
CHANGELOG clearly corresponds to version one by one, so it is necessary to look at the description of LERna version command, and you will see a configuration parameter called Conventional Commits. Yes, as long as we submit according to the specification, the current version of CHANGELOG will be automatically generated during lerNA version process. For convenience, instead of having to input parameters every time, you can configure them in lerna.json, as follows:
{ "packages": [ "packages/*" ], "command": { "bootstrap": { "hoist": true }, "version": { "conventionalCommits": True}}, "ignoreChanges" : / / *. * * md ", "version" : "0.0.1 - alpha. 1"}Copy the code
The LERna version will detect changes since the last release, but there are some file commits that we do not want to trigger version changes. For example, a change to the.md file does not actually cause a change in package logic and should not trigger version changes. This can be excluded using the ignoreChanges configuration. As above.
The actual LERNA version is rarely used directly because it is included in LerNA Publish.
3.4 Perfect test cases
Monorepo project: There are two ways to test
Unified JEST test configuration is used to facilitate global jEST running. The advantage is that it is convenient to calculate the test coverage of all codes. The disadvantage is that if the package is relatively heterogeneous (such as small programs, front-end, node server, etc.), unified test configuration is not easy to write
Each package supports the test command independently. If you run the YARN workspace run test command, the disadvantage is that the test coverage of all codes cannot be collected in a unified manner
If you use JEST to write test cases that support typescript, you need to initialize jest.config.js:
module.exports = {
preset: 'ts-jest',
moduleFileExtensions: ['ts'],
testEnvironment: 'node'
}
Copy the code
4 Practice Summary
At this point, you have basically built the best practices for monorepo projects based on Lerna and Yarn Workspace.
Perfect workflow
Typescript support
Uniform style coding
Complete unit testing
One-click publishing mechanism
Perfect update log