stories

Monorepo is no longer a novelty on the front end, but it is definitely becoming more common. Well known projects like React, Babel, vue warehouses have been upgraded to Monorepo.

Monorepo will be available in mid-2021. At that time, the team’s main code repository underwent a technical upgrade to the Monorepo architecture. That was my first experience with something that changed my thinking a lot.

Using Espoir would be the biggest support for me if I accepted my idea.

npm i -g espoir-cli@latest

Making: github.com/AntoineYANG…

The wheel?

Since the introduction of Node.js, the front-end has awakened a strong interest in infrastructure. There is a particularly famous saying of this era

Anything that can be written in JS will eventually be written in JS

Building a wheel is a lot of fun.

It’s great fun to build wheels for yourself/your team beyond business needs.

This article is also a story about making wheels.

monorepo

Let’s talk about why we use Monorepo first.

The most obvious feature of Monorepo is the integration of all the code into one large code repository. One word is consistency. Different application projects on the team can now be transparent to each other in the same repository. The management of the repository is unified, and the actual release of each project, etc., is no longer a one-to-one relationship with the code repository.

Imagine A scenario where project A uses A component foo, which is maintained by project A. But after a while, project B needs to use the same components. Should B CV large method, or require A to separate foo for separate maintenance?

This problem is solved by Monorepo — project A, project B, and component Foo are all maintained in the same repository. In this way, foo can be maintained uniformly and A and B have A right to know about it.

Even if A and B’s requirements for Foo change in the future, we can still retain the common logic in Foo.

Dependency management

Now that the code repository is one, what about dependency management? Using traditional NPM management tools, based on a one-to-one correspondence with package.json, we would get a project structure like this:

Each package corresponds to a node_modules, which doesn’t seem to be a problem. But if we look at this situation:

The same dependency is installed twice, and this is common for common and underlying dependencies. So I loaded the dependency management module on Espoir and designed it to look like the following:

Since node_modules has a lot of dependencies that are repeatedly installed, it’s easy to install them in the same place. The way dependencies are referenced does not change from project to project.

Version compatibility

Dependent version compatibility is an important issue. Suppose X and Y both depend on a package ABC, but they are incompatible with the version requirements of ABC (for example, X-ABC @^4.2.6, y-abc @^5.1.0). It’s hard to imagine what would happen if you just took one of these. If you install multiple versions, how do you isolate introductions from each other? I’m using links here.

The principle is very simple, because when Node resolves dependencies, it searches node_modules level by level, starting in the current directory. For X and Y, simply place a node_modules folder and add a link named ABC to the target version’s folder.

If some dependencies on the same package do not completely conflict (such as ABC @^5.0.2 ABC @>=5), the actual installed packages are minimized and the packages that depend on them are shared.

Shadow depend on

When it comes to versioning, we have to mention the well-known shadow dependency problem. Those of you who know about NPM3 will know that nPM3 is designed to address the problem of the nPM2 era where a lot of packages are repeatedly installed (as we discussed in the previous section) by leveling all dependencies and placing them in node_modules.

Project A relies on package X@^1.0.0, while the installed [email protected] depends on package [email protected]. Since ABC is also installed in node_modules, even though project A never installed ABC, we could even write in our business code:

import abc from 'abc';

abc.doSomeThing();
Copy the code

Remember that! This is illegal access!!

This may seem like a convenient result, but it can lead to unexpected errors. Suppose one day, the author of X updates the package to the [email protected] version. X itself is compatible with ^1.0.0, but unfortunately the author of X updates the version setting of the dependent ABC to 3.0.0. When we updated X with ease, our business code broke down.

abc.doSomeThing();
    ^
Uncaught TypeError: abc.doSomeThing is not a function
Copy the code

This is shadow dependency. Use a dependency specified by an undefined other dependency whose version is not under its own control. There is a potential risk that this layer of dependency may be updated or even deleted due to third-party updates.

How do I deal with shadow dependency in Espoir? I used an extra directory that will look very familiar to those of you who have used PNPM.

As well as exchange of needed goods

Let’s go back to the first example. Now A, B, and Foo are all stored in the same repository. But monorepo is meaningless if they can’t be imported between them. We can imagine a Monorepo where, in addition to individual projects, there are some common Spaces that can be reused across all projects.

But how? What if A and B access foo across the root directory? That’s not reasonable. It should be unclear to A and B that Foo exists. Both the scope of A and B exist only within their paths, and accessing content outside the scope is dangerous.

Two things to note here are that A and B must describe A dependency on Foo, and that the actual reference to Foo must be on A path that is legal for A and B. If these two conditions are met, you can easily think of a solution called dependencies.

In short, add A dependency to Foo in package.json for A and B. But when installing dependencies, instead of going to a remote repository to find a package with the same name, reference the local package and link it to node_modules. As shown in figure:

ing

When projects (packages) and code repositories no longer correspond one to one, there is an unexpected need to maintain some information independently for each package in Monorepo. Lerna is an expert in this area.

After completing the basic features of Monorepo, I also experimented with some ancillary features in Espoir.

Book with wen

Because repositories are monolithic, we can share configurations that constrain or stylize teams, such as EsLint and Prettier. These are at the code level. We also want the team’s code contributions to be legible, which requires team members’ Commit messages to conform to the specification. This has spawned many tools, such as CommitLint.

Note: Used in VS CodeGitLensPlug-in that displays the submission history of each line of code.

Espoir provides an interactive command line to assist developers in completing a description of a submission and using this information to automatically generate Changelog before submission.

Quick instructions

Defining scripts in package.json helps us perform some common operations quickly. But as we work on different projects, we need to switch back and forth between working directories. Espoir provides global access to all commands in Monorepo.

Another common scenario is that we choose to write a JS script when what we want to do is difficult to write on the command line. In package.json scripts, you’ll occasionally see something like this:

{
  "build": "node ./scripts/build.js"."dev": "node ./scripts/dev.js"
}
Copy the code

To do this, in esPOir, two additional directories, /scripts/ /tasks/, are detected for each package. You can use them directly without writing them to package.json:

The template

For the front-end students, the “start up” of writing code may be more of a scaffolding command line.

Creating new packages in Monorepo is also a common feature. Espoir predefined some simple scaffolding that can be used to create specific types of packages using existing templates. In the future, Espoir may add more templates or use other NPM packages to initialize new projects.

conclusion

Since writing the front end, they have thought about the construction of the front end code warehouse, about the value of doing things, through their own ideas, to achieve them. That’s where espoir comes in. A tolerant community at the front has a different approach to any problem, and it is acceptable to learn one of them, to accept the thinking of others, or to try it out for yourself.

Espoir was, for a while, my personal thing. Today, I have the opportunity to share what I have seen and heard before, and what I have thought and felt during the process of stepping into this project step by step. I would be happy if some students learned something new through this article, or were willing to try Espoir, or wanted to implement it themselves like I did. I try not to write too much, but my focus is not too little in this work. If you can read all the way to the end of the article, THANK you!


By Kyusho Miyasho, GitHub homepage