のらねこの気まま暮らし

技術系だけど、Qiita向きではないポエムとかを書くめったに更新されないヤツ

turborepoとchangesetを使って、monorepoのライブラリ開発における構成を検討する

概要

monorepoなライブラリをnpmに公開するための構成を調査、検討する。

検証リポジトリ

以下にこの記事で検証したリポジトリを示す。

github.com

動作環境

macOS Ventura バージョン13.6.6 Node 21.4.0

前提となる構成

turborepo

turboを使ってmonorepoを構成する

turbo.build

changeset

turborepoによると@changesets/cli - npmを推奨している。

www.npmjs.com

verdaccio

localで動作するnpm registry。 実験でpublishしまくるので公式のregistryは流石に使いにくい。

verdaccio.org

Verdaccioの構築

docker imageを使うのがお手軽そうなので、取得して立ち上げる。

以下の点に注意。 * portの指定忘れると開けない * volumeの指定しないと再起動でデータが消える

$ docker pull verdaccio/verdaccio:nightly-master 
$ docker run -it -p4873:4873 --rm verdaccio/verdaccio:nightly-master
(node:8) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
 info -=- local storage path /verdaccio/storage/data/.verdaccio-db.json
 info -=- no private database found, recreating new one on /verdaccio/storage/data/.verdaccio-db.json
 info --- using htpasswd file: /verdaccio/storage/htpasswd
 info --- http address http://0.0.0.0:4873/
 info --- version: 7.0.0-next-7.13
 info --- server started

指定したportをブラウザで開く。

open http://0.0.0.0:4873/

ひとまず初期画面が表示される。

Verdaccioの初期画面

検証用のリポジトリを作成

こちらに作成しました。作成の意図としては - nodejs で動作するpackageをイメージ - turborepoで構成 - 複数のpackageを構成

github.com

changesetの導入

packageにインストール

$ npm -w packages/core i -D @changesets/cli 
$ npm -w packages/express i -D @changesets/cli 

coreとexpressのpackage.jsonchangesetスクリプトを追加

diff --git a/packages/core/package.json b/packages/core/package.json
index f8facde..c7876fd 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -7,12 +7,14 @@
   "scripts": {
     "build": "tsup src/*",
     "lint:eslint": "eslint --max-warnings 0 --ext .ts,.tsx .",
-    "lint:prettier": "prettier './src/**/*.{ts,tsx,js,json,css}' --check"
+    "lint:prettier": "prettier './src/**/*.{ts,tsx,js,json,css}' --check",
+    "changeset": "changeset"
diff --git a/packages/express/package.json b/packages/express/package.json
index f8facde..c7876fd 100644
--- a/packages/express/package.json
+++ b/packages/express/package.json
@@ -7,12 +7,14 @@
   "scripts": {
     "build": "tsup src/*",
     "lint:eslint": "eslint --max-warnings 0 --ext .ts,.tsx .",
-    "lint:prettier": "prettier './src/**/*.{ts,tsx,js,json,css}' --check"
+    "lint:prettier": "prettier './src/**/*.{ts,tsx,js,json,css}' --check",
+    "changeset": "changeset"

changesetの初期化

$ npm -w packages/core run changeset init
$ npm -w packages/express run changeset init

publishのaccessをpublicに変更

diff --git a/packages/core/.changeset/config.json b/packages/core/.changeset/config.json
index 91b6a95..fce1c26 100644
--- a/packages/core/.changeset/config.json
+++ b/packages/core/.changeset/config.json
@@ -4,7 +4,7 @@
   "commit": false,
   "fixed": [],
   "linked": [],
-  "access": "restricted",
+  "access": "public",
   "baseBranch": "main",
   "updateInternalDependencies": "patch",
   "ignore": []
diff --git a/packages/core/package.json b/packages/core/package.json
index 5f59f4d..1987880 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -14,5 +14,8 @@
     "@changesets/cli": "^2.27.1",
     "@example-monorepo/dev-config": "*",
     "tsup": "^8.0.2"
+  },
+  "publishConfig": {
+    "access": "public"
   }
 }
diff --git a/packages/express/.changeset/config.json b/packages/express/.changeset/config.json
index 91b6a95..fce1c26 100644
--- a/packages/express/.changeset/config.json
+++ b/packages/express/.changeset/config.json
@@ -4,7 +4,7 @@
   "commit": false,
   "fixed": [],
   "linked": [],
-  "access": "restricted",
+  "access": "public",
   "baseBranch": "main",
   "updateInternalDependencies": "patch",
   "ignore": []
