はじめに
npm scriptsでtscのようなローカルインストールされたコマンドを実行する際、PowerShellターミナルから直接叩くと「認識されていません」というエラーになることがあります。本記事では、この現象の原因と、npxを使った場合の解決の仕組みについて解説します。
問題
TypeScriptプロジェクトでnpm run buildは正常に動作するのに、同じターミナルで直接tsc -bを実行すると以下のようなエラーが出ました。
tsc : 用語 'tsc' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されません。
英語環境のPowerShellでは以下のように表示されます。
PS C:xxx> tsc -b
tsc: The term 'tsc' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
typescriptはdevDependenciesにインストール済みで、npm run build経由なら問題なくtscが呼び出せているのに、なぜ直接実行だけ失敗するのか疑問でした。
解決方法
原因:PATHの扱いの違い
ローカルインストールされたパッケージの実行ファイルはnode_modules/.bin配下に置かれます。しかし、このディレクトリは常にPATHに入っているわけではありません。
npm run buildを実行すると、npmはそのスクリプトを実行する子プロセスに限り、PATH環境変数の先頭にnode_modules/.bin(および親ディレクトリの同ディレクトリ)を一時的に追加します。スクリプトの実行が終われば、そのPATH拡張も消えます。
つまり:
- 現在のPowerShellセッション自体のPATHは変化しない
- PATH拡張は
npm run buildのスクリプト実行中の子プロセス内でのみ有効 - ターミナルで直接
tscと打つのはnpmを介さない別プロセスのため、この恩恵を受けられない
対処法:npxを使う
npx tsc -b
これで直接実行と同じ感覚でローカルのtscを呼び出せます。
npxの探索順序
npxは以下の順序でコマンドを探します。
-
node_modules/.binにそのコマンドが存在するか確認 - 存在すれば → そのままローカルの実行ファイルを直接実行(ダウンロードやキャッシュは使わない)
-
存在しなければ → npmレジストリから一時的にfetchし、
~/.npm/_npx配下のキャッシュに展開して実行
typescriptがdevDependenciesにインストール済みの場合は手順2に該当し、「一時的にインストールして実行」しているわけではなく、既にあるnode_modules/.bin/tscを見つけて直接パスを指定して実行しているだけです。
「一時的にインストールする」動作(手順3)は、パッケージがローカルにもグローバルにも存在しない場合のみ発生します。
npm run buildとnpxの違いまとめ
| 方法 | 仕組み |
|---|---|
npm run build |
子プロセスのPATHを一時的に拡張し、PATH経由でtscを解決 |
npx tsc |
PATH拡張はせず、npx自身がnode_modules/.binを探索して直接そのパスを実行 |
実行結果(ローカルのtscが動く)は同じですが、コマンドを解決する仕組みが異なります。
おわりに
node_modules/.binが常にPATHに入っていると誤解しがちですが、実際はnpm run実行時の子プロセスに限定された一時的な拡張であることがわかりました。ターミナルから直接ローカルコマンドを叩きたい場合はnpxを使うのが簡単で確実です。
参考
JISOUのメンバー募集中!
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!
▼▼▼
https://projisou.jp