2026-09-06 · npm · nodejs · typescript · cli · 個人開発
配ってみるまで、配れていないことに気づけない
自作の CLI を3本、npm に出しました。3本とも、出す直前に壊れていることが分かりました。
どれもローカルでは動いていました。テストも通っていました。 壊れていたのは「配られた状態」だけで、それは実際に配ってみるまで見えません。
1. npm は node_modules の中の TypeScript を実行できない
Node 22.18 以降は .ts をそのまま実行できます。だから bin に .ts を指定して、
ビルド不要で配れると思っていました。
{ "bin": { "mytool": "src/cli.ts" } }
ローカルでは動きます。パッケージにして入れると、こうなります。
Error [ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING]:
Stripping types is currently unsupported for files under node_modules,
for ".../node_modules/@scope/mytool/src/cli.ts"
Node は node_modules 配下のファイルから型を除去することを拒否します。
意図的な制限です(依存の中身を実行時に変換したくない、という設計判断)。
つまり npx の初回実行で必ず落ちます。
直し方
コンパイルして dist/ を配ります。clone した人は今まで通り src を叩けます。
{
"bin": { "mytool": "dist/cli.js" },
"files": ["dist", "README.md", "LICENSE"],
"scripts": { "prepack": "tsc -p tsconfig.json" }
}
tsconfig.json は rewriteRelativeImportExtensions を入れておきます(TS 5.7 以降)。
ソースの import "./x.ts" が、出力では "./x.js" になります。
{ "compilerOptions": { "rewriteRelativeImportExtensions": true } }
typescript は devDependencies です。 配られるパッケージの依存はゼロのままです。
2. prepack だけでは、git から入れた人に届かない
npx github:owner/repo という入れ方があります。README にこれを書いている人は多い。
prepack はレジストリ向けのビルドで、git 経由のインストールでは走りません。
dist/ が無い状態で入るので、bin が指すファイルが存在しません。
sh: mytool: command not found
prepare を足します。 これは git 経由のインストールでも走ります。
{
"scripts": {
"build": "tsc -p tsconfig.json",
"prepare": "tsc -p tsconfig.json",
"prepack": "tsc -p tsconfig.json"
}
}
両方書くのは冗長に見えますが、片方だけだと、もう片方の経路で必ず壊れます。
3. README に書いた導入コマンドが、一度も動いていなかった
これがいちばん効きました。
公開済みのリポジトリの README に、こう書いてありました。
npx github:quintetkit/ccheck # リポジトリ直下で
まっさらなディレクトリで叩いてみました。
npm error code ENOENT
npm error enoent Could not read package.json
package.json の無いリポジトリは、npm がインストールできません。
そのリポジトリには package.json がありませんでした(依存ゼロで
node src/cli.ts で動くので、要らないと思っていた)。
README の最初の指示に従った人は、全員このエラーを見ていたことになります。公開時からずっと。
スターが0だったので実害は小さいですが、流入が来てから気づいたら最悪でした。
何が共通しているか
3つとも、「自分の環境で動く」と「配られた状態で動く」は別物だという話です。
| 見えるもの | 見えないもの |
|---|---|
ローカルで node src/cli.ts が動く |
node_modules の中では動かない |
npm pack が成功する |
git 経由では dist が無い |
| リポジトリを clone すれば動く | package.json が無いと npm が入れられない |
左側だけ見ていると、右側は永久に見えません。
検査を CI に置く
対処は「配られた状態を作って、実際に叩く」ことです。CI に入れました。
- name: パッケージした形でも動くこと
run: |
npm pack --silent
tmp=$(mktemp -d); cd "$tmp"
npm init -y > /dev/null
npm install --silent "$GITHUB_WORKSPACE"/*.tgz
npx mytool --help > /dev/null
npm pack が成功することは、何も保証しません。 入れて叩くまでは。
git 経由も同じように確かめられます。
cd $(mktemp -d) && npx -y github:owner/repo --help
まっさらなディレクトリで、これが 0 で返ること。 ローカルのキャッシュやグローバルインストールが効いていない場所で試すのが大事です。
公開する前にやったほうがいいこと
npm packの中身を1つずつ見る。テストのコンパイル結果を配っていました(8ファイル)filesを明示する。書かないと、思っていないものが入りますrepositoryの URL が実在するか確かめる。404 を指したまま公開されがちですversionが git のタグと一致しているか。片方だけ上げると、後で追えなくなります
まとめ
- Node は
node_modules配下の.tsから型を除去しない。npxの初回で落ちる - コンパイルして配る。clone した人は元のまま動くようにしておける
prepareとprepackは別の経路。 片方だけだと、もう片方で必ず壊れるpackage.jsonの無いリポジトリはnpx github:で入らない- 「自分の環境で動く」と「配られた状態で動く」は別。左だけ見ていると右は見えない
- CI で、パッケージして・入れて・叩く。
npm packの成功は何も保証しない
あわせて読む
- 自作ツールを composite GitHub Action で配る(npm 公開なし)
- テストが全部通るのに、本物のデータを1件も読めなかった
- 「動いた」を検証と見なすと、1コミットでだけ通るコードが出荷される
Claude Code に設計・実装・レビューを別々の人格として分担させ、GitHub Issue と
ブランチを軸に並列開発を回すための設定一式を MIT で公開しています。
コピーして ./setup.sh を叩けば動きます。技術スタックには依存しません。
https://github.com/quintetkit/quartet
このワークフローだけで実際にツールを 1 つ作りました。Issue の分割から PR、 レビュー、マージまで記録が全部残っています。うまくいかなかった箇所も消していません。
https://github.com/quintetkit/mdlinkcheck
UI 設計人格・レビュー基準・Issue 単位の並列実行スクリプト・実践ガイド 10 章を 足したものは製品ページにあります。
実践ガイド全10章は Zenn Book で読めます(¥1,500・2章まで無料)。
https://zenn.dev/quintetkit/books/claude-code-parallel-workflow
設定一式(5人格・スクリプト・ガイド全文)の配布はこちらです。