diff --git a/packages/express/package.json b/packages/express/package.json
index c7876fd..586fd64 100644
--- a/packages/express/package.json
+++ b/packages/express/package.json
@@ -19,5 +19,8 @@
     "@types/express": "^4.17.21",
     "express": "^4.19.2",
     "tsup": "^8.0.2"
+  },
+  "publishConfig": {
+    "access": "public"
   }
 }

changesetの設定を行う

$ npm -w packages/core run changeset                                            [branch:main](untracked)[~/project/example-monorepo]

> @example-monorepo/core@0.0.0 changeset
> changeset

🦋  Which packages would you like to include? · @example-monorepo/core, @example-monorepo/express
🦋  Which packages should have a major bump? · No items were selected
🦋  Which packages should have a minor bump? · No items were selected
🦋  The following packages will be patch bumped:
🦋  @example-monorepo/core@0.0.0
🦋  @example-monorepo/express@0.0.0
🦋  Please enter a summary for this change (this will be in the changelogs).
🦋    (submit empty line to open external editor)
🦋  Summary · the first update
🦋  
🦋  === Summary of changesets ===
🦋  patch:  @example-monorepo/core, @example-monorepo/express
🦋  
🦋  Note: All dependents of these packages that will be incompatible with
🦋  the new version will be patch bumped when this changeset is applied.
🦋  
🦋  Is this your desired changeset? (Y/n) · true
🦋  Changeset added! - you can now commit it
🦋  
🦋  If you want to modify or expand on the changeset summary, you can find it here

bump up

version, publishへのエイリアスを作成する。

diff --git a/package.json b/package.json
index 106fd31..3f6c163 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,8 @@
     "build": "turbo run build",
     "lint:eslint": "turbo run lint:eslint",
     "lint:prettier": "turbo run lint:prettier",
+    "version": "turbo run version",
+    "publish": "turbo run publish",
     "format": "prettier --write \"**/*.{ts,tsx,md}\""
   },
   "devDependencies": {
diff --git a/packages/core/package.json b/packages/core/package.json
index 1987880..74c6f4a 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -8,7 +8,9 @@
     "build": "tsup src/*",
     "lint:eslint": "eslint --max-warnings 0 --ext .ts,.tsx .",
     "lint:prettier": "prettier './src/**/*.{ts,tsx,js,json,css}' --check",
-    "changeset": "changeset"
+    "changeset": "changeset",
+    "version": "changeset version",
+    "publish": "changeset publish"
   },
   "devDependencies": {
     "@changesets/cli": "^2.27.1",
diff --git a/packages/express/package.json b/packages/express/package.json
index 586fd64..531b6b9 100644
--- a/packages/express/package.json
+++ b/packages/express/package.json
@@ -8,7 +8,9 @@
     "build": "tsup src/*",
     "lint:eslint": "eslint --max-warnings 0 --ext .ts,.tsx .",
     "lint:prettier": "prettier './src/**/*.{ts,tsx,js,json,css}' --check",
-    "changeset": "changeset"
+    "changeset": "changeset",
+    "version": "changeset version",
+    "publish": "changeset publish"
   },
   "dependencies": {
     "@example-monorepo/core": "*"
diff --git a/turbo.json b/turbo.json
index 8402ed6..c3df47b 100644
--- a/turbo.json
+++ b/turbo.json
@@ -18,6 +18,15 @@
     "dev": {
       "cache": false,
       "persistent": true
+    },
+    "version": {
+      "cache": false,
+      "persistent": true
+    },
+    "publish": {
+      "cache": false,
+      "persistent": true
     }
+
   }
 }

versionを上げる

$ npm run version
commit 62802584c2bbc5be7fec65d73d33d2efc12b01c3
Author: mizuki_r <ry.mizuki@gmail.com>
Date:   Mon Apr 29 15:40:07 2024 +0900

    bump 0.0.1

diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
new file mode 100644
index 0000000..726ce34
--- /dev/null
+++ b/packages/core/CHANGELOG.md
@@ -0,0 +1,7 @@
+# @example-monorepo/core
+
+## 0.0.1
+
+### Patch Changes
+
+- the first update
diff --git a/packages/core/package.json b/packages/core/package.json
index 74c6f4a..8b1b54c 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@example-monorepo/core",
-  "version": "0.0.0",
+  "version": "0.0.1",
   "private": true,
   "main": "build/index.js",
   "types": "src/index.ts",
