タイムリーに is-promise 2.2.0 破損に巻き込まれた話


Node.jsの環境をSSDに引越しして動作を確認していたところ、yoがインストールできない・動かないという謎の現象に見舞われた。実は自分の環境が悪いのではなく、タイムリーにyoが依存するたった1行の関数を提供するis-promiseが壊れた直後にインストールしたことが原因と分かった。

別のモジュールではNode.jsのバージョンが上がって動かなくなるケースでは対応が遅れることがあったので、今回はすぐ処置されてラッキーだった。(知らないだけで日常的に起きているのかもしれないが)

起きた現象

yoをインストールすると、ちゃんと動くかをチェックするYeoman Doctorが実行される。ここで、yo --versionでエラーが起きてしまっている。この後yoを実行しても、同じエラーで起動すらしない状況となった。

> [email protected] postinstall K:\nodejs\npm_global\node_modules\yo
> yodoctor


Yeoman Doctor
Running sanity checks on your system

√ No .bowerrc file in home directory
√ Global configuration file is valid
√ NODE_PATH matches the npm root
√ No .yo-rc.json file in home directory
√ Node.js version
Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "yo "--version""
internal/modules/cjs/loader.js:584
            if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw e;
                                                            ^

Error [ERR_INVALID_PACKAGE_TARGET]: Invalid "exports" main target "index.js" defined in the package config /K:/nodejs/npm_global/node_modules/yo/node_modules/is-promise\package.json
    at resolveExportsTarget (internal/modules/cjs/loader.js:542:13)
    at resolveExportsTarget (internal/modules/cjs/loader.js:581:20)
    at applyExports (internal/modules/cjs/loader.js:455:14)
    at resolveExports (internal/modules/cjs/loader.js:508:23)
    at Function.Module._findPath (internal/modules/cjs/loader.js:632:31)
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:1001:27)
    at Function.Module._load (internal/modules/cjs/loader.js:884:27)
    at Module.require (internal/modules/cjs/loader.js:1074:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Object.<anonymous> (K:\nodejs\npm_global\node_modules\yo\node_modules\run-async\index.js:3:17) {
  code: 'ERR_INVALID_PACKAGE_TARGET'
}

エラーの原因

エラーメッセージにある通り、run-asyncが利用するis-promiseで、ERR_INVALID_PACKAGE_TARGETが起きてしまっているのが原因。npmのis-promiseのページを見てみると、以下のようにビルドエラーが起きたと表示されている。30分ほど前にPublishされたバージョンで何が起きたのか…

is-promiseのGitHubにはIssueが既に投稿されていて、同じエラーが起きていることが分かった。(ここで初めて、自分の環境が問題ではないかも…と分かった)

原因はPackage.json incorrectly formatted #12に投稿されていた(ejs/cjsといった、Javascriptのモジュールの形式の指定が間違っていたとのこと…あまり理解できていないが、ejsをサポートするNode.jsのバージョンでNGになるそう)。今回のバージョンで追加された機能が悪さをしてしまった模様。

その後、[BUGFIX] Use correct paths for CJS and ESM compatibility #15で修正があり、プルリクエストが発行されていた。

とりあえず、少し待てば反映されるかもしれないので待ってみる。Weekly download 1000万超の定番モジュールがほんの少しのミスで色々なモジュールに影響を与えてしまうとは…

暫定対策

とりあえず問題が起きているのはyoのグローバルインストールが使っているrun-asyncなので、yoのnode_modules以下のrun-asyncpackage.jsonを変更して、is-promiseのバージョンを2.1.0に固定する。package.jsonを変更したらrun-asyncのフォルダでnpm installを実行し、is-promiseの2.1.0をインストールする。

package.json
  "dependencies": {
    "is-promise": "2.1.0"  元々は "^2.1.0" だった
  },

この処置の後、yoを実行したら動くようになった。

>yo
? 'Allo! What would you like to do? (Use arrow keys)
  Run a generator
> Code
  ──────────────
  Update your generators
  Install a generator
  Find some help
  Get me out of here!
(Move up and down to reveal more choices)

その後

2.2.0から3時間後に2.2.2がリリースされた。もう一度npm install -g yoしてみたところ、正常にインストールできた。早く対応してくれて良かった…

Yeoman Doctor
Running sanity checks on your system

√ No .bowerrc file in home directory
√ Global configuration file is valid
√ NODE_PATH matches the npm root
√ No .yo-rc.json file in home directory
√ Node.js version
√ npm version
√ yo version

Everything looks all right!