インストールタスクにパッケージマネージャー自動検出を追加

rails/jsbundling-rails

javascript:install:* タスクでYarnコマンドがハードコードされていた問題を解消し、ロックファイルから使用中のパッケージマネージャーを自動検出する Jsbundling::PackageManager モジュールを導入しました。これにより、npm・pnpm・bunを使うプロジェクトでも正しいコマンドでセットアップが行われるようになります。

背景

インストールタスクと実際のプロジェクト環境の間にあったパッケージマネージャーの不一致が、この変更の起点です。build.rake では Jsbundling::Tasks を通じてロックファイルによるパッケージマネージャー検出が行われていましたが、javascript:install:[esbuild|rollup|webpack] を実行するインストールスクリプト群(install.rb)では yarn addyarn build がハードコードされたままでした。(#217)

rails/rails 側では既に rails/rails#56636Rails::Generators::JsPackageManager が導入され、ジェネレーターでのパッケージマネージャー検出が統一されていました。本PRはその設計方針をjsbundling-railsにも適用し、インストールタスクとビルドタスクで検出ロジックを共通化します。

技術的な変更

新規モジュール Jsbundling::PackageManagerlib/jsbundling/package_manager.rb に追加され、ビルドタスクで使われていた Jsbundling::Tasks のインライン実装を置き換えます。

PackageManager はまずロックファイルの存在によってパッケージマネージャーを特定し、ロックファイルが見つからない場合は実行可能コマンドを探します。

def self.detect(root)
  if root.join("bun.lock").exist? || root.join("bun.lockb").exist? || root.join("bun.config.js").exist?
    :bun
  elsif root.join("pnpm-lock.yaml").exist?
    :pnpm
  elsif root.join("package-lock.json").exist?
    :npm
  else
    detect_by_executable || :yarn
  end
end

def detect_by_executable
  %i[ bun yarn pnpm npm ].each do |exe|
    return exe if system "command -v #{exe} > /dev/null"
  end
end

各パッケージマネージャーのコマンドは MANAGERS 定数にHashとして集約されており、addadd_devinstallbuild の4種類が定義されています。

MANAGERS = {
  bun:  { add: "bun add %s",      add_dev: "bun add %s --dev",    install: "bun install",  build: "bun run build" },
  pnpm: { add: "pnpm add %s",     add_dev: "pnpm add -D %s",      install: "pnpm install", build: "pnpm run build" },
  npm:  { add: "npm install %s",  add_dev: "npm install -D %s",   install: "npm ci",       build: "npm run build" },
  yarn: { add: "yarn add %s",     add_dev: "yarn add --dev %s",   install: "yarn install", build: "yarn build" }
}.freeze

インストールスクリプト側では、ハードコードされていた yarn addyarn build がそれぞれ Jsbundling::PackageManager.add_commandJsbundling::PackageManager.build_command の呼び出しに置き換わります。

変更前:

run "yarn add --dev esbuild"
# ...
run %(yarn build)

変更後:

run Jsbundling::PackageManager.add_command("esbuild", dev: true)
# ...
run Jsbundling::PackageManager.build_command

同様の変更が rollup/install.rbwebpack/install.rb にも適用されています。Procfile.dev のウォッチコマンドも install_procfile.rb 経由で動的に生成されるようになり、たとえばnpmを検出した場合は js: npm run build -- --watch が書き込まれます。

build.rake では Jsbundling::Tasks のインライン実装(約50行)が削除され、新しい PackageManager モジュールへの委譲に統一されました。これにより、ビルドタスクとインストールタスクの両方で同一の検出ロジックが使われます。

設計判断

Jsbundling::Tasks を廃止し Jsbundling::PackageManager に一本化する アプローチが採られました。build.rake に埋め込まれていた検出ロジックを独立したモジュールに切り出すことで、インストールスクリプトからも再利用できる形になっています。これはrails/railsが Rails::Generators::JsPackageManager として同様の分離を行った設計と一致しており、両プロジェクト間での挙動の一貫性が意識されています。

ロックファイルが存在しない場合のデフォルトはYarnです。これはリリース済みのRails v8.1.2の挙動に合わせた判断であり、デフォルトをnpmに変えるべきという提案(#217)に対して、既存リリースとの一貫性を優先しています。ロックファイルが見つからない場合は detect_by_executable で実行可能コマンドを探すため、yarnがインストールされていない環境でも代替マネージャーが選択されます。

npmのインストールコマンドには npm install ではなく npm ci が使用されています。これはCIや再現性の高いビルド環境を想定した選択であり、package-lock.json の厳密な依存解決を優先する設計意図が読み取れます。

まとめ

Jsbundling::PackageManager の導入により、インストールタスクとビルドタスクの両方でパッケージマネージャーの検出ロジックが統一されました。ロックファイルという既存の環境情報を起点とした検出戦略は、rails/railsのジェネレーター実装とも整合しており、プロジェクトが使うパッケージマネージャーを明示的に指定しなくても正しく動作する設計が実現されています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
1b4daed9

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

記事全体の構成が「リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)」という理想的な3部構成に準拠しています。各セクションの役割が明確で、読者が変更の全体像をスムーズに理解できるようになっています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```ruby:ファイルパス)およびGitHubのPR・Issueへのリンク記法([#123](URL))が、ガイドライン通りに正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

内容は、パッケージマネージャー、Rakeタスク、Railsの内部実装といった専門的なトピックに焦点を当てており、対象読者であるエンジニアに適した技術レベルと語彙で記述されています。冗長な説明はなく、簡潔です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが「総論→各論」の構成を持ち、各パラグラフはトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が徹底されています。1段落1トピックが守られており、段落長も適切で非常に読みやすいです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているすべてのコードブロック(`package_manager.rb` の新設、`install.rb` の変更前後など)は、提供されたDiff情報と完全に一致しています。ファイル名も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「ロックファイル」「ハードコード」「インライン実装」「ジェネレーター」などの技術用語が、文脈に沿って正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

パッケージマネージャーの検出ロジック(ロックファイル優先、次に実行可能コマンド)や、`build.rake` から新モジュールへの責務移譲など、技術的な変更に関する説明はすべてDiffの内容と整合性が取れており、正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(`Rails::Generators::JsPackageManager` との整合性、Yarnがデフォルトである理由など)は、PRのDescriptionで明確に裏付けられています。根拠のない推測や創作(ハルシネーション)は一切見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#224)、関連Issue番号(#217)、関連PR番号(rails/rails#56636)、バージョン番号(v8.1.2)など、記事に含まれるすべての数値・固有名詞は正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「インストールタスクにパッケージマネージャー自動検出を追加」は、PRの主題「Detect package manager in install tasks」を的確に要約しており、記事内容とも完全に一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事内で言及されている「Rails v8.1.2」というバージョン情報は、PRのDescriptionに記載されているものであり、PR情報に基づかない外部知識の持ち込みはありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

PRで行われた変更を記述しており、時間表現に歪曲や不正確な点はありません。PRで「将来の計画」として述べられている部分は記事に含まれておらず、事実のみを記述する姿勢が守られています。