diff --git a/packages/express/CHANGELOG.md b/packages/express/CHANGELOG.md
new file mode 100644
index 0000000..07d956c
--- /dev/null
+++ b/packages/express/CHANGELOG.md
@@ -0,0 +1,9 @@
+# @example-monorepo/express
+
+## 0.0.1
+
+### Patch Changes
+
+- the first update
+- Updated dependencies
+  - @example-monorepo/core@0.0.1
diff --git a/packages/express/package.json b/packages/express/package.json
index 531b6b9..1d38dc0 100644
--- a/packages/express/package.json
+++ b/packages/express/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@example-monorepo/express",
-  "version": "0.0.0",
+  "version": "0.0.1",
   "private": true,
   "main": "build/index.js",
   "types": "src/index.ts",

verdaccioとの接続

verdaccioのURLをnpmrcに登録

diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..30fb789
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+registry=http://0.0.0.0:4873

adduserしてverdaccioにユーザを登録

$  npm adduser
npm notice Log in on http://0.0.0.0:4873/
Username: mizuki_r
Email: (this IS public) XXXXX@gmail.com
Logged in on http://0.0.0.0:4873/.

いざ、publish、とおもったのに....

$ npm run publish  

> example-monorepo@0.0.0 publish
> turbo run publish

• Packages in scope: @example-monorepo/core, @example-monorepo/dev-config, @example-monorepo/express
• Running publish in 3 packages
• Remote caching disabled
@example-monorepo/core:publish: cache bypass, force executing 6d170e85bb8ec0a1
@example-monorepo/express:publish: cache bypass, force executing 97213dc3ac891ae0
@example-monorepo/express:publish: 
@example-monorepo/express:publish: > @example-monorepo/express@0.0.1 publish
@example-monorepo/express:publish: > changeset publish
@example-monorepo/express:publish: 
@example-monorepo/core:publish: 
@example-monorepo/core:publish: > @example-monorepo/core@0.0.1 publish
@example-monorepo/core:publish: > changeset publish
@example-monorepo/core:publish: 
@example-monorepo/express:publish: 🦋  warn No unpublished projects to publish
@example-monorepo/core:publish: 🦋  warn No unpublished projects to publish

 Tasks:    2 successful, 2 total
Cached:    0 cached, 2 total
  Time:    701ms 

packageがprivate: trueだったからです。

diff --git a/packages/core/package.json b/packages/core/package.json
index 8b1b54c..cc96e00 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@example-monorepo/core",
   "version": "0.0.1",
