Yarn Workspaces (Workspaces in this article) is the Monorepo dependency management mechanism provided by Yarn. Supported by default since Yarn 1.0, it is used to manage the dependencies of multiple projects in the root directory of the code repository. The goal of Yarn Workspaces is to make it easy to use Monorepo(Monorepository) to handle the primary usage scenarios of Yarn Link in a more declarative way. In short, they allow multiple projects to co-exist in the same code base, cross-reference each other, and ensure that any changes made to the source code of one project are immediately applied to other projects.

Monorepo

Whether maintaining NPM packages or developing large front-end systems, there are scenarios where multiple packages with similar functions are managed in different functional modules, or there are dependencies between these packages. If you split these packages into different repositories, the task of making changes across multiple packages can be tedious and complex. The problem of repetitive installation and cumbersome management has been around since the NPM package was born, and node_modules hell is the epitome of this problem.

To simplify the process, many large projects have adopted the Monorepo approach of managing all packages in a single repository

Babel, React, Vue, Jest, etc all use monorepo management style.

The advantage of Menorepo is that it can maintain multiple packages in a warehouse and build them uniformly. It is very convenient for cross-package debugging, dependency management and version release. The matching tools can also generate CHANGELOG uniformly. The trade-off is that even developing only one of the packages requires installing dependencies for the entire project. Take JEST as an example, its Monorepo code structure is:

| jest/ | ---- package.json | ---- packages/ | -------- babel-jest/ | ------------ package.json | -------- babel-plugin-jest-hoist/ | ------------ package.json | -------- babel-preset-jest/ | ------------ package.json | -- -- -- -- -- -- -- --... /Copy the code

Yarn Workspaces

Some of the concepts

  • workspace context: In the context of the workspace feature, aproject is the whole directory tree making up your workspaces (often the repository itself)

Workspace context: A subdirectory of the file directory in which the workspace resides is the workspace context, and the entire directory tree constitutes the workspace entity.

  • workspace: a.workspace is a local package made up from your own sources from that same project

A workspace is a native code package consisting of source code for the same project.

  • Worktree: A worktree is the name given to workspaces that list their own child workspaces.

Work trees are the names of workspaces that list their own child workspaces.

  • The workspace – root: A project contains one or more worktrees, which may themselves contain any number of workspaces. Any project contains at least one workspace: the root one.

A project contains one or more working trees, which themselves can contain any number of workspaces. Any project should have at least one workspace: workspace-root.

Why use Yarn Workspaces

In projects with Monorepo code organization, the scale and complexity of dependency management has increased considerably (understandably, as “volume” increases, any small problem becomes complicated). How do I reduce reliance on repeat installations? How to gracefully share code across directories? How to uniformly manage dependent versions to avoid version conflicts? So these problems can be solved with Yarn Workspaces! Yarn official usage of Yarn Workspaces (Why would you want to do this?) It was described like this:

  • Your dependencies can be linked together, which means that your workspaces can depend on one another while always using the most up-to-date code available. This is also a better mechanism than yarn link since it only affects your workspace tree rather than your whole system.

Workspace dependencies can be linked together, meaning that workspaces can depend on each other while always using the latest available code. This is also a better mechanism than YARN Link because it only affects your workspace tree, not the whole system.

  • All your project dependencies will be installed together, giving Yarn more latitude to better optimize them.

All project dependencies will be installed together, giving Yarn more freedom to optimize them better.

  • Yarn will use a single lockfile rather than a different one for each project, which means fewer conflicts and easier reviews.

Yarn will use a common lock file for each project rather than a different lock file for each project, which means fewer conflicts and easier version reviews.

Running the yarn command in any directory in the Workspace Workspace space installs all Workspace dependencies.

How to Enable Workspace

First, you need to ensure that yarn is installed in your project. How do you install YARN? With NPM, of course (this won’t be necessary one day when Node.js has both NPM and YARN built in).

npm install yarn --save
or 
npm install yarn --global // Global installation
Copy the code

Then add the following configuration to packag.json in the project root directory:

{
  "private": true."workspaces": ["app1"."app2"] // Named Workspace. The name must be the same as that of the package.json property in the subproject
}
// Full directory references can also be used if the Workspace is large
// Assume the project code is in the projects directory
{
  "private": true."workspaces": ["projects/*"]}Copy the code

Note that the private: true is required! Workspaces are not meant to be published, So we’ve added this safety measure to make sure that nothing can accidentally expose them. Note: Private: true configuration is required! Workspaces are not meant to be published, so this security measure needs to be added to ensure that they are not published to the NPM repository.

After that, create two subfolders named app1 and app2. In each of these files, configure the following package.json file: app1/package.json:

{
    "name": "app1"."version": "1.0.0"."dependencies": {
        / / * * *}}Copy the code

app2/package.json:

{
    "name": "app2"."version": "1.0.0"."dependencies": {
       / / * * *}}Copy the code

