📦

はじめてのOSSをnpmで公開しようとしたら、パッケージ名の類似性チェックにひっかかった話

に公開

はじめに

先日、オープンソースのCLIツール「ccresume」を公開しました。ほとんどバイブコーディングで仕上げた拙作ですが、困っていた人も多かったようで、思わぬ反響をいただきました。

https://zenn.dev/sasazame/articles/3634040f7fb4da

今回の記事は、そんなccresumeを公開したときにおきた、npmのパッケージ名についてのお話です。


ccresumeですが、npmに公開しようとしたところ、予期せぬエラーに遭遇しました。npm view ccresumeで確認すると名前は空いているのに、実際に公開しようとすると「名前が類似している」という理由で拒否されたのです。

npxやbunxなどで、簡単にツールが使用できるnpmでの公開ですが、そのネーミングには、バリデーションチェックに加えて、既存のリポジトリと酷似していないことのチェックもかかるようです。

この記事では、問題が発覚した経緯と、その解決方法について紹介します。

問題が起きた経緯

1. パッケージ名の確認

まず、使いたいパッケージ名が利用可能か確認します:

$ npm view ccresume
npm error code E404
npm error 404 Not Found - GET https://registry.npmjs.org/ccresume - Not found
npm error 404
npm error 404  'ccresume@*' is not in this registry.

エラーコード404。つまり、ccresumeという名前は誰も使っていません。やった!

2. --dry-runでの事前チェック

念のため、--dry-runオプションで公開プロセスをシミュレートしました:

$ npm publish --dry-run
...
npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access (dry-run)
+ ccresume@0.3.0

問題なし!これで安心して公開できる……と思いました。

3. 実際の公開で失敗

$ npm publish
npm error code E403
npm error 403 403 Forbidden - PUT https://registry.npmjs.org/ccresume - Package name too similar to existing package cc-resume; try renaming your package to '@sasazame/ccresume' and publishing with 'npm publish --access=public' instead
npm error 403 In most cases, you or one of your dependencies are requesting
npm error 403 a package version that is forbidden by your security policy, or
npm error 403 on a server you do not have access to.

なんと!cc-resumeという既存のパッケージと「類似している」という理由で拒否されてしまいました。

実際に確認すると、確かにそのパッケージは存在しましたが、今回のClaude Code向けツールとは無関係で、おそらく「CCさんの履歴書」を意味するものでした。(resumeとrésuméが同じ綴りなのが悪い……)

npmの類似性チェックの仕組み

なぜ類似性チェックが必要なのか

npmは、タイポスクワッティング対策の観点から類似したパッケージ名を制限しているようです。

昔もありましたよね、google.comとまちがえてgoggle.comにいくと、ウィルスに感染するサイトに飛んでしまう、みたいなやつ。アレです。