-  "private": true,
+  "private": false,
   "main": "build/index.js",
   "types": "src/index.ts",
   "scripts": {
diff --git a/packages/express/package.json b/packages/express/package.json
index 1d38dc0..df29381 100644
--- a/packages/express/package.json
+++ b/packages/express/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@example-monorepo/express",
   "version": "0.0.1",
-  "private": true,
+  "private": false,
   "main": "build/index.js",
   "types": "src/index.ts",
   "scripts": {

今度こそpublish

$ npm run publish

> example-monorepo@0.0.0 publish
> turbo run publish

• Packages in scope: @example-monorepo/core, @example-monorepo/dev-config, @example-monorepo/express
• Running publish in 3 packages
• Remote caching disabled
@example-monorepo/express:publish: cache bypass, force executing 95f6040856482a97
@example-monorepo/core:publish: cache bypass, force executing 304ea4a437f8c0b8
@example-monorepo/express:publish: 
@example-monorepo/express:publish: > @example-monorepo/express@0.0.1 publish
@example-monorepo/express:publish: > changeset publish
@example-monorepo/express:publish: 
@example-monorepo/core:publish: 
@example-monorepo/core:publish: > @example-monorepo/core@0.0.1 publish
@example-monorepo/core:publish: > changeset publish
@example-monorepo/core:publish: 
@example-monorepo/express:publish: 🦋  info npm info @example-monorepo/core
@example-monorepo/core:publish: 🦋  info npm info @example-monorepo/core
@example-monorepo/core:publish: 🦋  info npm info @example-monorepo/express
@example-monorepo/express:publish: 🦋  info npm info @example-monorepo/express
@example-monorepo/core:publish: 🦋  warn Received 404 for npm info "@example-monorepo/core"
@example-monorepo/express:publish: 🦋  warn Received 404 for npm info "@example-monorepo/express"
@example-monorepo/express:publish: 🦋  warn Received 404 for npm info "@example-monorepo/core"
@example-monorepo/express:publish: 🦋  info @example-monorepo/core is being published because our local version (0.0.1) has not been published on npm
@example-monorepo/express:publish: 🦋  info @example-monorepo/express is being published because our local version (0.0.1) has not been published on npm
@example-monorepo/express:publish: 🦋  info Publishing "@example-monorepo/core" at "0.0.1"
@example-monorepo/express:publish: 🦋  info Publishing "@example-monorepo/express" at "0.0.1"
@example-monorepo/core:publish: 🦋  warn Received 404 for npm info "@example-monorepo/express"
@example-monorepo/core:publish: 🦋  info @example-monorepo/core is being published because our local version (0.0.1) has not been published on npm
@example-monorepo/core:publish: 🦋  info @example-monorepo/express is being published because our local version (0.0.1) has not been published on npm
@example-monorepo/core:publish: 🦋  info Publishing "@example-monorepo/core" at "0.0.1"
@example-monorepo/core:publish: 🦋  info Publishing "@example-monorepo/express" at "0.0.1"
@example-monorepo/core:publish: 🦋  error an error occurred while publishing @example-monorepo/express: E409 409 Conflict - PUT http://0.0.0.0:4873/@example-monorepo%2fcore - this package is already present 
@example-monorepo/express:publish: 🦋  error an error occurred while publishing @example-monorepo/express: E409 409 Conflict - PUT http://0.0.0.0:4873/@example-monorepo%2fexpress - this package is already present 
@example-monorepo/core:publish: 🦋  error npm notice Publishing to http://0.0.0.0:4873 with tag latest and public access
@example-monorepo/core:publish: 🦋  error npm ERR! code E409
@example-monorepo/core:publish: 🦋  error npm ERR! 409 Conflict - PUT http://0.0.0.0:4873/@example-monorepo%2fcore - this package is already present
@example-monorepo/core:publish: 🦋  error 
@example-monorepo/core:publish: 🦋  error 
@example-monorepo/express:publish: 🦋  error npm notice Publishing to http://0.0.0.0:4873 with tag latest and public access
@example-monorepo/express:publish: 🦋  error npm ERR! code E409
@example-monorepo/express:publish: 🦋  error npm ERR! 409 Conflict - PUT http://0.0.0.0:4873/@example-monorepo%2fexpress - this package is already present
@example-monorepo/express:publish: 🦋  error 
@example-monorepo/express:publish: 🦋  error 
@example-monorepo/express:publish: 🦋  success packages published successfully:
@example-monorepo/core:publish: 🦋  success packages published successfully:
@example-monorepo/express:publish: 🦋  @example-monorepo/core@0.0.1
@example-monorepo/express:publish: 🦋  Creating git tag...
@example-monorepo/core:publish: 🦋  @example-monorepo/core@0.0.1
@example-monorepo/core:publish: 🦋  Creating git tag...
@example-monorepo/express:publish: 🦋  New tag:  @example-monorepo/core@0.0.1
@example-monorepo/core:publish: 🦋  New tag:  @example-monorepo/core@0.0.1
@example-monorepo/core:publish: 🦋  error packages failed to publish:
@example-monorepo/core:publish: 🦋  @example-monorepo/express@0.0.1
@example-monorepo/express:publish: 🦋  error packages failed to publish:
@example-monorepo/express:publish: 🦋  @example-monorepo/express@0.0.1

あー、個別にpublishするとだめなのか。 一応publishできているけど、多分rootに入れれば動くっぽい

rootにchangesetを設定する

$ npm install -D @changesets/cli
$ npm run changeset init
$ npm run changeset
$ npm run changeset version
$ npm run changeset publish

> example-monorepo@0.0.0 publish
> changeset publish

🦋  info npm info @example-monorepo/core
🦋  info npm info @example-monorepo/express
🦋  info @example-monorepo/core is being published because our local version (0.0.2) has not been published on npm
🦋  info @example-monorepo/express is being published because our local version (0.0.2) has not been published on npm
🦋  info Publishing "@example-monorepo/core" at "0.0.2"
🦋  info Publishing "@example-monorepo/express" at "0.0.2"
🦋  success packages published successfully:
🦋  @example-monorepo/core@0.0.2
🦋  @example-monorepo/express@0.0.2
🦋  Creating git tags...
🦋  New tag:  @example-monorepo/core@0.0.2
🦋  New tag:  @example-monorepo/express@0.0.2

version upに成功した画面

まとめ

  • turborepo x changesetsで良さそう
  • changesetsはrootに置く
    • 必要なパッケージを選択式でpublishできる
    • 変更をstackしてまとめてbumpできる
    • publishも一括でやってくれる
  • パッケージ作成実験にverdaccioは便利