🐙

npmパッケージ開発の車輪の再発明から解放!CLIツール「forge-npm-pkg」で最高水準のスタートを切る

に公開

はじめに

https://youtu.be/BSMumRV1xR4

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のアプローチ

src/version-fetcher.ts
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の方法

src/node-lts.ts
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 - プルリクエスト時の自動テスト

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 - タグプッシュ時の自動公開

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 - 依存関係の自動マージ

dependabot-auto-merge.yml
# patch/minor → 自動マージ
# major → 手動レビュー必要

💡 GitHub Actionsのバージョン(actions/checkout@v4など)も、GitHub APIから最新版を取得して生成します。

src/github-actions.ts
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