(関係ないですが、goggle.comは現在、googleの管理下ドメインらしいです。参考:Wikipedia | Goggle.com

類似性の判定基準

2018年のnpm公式ブログの投稿によると、パッケージ名の類似性チェックについて以下のように書かれています。

If you are publishing a new package—that is, a package that has not been in the registry before—we remove punctuation from its name and compare it to existing package names. If the names are identical without punctuation, we do not allow the package to be created. Instead, we suggest that you publish the package with that name under your own scope. You can, of course, also find a new name that’s sufficiently different from an existing package, but using your own scope is a fast way to do that.

(AI訳)
新しいパッケージ(つまり、これまでにレジストリに存在していないパッケージ)を公開する場合、名前から句読点(ピリオドやハイフンなど)を取り除いて、既存のパッケージ名と比較します。句読点を除いた状態で名前が既存のパッケージと同一である場合、そのパッケージを作成することはできません。
その代わりに、自分のスコープ下でその名前のパッケージを公開することを提案しています。もちろん、既存のパッケージとは十分に異なる新しい名前を見つけるという方法もありますが、自分のスコープを使う方が手っ取り早い方法です。

引用元:https://blog.npmjs.org/post/168978377570/new-package-moniker-rules

パッケージネームに使用できる句読点はドット(.)、ハイフン(-)、アンダースコア(_)の3つだそうですから、類似性チェックの際には、与えられたパッケージネームからこれらの文字種を取り除いた文字列で、既存のパッケージが存在していないか確認しているものと思われます。

ただ、npmリポジトリのIssueを見ていると、fix.jsが既存の(自分の)リポジトリであるfixと類似しててダメだった、みたいなコメントもありましたので、類似性チェック時は更に追加のルールが存在する可能性があります。

https://github.com/npm/npm/issues/19438

dry-runの落とし穴

また、厄介なのは、npm publish --dry-runでは類似性チェックが行われないことです。

公式ドキュメントを見ると、dry-runについて、下記のように書いてあります。

Indicates that you don't want npm to make any changes and that it should only report what it would have done. This can be passed into any of the commands that modify your local installation, eg, install, update, dedupe, uninstall, as well as pack and publish.

(AI訳)
npm に変更を加えさせず、「何を実行するつもりだったのか」だけを報告させたい場合に使用します。これは、install、update、dedupe、uninstall のようにローカルのインストールに変更を加えるコマンドだけでなく、pack や publish にも渡すことができます。

引用元:https://docs.npmjs.com/cli/v11/commands/npm-publish

なんとなく、実際にpublishしないから、npmに変更は加わらないけど、それ以外のことは一通りやって結果報告してくれる、っぽい感じに書いてあるように見えるんですが、実際には類似性チェックはやってくれないというのが実情です。

(余談ですが、npm publish --dry-checkについてGoogle検索すると、2017年に投稿されている、dry-checkは存在しないっていう記事がTOPにヒットします笑)

回避策と解決方法

1. スコープ付きパッケージ名を使う

npmが提案する最も確実な方法は、スコープ付きパッケージ名を使うことです。ただし、スコープ付きパッケージにするためには、access修飾子をpublicにする必要がある模様(無料ユーザの場合のみ?):

package.json:

{
  "name": "@sasazame/ccresume",  // スコープ付きパッケージ名
  "publishConfig": {
    "access": "public"  // 公開パッケージとして設定
  }
}

ccresumeでも、この方法を取りました。

覚えにくくなるデメリットはもちろんあるのですが、逆に言えば「誰のリポジトリのものか」が明確ですし、間違いも発生しにくいでしょうから、これはこれで悪くないと個人的には思います。

2. 完全に異なる名前を選ぶ

当たり前ですが、類似性チェックに引っかからないような、全く異なる名前を選ぶのも一つの方法ですね:

  • ccresumecc-conversation-resume
  • ccresumecc-resume-cli

既存のパッケージに近しい名前を使用することは、混乱を生む要員ともなり得ますので、極力独自性のある名前が良いでしょう。

ただ、変に名前が長くても覚えられないので、これまた悩ましいところです。

3. npmサポートへの問い合わせ

例えば、すでに存在するリポジトリが、明らかにあなたの商標を害しているとかいうときであれば、npmサポートに問い合わせすることで解決できるかもしれません。

ほとんどのケースでは何もしてくれないと思いますので、ごくごく限られたシチュエーションだと思います。

4. 公開名を譲ってもらう

これは最終手段とも言えますが、すでに公開済みのパッケージのオーナーに連絡して、そのパッケージ名を譲ってもらうという案も考えられます。

下記の記事に、実際にそうして譲ってもらったというお話が載っていました。ただし、このときにもトラブルがあったようで、危うく譲ってもらった名前が完全に使用不可能に陥りかけた模様。

実行するときは、注意を払う必要があると思われます。

https://xyflow.com/blog/reactflow-npm-package-name

まとめ

npmのパッケージ名の類似性チェックは、ユーザーの安全性などを保つためにも、開発者の独自性を保つためにも重要な機能です。

ただ、いざリリースするぞ、となったときに使いたかった名称が使えなくて困る、というシーンも現実として発生しうると思います。

公式の推奨する通り、スコープをつけた名称にするのか、あるいはまた新たな名称をつけるのか。

どの方法を選ぶかは状況次第ですが、慌てずに対処できるよう、今回のような事例を参考にしてもらえればと思います。

ちなみに、公式ドキュメントのパッケージ名ガイドラインを見ると

when choosing a name for an unscoped package, choose a name that:
Is not spelled in a similar way to another package name

https://docs.npmjs.com/package-name-guidelines

って書いてあるので、やっぱりnpm的にはもうスコープありのパッケージ名にしなよ、って感じなのかもしれません。

宣伝

はてなブログさんの方で、ささざめブログという雑記ブログを基本毎日更新しています。よかったら遊びに来てね。

Discussion