Finally, you can start developing app1 and app2 projects under workspace. When installing project dependencies, you can run YARN Install in the root directory of the project to facilitate subsequent development. In this way, the entire Workspace dependencies will be installed, and there is no need to reinstall them in either App1 or App2. After yarn I is executed, the directory structure for the preceding example workspace is as follows:

/package.json
/yarn.lock
/node_modules

/projects/app1/package.json
/projects/app1/yarn.lock
/projects/app1/node_modules

/projects/app2/package.json
/projects/app2/yarn.lock
/projects/app2/node_modules
Copy the code

Use the yarn workspaces info [–json] command to obtain the entire workspace directory structure:

/ / yarn machine-specific v1.22.10
{
  "app1": {
    "location": "projects/app1"."workspaceDependencies": []."mismatchedWorkspaceDependencies": []},"app2": {
    "location": "projects/app2"."workspaceDependencies": []."mismatchedWorkspaceDependencies": []}}Copy the code

Description:

  • Projects is the parent directory of each subproject, i.e. workspace-root, while app1 and app2 are called workspace.
  • yarn installThe command can be executed in either the workspace-root directory or any workspace directory. The effect is the same.
  • Node_modules in app1 and App2 are not mandatory. They only exist if app1 and App2 depend on the same dependency of different versions or have their own unique dependencies.
  • workspaceDependenciesApp1 and App2 are not dependent on other workspaces, so the value is an empty array.

Yarn Workspaces CLI is commonly used

Existing Yarn 1 x

yarn workspace

Command format:

yarn workspace <workspace_name> <command>
Copy the code

Function: Run command under the specified workspace, that is, the command scope is a workspace. Example:

Install React for app1
yarn workspace app1 add react --save

Execute the start script in app1
yarn workspace app1 run start
Copy the code

yarn workspaces info [--json]

Effect: This command displays the workspace dependency tree for the current project. Example:

yarn workspaces info
Copy the code

yarn workspaces run <command>

Effect: The selected Yarn command will be run in each workspace. That is, run the command command through all workspace.

Run the yarn start command in all workspaces
yarn workspaces run start

Run the yarn test command in all workspaces
yarn workspaces run test
Copy the code

The new Yarn 2 x

Note: The Yarn workspaces info directive is deleted in Yarn 2.x

Yarn releases the latest version 2.4.3 on September 6, 2021. Several SCIs related to workspace have been added in this release.

yarn workspaces focus

To use this command, install the yarn plugin import workshop-tools plug-in

Role: Installs a single workspace and its dependencies.

yarn workspaces foreach [command]

To use this command, install the yarn plugin import workshop-tools plug-in

Function: Runs command on all workspaces (somewhat similar to Yarn workspaces in Yarn 1.x). Example:

yarn workspaces foreach run start
Copy the code

yarn workspaces list

Note: You need to run this command under workspace-root.

Function: Lists all available workspaces. Example:

yarn workspaces list
Copy the code

Yarn machine-specific practice

A typical practice of Yarn Workspace is to link references between Workspace, which saves tedious NPM publishing steps. Local references are based on the Yarn Link soft chain to the local source code, and code changes take effect in the referenced Workspace in real time. Here is an example to demonstrate the power of Yarn Workspace, which has been uploaded to Github.

The main practices of demo project include:

  • Through components in the YARN Workspace link repository;
  • Use YARN as package Manager to manage dependencies in your project.

Code directory:

├── ├─ ├─# Common Components│ ├─ ├─ ├─ ├─ ├─ ├─ ├.txt ├─ ├.txt ├─ ├.txtReact Scaffolding project│ │ ├ ─ ─ the config │ │ ├ ─ ─ package. The json │ │ ├ ─ ─ public │ │ ├ ─ ─ scripts │ │ ├ ─ ─ the SRC │ │ └ ─ ─ yarn. The lock │ └ ─ ─ app2React Scaffolding project│ ├ ─ ─ package. Json │ ├ ─ ─ public │ ├ ─ ─ the SRC │ └ ─ ─ yarn. The lock └ ─ ─ yarn. The lockCopy the code

There are three workspace roots under workspace root: app1, app2, and Hello-world. App1 and app2 are the React scaffolding projects generated by create-React-app. Hello-world is a simple React component. Package. json for the three workspace is configured as:

