/pnpm-monorepo-seed

A monorepo repository example based on pnpm, including guide documentation

Primary LanguageJavaScript

Monorepo

Background -- Monorepo vs. polyrepo

https://github.com/joelparkerhenderson/monorepo-vs-polyrepo

Quick Start

Preinstall

$ npm i -g pnpm
$ pnpm -v # should >= 6.20.0
# To use native code in your project, please install Rust and Cargo.
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Please be patient as the installation of dependencies may take some time.

$ pnpm install

structure

  • apps - Apps that only use packages and aren't aware of other apps.
  • packages - Packages that may use other monorepo packages.

Scenario for using Monorepo

1.A common scenario for using a Monorepo is when multiple packages, such as a client and server, need to be released together, Monorepo makes the release process of these packages more convenient.

client - React App

# Start React App
$ pnpm --filter "react-app" start

server - Node.js App

$ pnpm --filter "node-app" start

2.If different packages need to share common code

To address the reuse of enumerations, configurations, and utility methods, we aim to make them available for use in all projects.

Here is how to use it:

Declare the module's dependency in the project, where workspace:* indicates the use of the current Monorepo's shared module package.

// apps/*/package.json
{
  "dependencies": {
    "@infras/shared": "workspace:*"
  }
}

To facilitate convenient debugging of Monorepo package modules, we'll use the pnpm workspace protocol (which is also supported by yarn). In the project, use the following:

Import using the esm format Require using the cjs format Utilize commonly used project configuration tsconfig.json

// apps/*/index.{ts,tsx,jsx}
import { AppType } from '@infras/shared/types';
import { sum } from '@infras/shared/utils';

console.log(AppType.Web); // 1
console.log(sum(1, 1));   // 2
// apps/*/index.js
const { AppType } = require('@infras/shared/types');
const { sum } = require('@infras/shared/utils');
// apps/*/tsconfig.json
{
 "extends": "@infras/shared/configs/tsconfig.base.json"
}

packages/shared

$ pnpm --filter "@infras/shared" dev

packages/ui

$ pnpm --filter "@infras/ui" dev

3.Module packages written in native languages (Rust / C++ / Golang, etc.). We can use npm package modules written in Rust / Golang within a Monorepo to handle CPU-intensive tasks.

packages/native

$ pnpm --filter "@infras/native" build

Usage:

Declare the dependency on the native language module in the project.

// apps/node-app/package.json
{
  "dependencies": {
    "@infras/rs": "workspace:*"
  }
}

Calling it in Node.js:

// apps/node-app/index.js
const { sum } = require('@infras/rs');
console.log('Rust `sum(1, 1)`:', sum(1, 1)); // 2

// apps/node-app/index.mjs
import { sum } from '@infras/rs';

Building it as cjs:

Here, we use napi-rs to initialize an npm package built with Rust. napi-rs doesn't build an esm format package but instead chooses cjs format for compatibility with esm (related to node#40541).

# packages/rs
- src
    - lib.rs
- npm
- index.js
- index.d.ts
- package.json
- Cargo.toml

package.json:

We directly use the package.json generated by napi-rs without any modifications.

{
  "name": "@infras/rs",
  "version": "0.0.0",
  "type": "commonjs",
  "main": "index.js",
  "types": "index.d.ts",
  "devDependencies": {
    "@napi-rs/cli": "^2.0.0"
  },
  "scripts": {
    "prepare": "npm run build",
    "artifacts": "napi artifacts",
    "build": "napi build --platform --release",
    "build:debug": "napi build --platform",
    "version": "napi version"
  }
}

Running:

In the Node project, run pnpm start --filter "node-app". This way, you can observe that the Rust-compiled function is much faster than the nodejs.

js implementation (8.44ms → 0.069ms).