概要
手元の雑多なプライベートnpmパッケージ類をmonorepoに押し込んで効率的に管理しつつ、公開できるものは公開してしまおう、という試みにおける開発環境構築の記録。
pnpm WorkspaceによるmonorepoでTypeScriptからESM/CJS両対応またはESM専用のパッケージを生成し、Changesetsによるパッケージ別のバージョン管理と、これに連動した一括デプロイを行う、というもの。
ステップ毎に各設定ファイル等の一部を引用して記述しているため、全体像は完成形の下記ボイラープレートを参照のこと。
要件
- 作業はWSL2のDebian上で行う
- Node.js は tj/n で管理しバージョンは16系を使用する
- リポジトリは pnpm Workspaceを使用したmonorepo構成とする
- 各パッケージのバージョンは個別管理とする
- Gitプラットフォームは GitHub を使用する
- 開発言語は TypeScript とする
- Linterには ESLint を、Formatterには Prettier を使用し EditorConfig を併用する
- ドキュメンテーションは TSDoc に従い、出力が必要な場合は TypeDoc を使用する
- エディタには Visual Studio Code を使用し、必要に応じてコーディング用フォントと拡張機能を導入する
- Simple Git Hooks と lint-staged でコミット時にLinter/Formatter/型チェックを自動で実行する
- 英語版と日本語版のREADMEを作成し、 Shields.io で各種バッジを貼る
- Changesets でバージョニングとCHANGELOGの生成を行う
-
tsup でESModule/CommonJS両対応のデュアルパッケージとしてビルドする
- またはTypeScriptでESM専用モジュールとしてビルドする
- pnpmでpackage.json上のバージョンが上がったパッケージをnpmに一括デプロイする
執筆時の詳細なバージョン情報
Name | Version |
---|---|
Windows11 | 21H2 |
Debian | 11.3 |
git | 2.36 |
Node.js | 16.15.1 |
pnpm | 7.2.0 |
typescript | 4.7.3 |
eslint | 8.17.0 |
prettier | 2.6.2 |
typedoc | 0.22.17 |
simple-git-hooks | 2.8.0 |
lint-staged | 12.0.1 |
tsup | 6.1.2 |
構築
- 便宜上コードブロックのファイル名はプロジェクトルートをルートディレクトリと見なした絶対パスで記述する
- WSL2の準備は省略
- 概略:BIOSでCPUの仮想化機能を有効化 → WSLを有効化 → Microsoft Storeから Debian をインストール
-
Gitのインストールは省略
- 概略:上記リンクの「ソースからのインストール」の通り
1. Node.jsのインストール
Node.jsのバージョン管理ツール tj/n を n-install を使用してインストールし、Node16系の最新版をインストールする。
# tj/nのインストール(n-install使用)
$ sudo apt-get update
$ sudo apt-get -y install curl
$ curl -L https://bit.ly/n-install | bash
# 16系の利用可能なバージョンを確認
$ n lsr --all | grep ^16
16.15.1
16.15.0
16.14.2
...
# 16系の最新バージョンをインストール
$ n i 16.15.1
$ node -v
16.15.1
2. pnpmのインストール
npmで pnpm をグローバルインストールする。
$ npm i -g pnpm
-
pnpm公式ドキュメント
- 日本語化完了率100%だとか(2022-06-05現在)
3. リポジトリ作成
プロジェクトのディレクトリを作成し、gitリポジトリとして初期化して設定を行う。
# リポジトリのディレクトリを作成
$ mkdir project-name
$ cd project-name
# git初期化
$ git init
$ git config core.filemode false
# その他必要に応じてユーザ名・メールアドレス・リモートリポジトリ等を設定
- Windows環境で編集される可能性があるリポジトリは
filemode = false
にした方がよさそう- パーミッションが重要なリポジトリはWinでは触らないはず
- 同様に
ignorecase
をどうするかも悩ましい(WinのFSは大文字小文字を区別しない)
3-1. .gitignore
.gitignore
ファイルを作成して設定する。
GitHub公式の.gitignoreテンプレート から各OS用とNode.js用の内容を抽出して結合したものをベースとする。
# tsup
dist
# TypeDoc
packages/*/docs
# VSCode
.vscode
*.code-workspace
# WSL
*Zone.Identifier
# 以下テンプレートの内容
4. ルートプロジェクト設定
ルートプロジェクトとはmonorepoにおける親となるリポジトリのこと。子はサブプロジェクト。
ルートプロジェクトの package.json
とpnpmの設定ファイルを作成し編集する。
4-1. 初期化
# package.jsonの雛形を生成
$ pnpm init
4-2. package.json
{
"name": "<project-name>",
"version": "0.0.1", // 非公開なので重要ではないが、大きな変更があったら適宜手動でインクリメント予定
"description": "<project-description>",
"author": "<author-name>",
"private": true, // ルートプロジェクトは公開しない
"homepage": "https://github.com/<github-username>/<repository-name>",
"repository": {
"type": "git",
"url": "https://github.com/<github-username>/<repository-name>.git"
},
"bugs": {
"url": "https://github.com/<github-username>/<repository-name>/issues"
},
"scripts": {
"preinstall": "npx -y only-allow pnpm" // pnpmを強制する
},
"engines": {
"node": ">=16.8.0", // 後述
"npm": "use pnpm", // pnpmを強制する
"pnpm": ">=7",
"yarn": "use pnpm" // pnpmを強制する
}
}
-
npmにおけるpackage.json仕様(V8系)
-
package.json
の内容にはnpm用の記述とNode.js用の記述が同居している点に注意、兼用のフィールドもある - 参考: Node.jsにおけるpackage.jsonフィールド定義
-
-
engines.node
は node.green などを参考に同じメジャーバージョン内で互換性のありそうな最も古いバージョンを指定した
4-3. サブプロジェクト検出パターン設定
pnpm-workspace.yaml
を作成して設定する。
packages:
- 'packages/*'
- 公式ドキュメント
- 今回はpackagesディレクトリ直下のディレクトリをサブプロジェクトと見なす
5. サブプロジェクト設定
サブプロジェクトの package.json
を作成し編集する。
5-1. 初期化
# ディレクトリ追加
$ mkdir packages
$ mkdir packages/package-name
$ cd packages/package-name
# 初期化
$ pnpm init
5-2. package.json
{
"name": "<package-name>",
"version": "0.0.0", // Changesetsが自動でインクリメントするため0.0.0にしておく
"description": "<package-description>",
"author": "<author-name>",
"license": "<license>",
"homepage": "https://github.com/<github-username>/<repository-name>/tree/main/packages/<package-name>#readme", // GitHub上のREADME.mdを使う場合
"repository": {
"type": "git",
"url": "https://github.com/<github-username>/<repository-name>.git",
"directory": "packages/<package-name>" // package.jsonのあるサブディレクトリを指定
},
"bugs": {
"url": "https://github.com/<github-username>/<repository-name>/issues"
},
"keywords": [
"hoge", // 後述
"fuga"
],
"files": [ // パッケージに含めるファイル
"dist" // pnpm publishを再帰的に実行する際は `./dist` だと処理されない
]
}
-
package.json
などはfiles
フィールドで指定しなくても標準でパッケージに含まれる- 詳細は npmにおけるpackage.json仕様(V8系) を参照
- 後述するがルートプロジェクトでpnpm Workspaceの機能
pnpm -r publish
を使用して再帰的にデプロイする場合、files
フィールドのパス指定の先頭に./
があると処理されない- 2022Q1に再帰的なpublishがfilesフィールドを参照しないバグが起票されて解決しており、その結果が現状であるため、現時点では仕様と思われる
- 相対パスを
./
から始める流派の人は要注意 - サブプロジェクト毎に
npm publish
(pnpmではなくnpmでのデプロイ)を行う場合は./
が入っていても問題ない
-
keywords
フィールドは npm のパッケージ情報Readmeタブ最下部のKeywords
欄に表示される- 検索対象なので過不足のないように
- npmで公開しないものは
private: true
を指定しておく
6. TypeScript設定
TypeScriptコンパイラを導入し設定する。
# インストール
$ pnpm add -wD typescript @tsconfig/node16-strictest-esm
6-1. tsconfig.json
-
TSConfig公式リファレンス
- 日本語版もそこそこの翻訳率
-
tsconfig.json
は TypeScriptコンパイラ の設定ファイル、即ちtsc
コマンド実行時の設定を行うもの- 内容は大別すると入出力に関する設定とコーディング規約に関する設定に分かれている
- 入出力に関する設定は当然ながらプロジェクトごとに異なるため、プロジェクトごとに
tsconfig.json
が必要になる- 互換コンパイラ(ESBuild等)も使用する
- コーディング規約に関する設定はESLintのrulesに相当する
- こちらはLinterやFormatterが利用する
-
tsconfig.json
を参照するLinterに「npm-scriptsやテストで使用するスクリプト」や「*rc.ts
*.config.ts
系の設定ファイル」などもチェックさせたい場合、これらはコンパイルには無関係なファイルなので、コンパイル用のtsconfig.json
のinclude
に含めてしまうのは違和感がある- 設定によっては「どこからも参照されていない孤立ファイルがある」として怒られることも
- コンパイル用の
tsconfig.json
は過不足のないように設定し、Linter用にコンパイル用の設定を継承して調整したものを別途用意するのが妥当と思われる
ルートプロジェクト
@tsconfig/bases に収録されている @tsconfig/node16-strictest-esm
を継承し、適宜オーバーライドする。
{
"extends": "@tsconfig/node16-strictest-esm/tsconfig.json",
"compilerOptions": {
"lib": ["esnext"], // enginesで指定したNode.jsが属するバージョンを選択
// 以下お好みで
"resolveJsonModule": true,
"moduleResolution": "node", // resolveJsonModuleをtrueにする場合は継承していても書かないとエラーになる
"declaration": true,
"noEmitOnError": true,
"allowSyntheticDefaultImports": true,
"checkJs": false
}
}
サブプロジェクト
ルートプロジェクトを継承し、入出力を設定する。
{
"extends": "../../tsconfig.json", // ルートプロジェクトから継承
"compilerOptions": {
"outDir": "./dist" // コンパイル先はdistとする
},
"include": ["src/**/*"] // ソースはsrc配下に収める
}
6-2. 型チェック
サブプロジェクトのnpm-scriptsに型チェックの設定を追加する。
"type-check": "tsc --noEmit -p ."
- tsc CLIオプション(公式)
- コミット時の自動チェックに使用する
- なお、サブプロジェクトで共通のビルド・テスト関連の依存関係はホイストを利用してルートプロジェクトの
devDependencies
で賄うこととする-
typescript
jest
ts-jest
tsup
rimraf
など - それ以外の型定義などは個々のサブプロジェクトの
devDependencies
に入れる
-
7. ドキュメンテーション設定
TypeDoc を導入し TSDoc 形式のドキュメンテーションをドキュメントとして出力できるようにする。
# インストール
$ pnpm add -wD typedoc
- TSDocはTypeScriptの開発元であるMicrosoft謹製のドキュメンテーション仕様
- 概ねJSDocから型関係の記述を省略したもの
- 執筆時のバージョンが
0.14.1
であることからわかるように、まだ正式版ではない
- 今のところ @microsoft/tsdoc には出力機能がないため TypeDoc を使用する
"typedoc": "typedoc src/index.ts"
- サブプロジェクトのnpm-scriptsにTypeDoc出力の設定を追加する
packages/*/docs
- TypeDocは標準設定では
docs
ディレクトリに出力するのでgitに無視させる
8. Linter/Formatter設定
ESLint と Prettier および各種設定を導入し、文法チェックとフォーマットを設定する。
# インストール
$ pnpm add -wD eslint eslint-define-config eslint-plugin-n \
@typescript-eslint/eslint-plugin @typescript-eslint/parser \
eslint-plugin-import eslint-import-resolver-typescript \
eslint-plugin-tsdoc eslint-config-prettier prettier
-
eslint-define-config
は複雑になりがちな.eslintrc.js
をTSの型定義を利用して文法チェックする仕組み -
eslint-plugin-n
は更新が途絶えていたNode.js用プラグインの後継
8-1. ESLint
// @ts-check
// ↑ eslint-define-config用
const { defineConfig } = require('eslint-define-config') // eslint-disable-line
module.exports = defineConfig({
root: true,
env: {
es2021: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended', // TypeScript用の設定はこれで全て入る
'plugin:import/recommended', // eslint-plugin-import
'plugin:n/recommended', // eslint-plugin-n
'prettier',
],
plugins: ['eslint-plugin-tsdoc'],
parserOptions: {
ecmaVersion: 'latest',
},
settings: {
'import/parsers': { // import用設定
'@typescript-eslint/parser': ['.ts'],
},
'import/resolver': { // import用設定
typescript: {
alwaysTryTypes: true,
project: 'packages/*/tsconfig.json',
},
},
},
rules: {
// 必須設定
// eslint-plugin-node
'n/no-missing-import': 'off', // 拡張子を省略したimportがエラーになるため無効化
'n/no-unsupported-features/es-syntax': 'off', // importを使うので無効化
// eslint-plugin-import
'import/order': ['error', { 'newlines-between': 'always' }], // エラーになるよう設定することで自動修正が効くようになる
'import/no-named-as-default-member': 'off', // デュアルパッケージでビルドする場合、この設定に従うと一部のimportが動作しない
// eslint-import-resolver-typescript
'import/no-unresolved': 'error', // 解決できないimportをエラー扱いにする
// eslint-plugin-tsdoc
'tsdoc/syntax': 'warn', // TSDoc関連は全てwarning扱いにする
// 以下お好みで
'@typescript-eslint/no-non-null-assertion': 'off', // Mapが使いづらいため
'@typescript-eslint/no-implicit-any-catch': [ // try..catchでerrorを扱うため
'error',
{ allowExplicitAny: true },
],
'@typescript-eslint/no-explicit-any': 'off', // 厳しすぎる
'@typescript-eslint/no-empty-interface': 'off', // 厳しすぎる
},
})
- ESLint設定ファイルに関する公式ドキュメント
-
extends
はその名の通り全体の様々な箇所に設定を注入するため、適宜定義プロジェクト側でオーバーライドする必要がある - 各extendsの内容を知りたければそれぞれの設定ファイルの原本を参照する
-
@typescript-eslint/recommended
-
@typescript-eslint/base
と@typescript-eslint/eslint-recommended
を含んでいることがわかる
-
- plugin:import/recommended
- plugin:n/recommended
-
@typescript-eslint/recommended
packages/*/dist
packages/*/docs
packages/*/coverage
packages/*/__tests__/src
packages/*/__tests__/dist
jest.config.cjs
!.*.js
!.*.cjs
!.*.mjs
!.*.ts
- .eslintignoreに関する公式ドキュメント
- 以下を無視する
- 各種出力ファイル(まだ説明していないものは後述する)
- 敢えて瑕疵のあるソースを含める可能性があるテスト用ファイル類(後述)
- TSDocに怒られる
jset.config.cjs
(回避手段検討中)
- dotファイル類は逆に標準ではチェックされないため「無視リストから外す」記述を行う必要がある
- dotディレクトリについても同様
- 公式ドキュメントに動作機序とサンプルが載っている
"lint": "eslint --fix **/*.{ts,d.ts,js,cjs,mjs}"
- ルートプロジェクトのnpm-scriptsに追加
-
.eslintignore
が正しく設定できていれば対象拡張子の全ファイルを対象に実行すればよい
8-2. Prettier
// お好みで
{
"singleQuote": true,
"semi": false
}
- 公式ドキュメント
- これがトレンドのようなので
packages/*/dist
packages/*/docs
packages/*/coverage
packages/*/__tests__/src
packages/*/__tests__/dist
*.md
- 公式ドキュメント
- 概ね
.eslintignore
と同様 - Markdownのフォーマットはなんか変なので使用しない
- VSCodeの
markdownlint
に任せる-
markdownlint-cli
かmarkdownlint-cli2
を入れて自動化してもいいがひとまずスキップ
-
- VSCodeの
"prettier": "prettier --write --ignore-unknown **/*"
- ルートプロジェクトのnpm-scriptsに追加
-
.prettierignore
が正しく設定できていれば全ファイルを対象に実行すればよい
8-3. EdiotorConfig
# お好みで
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
indent_size = 4
trim_trailing_whitespace = false
[*.json]
insert_final_newline = false
9. VSCode
- WSLで開発するなら必須感がある
- そろそろ
files.excludeGitIgnore
が利用できるようになりそう(何年越し?)
9-1. 拡張機能
-
Remote - WSL
- VSCodeから直接WSL内のソースを扱う、ターミナルもWSLのものになる
-
EditorConfig for VS Code
-
.editorconfig
を反映する
-
-
ESLint
- ESLintのエラーを表示する
-
Prettier - Code formatter
- Prettierでフォーマットする
-
markdownlint
- PrettierのMarkdownフォーマッタがなんか変なのでこちらで代替する
- テーブル整形用に Markdown Table を組み合わせる
- その他便利系
- GitLens
- zenkaku ※コーディング用フォントを使用する場合はなくてもよい
- Quick and Simple Text Selection
- Trailing Spaces
- Path Autocomplete ※引用符外でも補完を有効にするオプションがある
- Insert Numbers
- Incrementor
- indent-rainbow
9-2. フォント
- お好みで
- 個人的には PlemolJP を使用
10. precommitフック
Simple Git Hooks と lint-staged を導入し、コミット時に自動で文法チェック・フォーマット・型チェックが行われるように設定する。
# インストール
$ pnpm add -wD simple-git-hooks lint-staged
{
// ...
"scripts": {
"prepare": "simple-git-hooks"
},
"simple-git-hooks": {
"pre-commit": "pnpm exec lint-staged --concurrent false -c lint-staged.mjs"
},
// ...
}
- 型チェックをステージされたパッケージに絞って実行するためlint-stagedの設定は外に出してJavaScriptで記述する
- lint-stagedの設定ファイルの外部ファイル化は
package.json
を長くしたくない場合も有効
export default {
// Prettier
'**/*': 'prettier --write --ignore-unknown',
// ESLint
'**/*.{ts,d.ts,js,cjs,mjs}': 'eslint --fix',
// TypeScript
'packages/*/src/**/*.ts': (filenames) => {
const updatedPackages = new Set()
const reGetPackageName = /packages\/(?<packageName>.+)\/src/
for (const filename of filenames) {
const found = filename.match(reGetPackageName)
updatedPackages.add(found.groups.packageName)
}
return `pnpm --filter ${[...updatedPackages].join(' --filter ')} type-check`
},
}
- PrettierとESLintの設定はnpm-scriptsと同一
- 各パッケージのsrcディレクトリ内のTSファイルがステージされていた場合、ステージされたTSファイルを含むパッケージのみ型チェックを実行する
- pnpmの
--filter
オプションでパッケージを指定し、各パッケージのnpm-scripts
にあるtype-check
を実行している-
--filter
オプションは複数指定して並列実行できる
-
- pnpmの
$ pnpm prepare
- 忘れずにフックを有効化する
11. ビルド
11-1. デュアルパッケージ
tsup を導入してESModuleとCommonJS対応のデュアルパッケージでビルドし、サブプロジェクトの package.json
をデュアルパッケージに対応させる。
# インストール
$ pnpm add -wD tsup rimraf
{
// ...
"main": "./dist/index.js",
"module": "./dist/index.mjs", // どこで定義されているか不明
"types": "./dist/index.d.ts", // TypeScriptによって定義されているフィールド
"exports": { // Node.js 12 で追加された、パターンへの対応は14から
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"clean": "rimraf ./dist", // 出力ディレクトリを削除
"tsup": "tsup src/index.ts --dts --format esm,cjs", // 型定義を出力、ESModuleとCommonJSを出力
"build": "pnpm clean && pnpm tsup" // 上二つを順次実行
},
// ...
}
-
Node.jsにおけるpackage.jsonフィールド定義
-
package.json
の内容にはnpm用の記述とNode.js用の記述が同居している点に注意、兼用のフィールドもある- 例えば
name
やmain
フィールドは兼用、files
フィールドはnpm用
- 例えば
- 参考: npmにおけるpackage.json仕様(V8系)
-
11-2. ESM専用パッケージ
TypeScriptのESM対応により今後はESM専用のパッケージも増えていくと思われる。依存関係にESM専用パッケージを含むパッケージは自身もESM専用にならざるを得ない。
TSCでESM専用パッケージをビルドする場合は以下のような設定になると思われる。(2022-07-10時点)
{
// ...
"type": "module",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"clean": "rimraf ./dist", // 出力ディレクトリを削除
"tsc": "tsc -p .", // tsconfig.jsonの設定に従ってコンパイル
"build": "pnpm clean && pnpm tsc" // 上二つを順次実行
},
// ...
}
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "esnext",
"removeComments": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}
12. README
-
README.md
はnpmパッケージとして公開した際にパッケージのホームとして表示されるため英語で記述する - 日本語のREADMEは
README.ja.md
やREADME.ja_JP.md
としておき、冒頭でリンクを貼ることとする - 有名所のパッケージ等を参考に最初に規格を統一しておいた方が後々面倒がない
- とはいえ凝ろうと思えばどこまでも凝ることができるため時間を溶かさない程度で
12-1. バッジ
-
Shields.io
- 様々なサービスのエンドポイントからSVGのバッジを生成するサービス
- npmパッケージであれば公開中のバージョンや設定しているライセンス、qualityやmaintenance等のレートなどが表示できる
- 若干わかりづらいが、サイトを開いた際にフォーカスの当たっている水平線は検索用の入力欄であり、名称やサービスのURLから対応しているエンドポイントを検索できる
- テキストとアイコン(次項参照)を組み合わせた自由記述での生成も可能
- 様々なサービスのエンドポイントからSVGのバッジを生成するサービス
-
Simple Icons
- Shields.ioで使用できるサービス・技術・ファイルタイプ・ブランド・企業などのアイコン
- 全アイコンslugリスト
例としてパッケージとしてのnpmのバージョンとライセンスを表示する場合は以下のようになる。
![npm version](https://img.shields.io/npm/v/npm.svg)
![License](https://img.shields.io/npm/l/npm)
<!-- リンクを貼るなら更に囲む -->
[![npm version](https://img.shields.io/npm/v/npm.svg)](https://www.npmjs.com/package/npm)
13. Changesets
Changesetsを導入し、対話型CLIで CHANGELOG.md
の追記と package.json
のバージョンのインクリメントを行う。
# インストール
$ pnpm add -wD @changesets/cli
# 初期化
$ pnpm changeset init
- Changesetは標準でpnpmのworkspaceに対応しておりインストールするだけで設定不要で使用できる
- pnpmの公式ドキュメントにもChangesetsとの連携のページがある
13-1. アップデート対象パッケージの選択とサマリーの入力
$ pnpm changeset
- Changesetsによるバージョン管理はコミット等には紐付いておらず、完全に任意のタイミングで実行できる
-
changeset
コマンドを実行すると対話式CLIで更新するパッケージの選択とCHANGELOG.md
のサマリーの入力を行う - 基本的な流れは以下の通り(選択内容やその時点のバージョンによって変化する)
- 更新するパッケージの選択
- メジャーバージョンを上げるパッケージの選択
- マイナーバージョンを上げるパッケージの選択
- パッチバージョンを上げるパッケージの選択
- サマリーの入力
- 入力確認
-
changeset
コマンドを最後まで進めるとルートプロジェクトの.changeset
ディレクトリにランダムな名前のMarkdownファイルが生成される- このMarkdownには対象サブプロジェクトとバージョンアップの種別を示すメタデータ、および
CHANGELOG.md
に追記される内容(=入力したサマリー)が記録されている - 次節の操作でサブプロジェクトの
CHANGELOG.md
に適切に転記される
- このMarkdownには対象サブプロジェクトとバージョンアップの種別を示すメタデータ、および
- サマリーを複数入力する場合やパッケージ毎に異なるサマリーを入力する場合は
pnpm changeset
を複数回実行する- 変更が発生する度に
pnpm changeset
でサマリーを入力しておけば抜け漏れを防げる - 生成されたMarkdownを編集しても構わないが、トップレベルの箇条書きの項目を増やしたい場合は
pnpm changeset
を複数回実行するしかない
- 変更が発生する度に
- 次節で行う操作で書き換わった後の
CHANGELOG.md
を直接編集しても問題ないため、ケースバイケースで使い分けるとよい
13-2. CHANGELOG.mdの更新とバージョンのインクリメント
$ pnpm changeset version
-
.changeset
ディレクトリ内のMarkdownファイルで指定されたメタデータに従って対応するパッケージのpackage.jsonのバージョンをインクリメントする -
.changeset
ディレクトリ内のMarkdownファイルの内容を各パッケージのCHANGELOG.md
に追記する- 完了すると
.changeset
ディレクトリ内のMarkdownは削除される
- 完了すると
14. デプロイ
pnpmのpublishコマンドを再帰実行フラグ( -r
)付きで使用し、バージョン情報を元に更新されたパッケージを全てnpmにデプロイする。
# 念のため確認したければドライラン
$ pnpm -r publish --dry-run
# ワンタイムパスワードを指定して実行
$ pnpm -r publish --otp <one_time_password>
- npmに公開中のバージョンと
package.json
のバージョンが比較され、更新されているもののみデプロイされる - Changesetsを適切に使用していればデプロイ対象のパッケージは
package.json
のバージョンが上がっているため自動的にデプロイ対象になる - gitのステージは綺麗な状態にしておく必要がある
- テスト等の際は
--no-git-checks
で無効化できるが、普段は有効にして安全なデプロイフローを習慣化した方がよさそう
- テスト等の際は
- 当然ながらnpmにログインしている必要がある
15. 依存関係の更新
手動で行う場合は定番のCLIツールである npm-check-updates が有用。
# ncuをサブディレクトリを含めて再帰的に実行する
$ ncu --deep
# ncuの結果をpackage.jsonに反映する
$ ncu -u --deep
- グローバルインストールしてもいいし
pnpm dlx
等でオンデマンドで実行してもよい - 対話モードやフィルタ機能、更には更新されたパッケージのアップデート後に自動でテストを実行するモードなどもあり、かなり高機能
- 細かい条件がある場合は設定ファイル(
.ncurc.{json,yml,js}
)に書き出しておくこともできる
関連記事
本稿の13-2~14の手順をGitHub ActionsとChangesets Release Actionで自動化した。