Author: m1heng

From the first commit on December 7, 2018, to now 154 business packages, 11 public packages and 7 toolkits, our MonorePO has gone through a long track. This article is only a primer. To record, precipitate and memorialize the monorepo process.

primers

One day, A student has been frowning since early in the morning. After lunch break, when everyone was still sleepy-eyed, he suddenly jumped to his face and said, “Is there any black technology in your common-A package? Why did you change tsconfig?”

At this point, the maintainer of common-A silently ran up behind B and whispered, “You clicked on the package configuration of your business package. Did you include the common-A package in your include?”

Little B clicks open ****. Config. js in VSCode, which reads

{
   tsLoader: (opts, { addIncludes }) = > {
      addIncludes([/(packages|@monorepo_workspace)/]); }},Copy the code

Small B still indissoluble: “this have what problem?”

A touched B’s head and said: “Your business package is the source code of common-a, so the tsconfig of common-a package does not work.”

What went wrong?

One of the great advantages Monorepo offers developers is that abstract public packages can be referenced in the REPO without having to ship them out. This convenience greatly stimulated the enthusiasm of the team for the implementation and iteration of the Common Package.

However, when the logic in common is referenced in the business package, alias and Loader are generally adopted to add include, and the code in the common package is given to the packaging tool as the business source code and compiled together, which is regarded as the code in the business rather than a normal package.

In the case of monorepo full TS scene, This works, but with hidden problems.

Package.json is ignored

The entry defined in the Common package is actually invalid. The business package can refer to any section of logic in the Common package regardless of the entry, which makes the maintenance of the Common package difficult.

Tsconfig. json is ignored

In the case of TS, the tsconfig.json configuration of the common package is ignored and the configuration of the business package is used instead. The common package needs to adapt tsConfig for all business packages rather than maintain a self-consistent TSConfig.

Phantom Dependency

Dependencies referenced in the Common package are declared only in the Common package and are not declared twice when used by the business package. However, as source code packaging, there are problems with implicit dependencies and dependent version uncertainty.

In general, the common package is no longer a package, but a folder in which package.json and tsconfig.json are just self-deprecating.

Is there a solution?

Our goal is to make the Common TS package a package that can be used in a business package as if it were distributed in NPM. In real development scenarios we tend to focus on the following:

  1. Because there are more existing business packages, the new solution requires fewer changes to the original business package (but not code changes due to entry).
  2. You need to adapt both the Node project and the Web project.
  3. It is best to support changes to the common package when Dev, even if they take effect, without additional manual steps.

In fact, there are many kinds of solutions. Numerous unplanned solutions have appeared in the process of brainstorming with students, but most of them have problems of too many hacks or high development cost. Overall, only two solutions with high feasibility rely on Git hook automatic compilation or use ProjectReferences.

Automatic compilation -w/Git Hook

This solution was actually tried in the next group by adding all scripts compiled by the Common package in Git pull hooks.

The developer automatically triggers compilation every time git pull, compiling all common packages locally. This is pretty much a daily requirement for a business package-only developer

However, when common package and service package are developed at the same time, it is often necessary to open two terminals to run compilation at the same time, and it is difficult for the dev process of service package to perceive the changes of common package. This requires the developer to manually restart the dev process of the business package frequently, which is very inefficient.

ProjectReferences

  • TypeScript introduces a new feature, Project References, in 3.0.

  • Provides fine-grained TSC capabilities for more segmented TS projects. According to the official TS documentation, this feature is intended to meet the same project subdivided small module independent compilation improvement such as singleton test scenarios. From our point of view, this is a nice addition to the automatic compilation of the Common TS package under Monorepo.

  • Contains the ability to handle multiple tsconfig.json link dependencies. When projectReferences fields are present in tsconfigA, TSC compiles tsconfigA first and then tsconfigA. Link dependencies are also supported. Such as tsconfigA -> tsconfigB -> tsconfigC.

  • Project Regences is also supported by TSLoader

  • TSLoader also supported Project Regences capabilities beginning at 5.2.0❤️, with significant performance improvements in subsequent iterations. When webPack-based TS projects use TSLoader, TSLoader will recognize projectReferences in TsConfig and hand them over to TSInstance for compilation.

It can be found that using ProjectReferences, either a Node Server project requiring TSC compilation or a Web project requiring Webpack packaging can be well supported.

How do you do that?

The first step is to ensure that the Common package itself is configured correctly

  1. The tsConfig of the Common package and service package must meet the requirements of TS. The common package allows you to configure two sets of tsConfig for different uses, such as tsconfig.es. Json + tsconfig.lib.json.
  2. Configure the entry properties for a normal package in package.json of the common package.
// pacakge.json
{
  "name": "@monorepo_workspace/common-a"."version": "1.0.0"."description": "One Common package"."sideEffects": false."exports": {
    ".": {
      "import": "./es/index.js"."require": "./lib/index.js"}},"main": "./lib/index.js"."module": "./es/index.js"."typings": "./es/index.d.ts"
}
Copy the code
  1. Adjust some configurations in the business package
  • Remove the associated include from the package tool, open Tsloader, and open projectReferences.
// some js config
{
  tsLoader: (config) = > {
    config.projectReferences = true;
    config.compilerOptions = undefined; }}Copy the code

One more thing, the reason you set compilerOptions to undefined is because some frameworks have some compilerOptions configured by default, These compilerOptions in tsloader config will override the tsconfig of projectReferences package, causing some strange problems, so undefined is set to override the default configuration.

  • The package.json dependency ensures that the common package is declared, and that package management tools help locate the common package correctly as a package.
  • The tsconfig path for the common package to find is configured in projectReferences of tsconfig.json.
// tsconfig.json
{
  "references": [{"path": ".. /.. /common/common-a/tsconfig.es.json"
    },
    {
      "path": ".. /.. /common/common-b/tsconfig.json"}}]Copy the code

Path can write the specific tsconfig.json address or the package Path. The tsconfig.json file in the folder Path is automatically read

After configuration, Run Dev will see that the Common package in projectReferences is built by TS before running the business code, and that your packaging tool will finally treat the Common package as a REAL package during the actual packaging process.

Can we go further?

Project Treferences has been implemented in our Monorepo for some time, the overall evaluation is good, and the students in the group can quickly understand and modify their own projects to use Project Treferences.

But “lazy” is the nature of programmers, and adding two lines of references to tsConfig is an additional burden.

Theoretically, the ability to add a Webpack plug-in before the TS-Loader, or to provide autoReference in the TS-Loader, should allow local packages to be automatically treated as projectReference. This idea is still in the early stage of our discussion. If you are interested in it, welcome to discuss and build it privately.

To be continued

In addition to resolving common package reference issues, Monorepo has experienced node deployment, yarn.lock review hell, and too many resolutions.


Welcome to “Byte front end ByteFE”

Resume mailing address: [email protected]