// app1
{
  "name": "app1"."version": "1.0.0"."private": true."dependencies": {
     "react": "^ 17.0.2" 
     / /...}}// app2
{
  "name": "app2"."version": "1.0.0"."private": true."dependencies": { 
    "react": "^ 17.0.2" 
    // ...}}// hello-world
{
  "name": "hello-world"."version": "1.0.0"."description": "hello-world"."main": "index.js"."dependencies": {
    "react": "^ 17.0.2"}}Copy the code

The package.json configuration in the root directory enables YARN workspace:

{
  "private": true."workspaces": [
    "projects/*".// Scan the projects directory to include the project in the workspace context
    "components/*" // Scan the Components directory to include the project in the workspace context]."name": "version-demo"."version": "1.0.0"
}
Copy the code

Yarn Workspaces info –json

{
  "app1": {
    "location": "projects/app1"."workspaceDependencies": []."mismatchedWorkspaceDependencies": []},"app2": {
    "location": "projects/app2"."workspaceDependencies": []."mismatchedWorkspaceDependencies": []},"hello-world": {
    "location": "components/hello-world"."workspaceDependencies": []."mismatchedWorkspaceDependencies": []}}Copy the code

Use components in Components

Workspace cross-reference changes are easy under the Yarn Workspaces scope, and now use components/ Hello-wrold components in APP1 in just two steps.

  • Step 1: Useyarn | yarn workspacesCommand “Install” dependency
cdProjects /app1 & yarn add [email protected]# specify version to ensure an accurate hitOr yarn workspace app1 add [email protected]# install workspace in root
Copy the code

Yarn Workspaces info –json

{
  "app1": {
    "location": "projects/app1"."workspaceDependencies": [
       "hello-world"]."mismatchedWorkspaceDependencies": []},"app2": {
    "location": "projects/app2"."workspaceDependencies": []."mismatchedWorkspaceDependencies": []},"hello-world": {
    "location": "components/hello-world"."workspaceDependencies": []."mismatchedWorkspaceDependencies": []}}Copy the code

The workspaceDependencies dependencies of app1 add a hello-world dependency.

  • Step 2: Modify app1 ->paths.js, webpack.config.js configuration and add the Components directory to the build path
// paths.js
module.exports = {
  // ...
  componentsPath: resolveApp('.. /.. /components'),};// webpack.config.js
{
   test: /\.(js|mjs|jsx|ts|tsx)$/,
   include: [paths.appSrc, paths.componentsPath],
   loader: require.resolve("babel-loader"),... }Copy the code
  • Execute in app1yarn startThe effect is as follows:

Components soft chain process

  1. Check whether there is a match in Monorepoapp1Required versionhello-world;
  2. If yes, perform the link operation.app1Use localhello-world;
  3. If not, pull the version from the remote NPM repositoryhello-worldapp1Use;
  4. If the remote NPM repository cannot be foundhello-worldComponent, an error is reported:error An unexpected error occurred: "https://registry.npmjs.org/hello-world: Not found".

Yarn Workspaces limitations and deficiencies

  1. Yarn Workspace does not encapsulate a large number of high-level apis like Lerna. The whole yarn workspace depends on the whole YARN command system.
  2. Workspace does not support nesting (there can only be one workspace-root).
  3. Workspace uses upward traversal, so workspace does not recognize dependencies outside of workspace-root.
  4. Dependency reference version uncertainties, such as components/ Hello-world above, can occur when developers publish to the NPM repository and the NPM repository version number is inconsistent with the local version number.
  5. The dependency of yarn Workspace Link needs to be added to the build path of build tools such as Webpack, rollup, and vite, which increases the build cost and slows down the release.
  6. Yarn. lock is prone to conflicts.

Yarn Workspaces usage specifications are recommended

To solve the preceding problems in Yarn Workspaces, a set of project-level implementation specifications is recommended. To make use of its strengths and avoid its weaknesses.

  1. The Packages for the YARN Workspace link are set to private

Set private: true in package.json of components that require local dependencies to prevent them from being uploaded to the NPM remote repository by mistake. By forcing references only through the native source link, problem 4 can be avoided.

  1. All workspace dependency declarations converge to workspace-root

That is, no dependencies and devDependencies are declared in pakcage. Json of each workspace, and all dependencies are converged to package.json of workspace. For new projects, you can start the installation using yarn -w add [package] [–dev]. For historical projects, you can manually modify the package.json of each workspace and cut the dependencies to the package.json in the root directory. The advantage of this is to unify the dependency versions of each sub-workspace, avoid installing different versions of the same dependency, maintain the unity of the dependency versions in the project under the worktree, and share the dependency among the workspace. An increase in the initial installation time of a project due to global shared dependencies is acceptable because the initial installation takes a long time and the subsequent installation time is greatly reduced due to caching.

  1. Disable new workspace dependencies independently

All new dependencies need to be installed through yarn -w add [package] [–dev], which can be recognized as a supplement to the second specification.

  1. yarn.lockIt must be submitted and conflicts must be resolved

Yarn. lock is the basis of yarn-dependent version control. In the framework of global shared dependency, yarn.lock file maintenance becomes particularly important. Ensure submission to ensure that new dependencies can be included into YARN version control. Resolve conflicts correctly to ensure the uniqueness and uniformity of dependency versions.

  1. Add eslint and prettier to the Yarn Workspace and directory. The subworkspace inherit the root configuration

Eslint and Prettier configurations have become the norm for standard project code, so they must be uniform throughout the Workspace.

  1. Daily development should be in the workspace-root directory

This is a complement to specification 5, ensuring that the global code specification is in effect; It also allows developers to easily view yarn Workspace Link Packages

reference

Yarn 1 Workspace Yarn 2 Workspace Monorepo-Lerna-yarn-workspaces Application-level MonorePO optimization solution