npmパッケージ開発の車輪の再発明から解放!CLIツール「forge-npm-pkg」で最高水準のスタートを切る
はじめに
npmパッケージを作るたびに、こんな経験はありませんか?
// また同じ設定をコピペ...
const tsconfig = {
compilerOptions: {
target: "ES2022",
module: "NodeNext",
strict: true,
// 前のプロジェクトから持ってきたけど、これ最新のベストプラクティス?
}
};
- package.jsonのexports設定が複雑すぎる
- ESM? CommonJS? Dual? 結局どれがいいの?
- GitHub ActionsのCI/CDを毎回手書き
- Dependabotの設定も毎回必要
- 半年前のテンプレートはもう古い...
この「毎回同じ車輪の再発明」を解決するために、forge-npm-pkgというCLIツールを作りました。
npx forge-npm-pkg my-awesome-package
1コマンドで、常に最新のベストプラクティスに基づいたnpmパッケージが生成されます。
📦 NPM: https://www.npmjs.com/package/forge-npm-pkg

forge-npm-pkgが解決する5つの課題
| 課題 | 従来の方法 | forge-npm-pkgの解決策 |
|---|---|---|
| 依存関係のバージョン | テンプレートに固定値 → 半年で古くなる | npm registryから動的に最新版取得 |
| Node.js LTSバージョン | 手動でengines更新 | Node.js公式APIから自動検出 |
| package.json exports | ドキュメントと格闘 | ESM/CommonJS/Dualを完璧に生成 |
| CI/CD設定 | 毎回手書き、属人化 | GitHub Actionsフル装備で生成 |
| 品質のばらつき | プロジェクトごとに違う設定 | 標準化されたベストプラクティス |
動的バージョン取得の仕組み
従来のテンプレートツールの問題点を見てみましょう。
❌ 従来のテンプレート
// template/package.json
const dependencies = {
"typescript": "5.3.0", // ← 作成時点のバージョンが固定
"vitest": "1.0.0", // ← 半年後には古い
"tsup": "8.0.0", // ← メンテナンスしないと陳腐化
}
✅ forge-npm-pkgのアプローチ
async function fetchLatestVersion(packageName: string): Promise<string> {
const response = await fetch(
`https://registry.npmjs.org/${packageName}/latest`
);
const data = await response.json();
return data.version;
}
async function fetchLatestVersions(packages: string[]): Promise<Record<string, string>> {
const results = await Promise.all(
packages.map(async (pkg) => [pkg, await fetchLatestVersion(pkg)])
);
return Object.fromEntries(results);
}
💡 npm registryから動的に最新バージョンを取得するため、テンプレートのメンテナンス不要。いつ使っても最新の依存関係でスタートできます。
さらに、リリースから30日以内の新しすぎるバージョンには警告を表示:
⚠ typescript@5.7.0 was released only 15 days ago.
Consider using 5.6.x for stability.
安定性とのバランスも考慮した設計になってます。
Node.js LTSの自動検出
Node.jsのenginesフィールドとCI/CDのマトリクス設定、手動で管理するのは面倒ですよね。
❌ 従来の方法
{
"engines": { "node": ">=18.0.0" }
}
↑ Node.js 18がEOLになっても気づかない...
✅ forge-npm-pkgの方法
async function fetchNodeLTSVersions(): Promise<string[]> {
const response = await fetch('https://nodejs.org/dist/index.json');
const releases = await response.json();
return releases
.filter((r: any) => r.lts) // LTSのみ抽出
.map((r: any) => r.version.replace('v', '').split('.')[0])
.slice(0, 2); // 最新2つのLTS (例: [22, 20])
}
Node.js公式APIから現在のLTSバージョンを取得し、enginesフィールドとGitHub Actionsのマトリクスを自動設定します。
完璧なpackage.json exports設定
package.jsonのexportsフィールド、正しく書けてる自信ありますか?
forge-npm-pkgはESM、CommonJS、Dualの3つのモジュール形式に対応した完璧なexports設定を生成します。
ESM (モダン) の場合
{
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
}
}
Dual (ESM + CommonJS) の場合
{
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
さらに、@arethetypeswrong/cliによるexports検証スクリプトも自動生成:
npm run check:exports # exports設定の正しさを検証
📌 @arethetypeswrong/cliは、パッケージのexports設定が正しくTypeScript型を解決できるかチェックするツールです。ESM/CJSの設定ミスを早期発見できます。
フル装備のCI/CD
生成されるGitHub Actionsワークフローを見てみましょう。
ci.yml - プルリクエスト時の自動テスト
jobs:
test:
strategy:
matrix:
node-version: [20.x, 22.x] # ← LTSを自動検出
steps:
- run: npm test
- run: npm run typecheck
- run: npm run lint
- run: npm run build
publish.yml - タグプッシュ時の自動公開
on:
push:
tags: ['v*'] # v1.0.0 形式のタグで発火
jobs:
publish:
steps:
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
dependabot-auto-merge.yml - 依存関係の自動マージ
# patch/minor → 自動マージ
# major → 手動レビュー必要
💡 GitHub Actionsのバージョン(actions/checkout@v4など)も、GitHub APIから最新版を取得して生成します。
async function fetchActionVersion(action: string): Promise<string> {
const [owner, repo] = action.split('/');
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/releases/latest`
);
const data = await response.json();
return data.tag_name; // "v4" など
}
インタラクティブな設定
◆ What language do you want to use?
│ ○ TypeScript (Recommended)
│ ○ JavaScript
◆ What module format do you want to use?
│ ○ ESM (Modern)
│ ○ CommonJS (Legacy)
│ ○ Dual (ESM + CommonJS)
◆ What test runner do you want to use?
│ ○ Vitest (Recommended)
│ ○ Jest
│ ○ None
◆ Enable ESLint + Prettier?
│ ● Yes / ○ No
◆ Initialize git repository?
│ ● Yes / ○ No
◆ Setup GitHub Actions CI/CD?
│ ● Yes / ○ No
プロジェクトの要件に合わせて柔軟にカスタマイズできます。
生成されるプロジェクト構造
my-awesome-package/
├── src/
│ ├── index.ts
│ └── index.test.ts
├── .github/
│ ├── workflows/
│ │ ├── ci.yml
│ │ ├── publish.yml
│ │ └── dependabot-auto-merge.yml
│ └── dependabot.yml
├── package.json # 完璧なexports設定
├── tsconfig.json # strict mode
├── tsup.config.ts # ビルド設定
├── vitest.config.ts # テスト設定
├── eslint.config.js # ESLint flat config
├── .prettierrc
├── .gitignore
└── README.md
すべてのファイルが連携して動作します。個別にコピペした設定の整合性を心配する必要はありません。
使い方
インストール不要(npx)
npx forge-npm-pkg my-package-name
グローバルインストール
npm install -g forge-npm-pkg
forge-npm-pkg my-package-name
GitHub リポジトリも同時作成
# gh CLI がインストールされていれば
forge-npm-pkg my-package-name --github
設計思想:「標準化」の力
私は業務でチームの開発プロセスを標準化した経験があります:
- Epic/Story/Taskによるタスク管理
- PRベースの開発フロー
- semantic-releaseによる自動バージョニング
この経験から学んだのは:
良いプラクティスを「仕組み化」すれば、チーム全体の品質が上がる
forge-npm-pkgは同じ考えを「npmパッケージ開発」に適用したものです。
「正しい設定」を一度定義すれば、誰でも・いつでも・最高水準のパッケージを作れる
これは個人の生産性向上だけでなく、チーム・組織全体の品質向上につながります。
横展開の可能性
このアプローチは他の領域にも応用できます:
| 領域 | 標準化できること |
|---|---|
| RAGプロジェクト | ベクトルDB設定、チャンク戦略、プロンプトテンプレート |
| Next.jsプロジェクト | 認証設定、API構成、デプロイ設定 |
| AWS CDK | セキュリティ設定、タグ付けルール、モニタリング |
「毎回同じことを繰り返す」→「テンプレート化して標準を担保」
このパターンは様々な開発領域で活用できます。
まとめ
forge-npm-pkgは「npmパッケージ開発のベストプラクティス」を誰でも使える形にしたツールです。
✅ 解決する5つの課題
| 課題 | 解決策 |
|---|---|
| 毎回同じ設定を書く手間 | 1コマンドで全て生成 |
| 古いバージョンの依存関係 | npm registryから動的取得 |
| package.json exports設定の複雑さ | 完璧な設定を自動生成 |
| CI/CD設定の属人化 | GitHub Actionsフル装備 |
| 品質のばらつき | 標準化されたベストプラクティス |
✅ 技術的なこだわり
- npm registry APIから動的バージョン取得
- Node.js公式APIからLTS自動検出
- GitHub APIからActionsバージョン取得
- 新しすぎるバージョンへの警告機能
もしあなたが「npmパッケージ開発を始めるたびに同じ設定を繰り返している」と感じているなら、ぜひ試してみてください。
npx forge-npm-pkg my-awesome-package
📦 NPM: https://www.npmjs.com/package/forge-npm-pkg
📂 GitHub: https://github.com/your-username/forge-npm-pkg
フィードバックやコントリビューションも大歓迎です!
Discussion