LoginSignup
11
4

More than 3 years have passed since last update.

開発中のnpmパッケージをローカルで参照する際に気をつけること

Last updated at Posted at 2020-08-12

npmパッケージを開発中、デモページも同じリポジトリで開発してたら思わぬところでハマったので経緯と解決法を記録しておく。

経緯

ReactでシンプルなJSONエディタがほしくて作って

次回別のコンポーネントを作って公開する際にやることを最小化できるように、boilerplateとして切り出してまとめて

せっかくなのでreact-plain-json-editorもこのboilerplateにしたがって編集しなおしたところ、デモページが動かなくなったので

修正した!

現象

デモページを開くとブラウザで下記のエラーが出現した。
(非常に丁寧なエラーメッセージですね)

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
    at resolveDispatcher (react.development.js:1465)
    at Object.useState (react.development.js:1496)
    at ../dist/PlainJsonEditor.js.exports.PlainJsonEditor (PlainJsonEditor.js:38)
    at renderWithHooks (react-dom.development.js:14803)
    at mountIndeterminateComponent (react-dom.development.js:17482)
    at beginWork (react-dom.development.js:18596)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at beginWork$1 (react-dom.development.js:23203)

よくある原因として挙げてくれている3点のうち、1,2は大丈夫そうだったので、3が怪しい。

3. You might have more than one copy of React in the same app
(意訳)Reactが複数含まれているかもしれません

ざっくり現象の概要をまとめてしまうと、「ライブラリ本体とデモページとで別々のReactを参照しているので不整合が起きている」ということになるかと思う。

とりあえず結論だけ知りたい方はこちらへ。

ちなみに、現象としてはこちらのページに書かれているものとおそらく同じだが、少し違う対処をした部分もあるのでこちらはこちらで記しておく。
Reactコンポーネントをnpmパッケージとして開発する

状況の整理

  • ライブラリ本体の開発
    • 開発フォルダをルートディレクトリ(/)と呼ぶことにする
    • ビルドツールはtsc
    • ビルドに関連するファイルはpackage.json
  • デモページの開発
    • 開発フォルダはdemoディレクトリ
    • ビルドツールはwebpack
    • ビルドに関連するファイルはdemo/package.jsondemo/webpack.config.js

問題と対処

問題1: ライブラリ本体のdependenciesにreactが入っている

これはそもそもよくない。普通は

  • 動作に必要なパッケージ => dependencies
  • 開発に必要なパッケージ => devDependencies

だが、パッケージとして公開することが前提であれば、使用側との競合を避けるため

  • 動作に必要なパッケージ => peerDependencies
  • 開発に必要なパッケージ => devDependencies

とするべきだ。
ただし、これだけではpeerDependenciesyarn install(またはnpm install)に無視されるので、開発時にビルドできなくなる。

問題1への対処

  • 動作に必要なパッケージ => peerDependenciesdevDependenciesの両方
  • 開発に必要なパッケージ => devDependencies

とするのが良さそうだ。

問題2: node_modulesdemo/node_modulesの両方にreactが含まれている

問題1に対処しても、まだreactの競合は避けられていないので、
どちらかだけに含むようにする(またはどちらかだけを参照するようにする)必要がある。

修正前

demo/package.json(修正前/一部抜粋)
"dependencies": {
    "react": "^16.13.1"
}
/demo/src/component/App.tsx(修正前/一部抜粋)
// ルートディレクトリを直接参照
import { PlainJsonEditor } from '../../../'
demo/webpack.config.js(修正前/一部抜粋)
// 実際にはこれは書いていなかったが、説明のためにデフォルト値で記載。
resolve: {
    modules: ['node_modules']
}

これらの指定により、デモページ側ではdemo/node_modulesにあるreactが参照されてしまう。

修正方針

実際にパッケージが使用される環境を想定すると、reactはライブラリ側ではなくdemo側にインストールされる方が正しくはあるが、開発環境が不便になるので、ライブラリ側だけにインストールし、demo側からルートのnode_modulesを参照できるようにする。

問題2への対処

yarn linkまたはnpm linkを使っておいてから"react-plain-json-editor": "latest"などを指定しても良いが、誰か別の人が使うときに管理する状態を極力増やさないようにしたいので今回は"link:../"を使った。

demo/package.json(修正後/一部抜粋)
"dependencies": {
    "react-plain-json-editor": "link:../"
}
demo/src/components/App.tsx(修正後/一部抜粋)
// dependenciesでlinkしたので、通常のパッケージをimportする時と同じ記法で使用可能
import { PlainJsonEditor } from 'react-plain-json-editor'
demo/webpack.config.js(修正後/一部抜粋)
resolve: {
    // ルート側を優先して参照するようにする
    modules: ['../node_modules', 'node_modules']
}

ちなみにこのdemo/webpack.config.jsonの修正後も、demo/node_modulesreactが含まれていると同様のエラーが起きた。
ここはwebpackの仕様の話になるのだろうが、これ以上は調べていない。

問題3: package.jsonにバージョン指定がない

※今回の問題とは直接の関係はない

パッケージのリリースはGitHub Actionsに登録したsemantic-releaseに任せており、バージョン番号はsemantic-releaseが管理するので、package.jsonにバージョンを書くと冗長だったり公開バージョンとの間に見た目上の不整合が生じたりすると思って、versionの指定自体を削除していた。

するとdemoのyarn install時に下記エラーが発生した。

error Can't add "react-plain-json-editor": invalid package version "".

問題3への対処

package.json"version": "0.0.0"を追加


以上で問題なくデモページが動くようになった。

成果物はこちらです。
react-plain-json-editor
dev-npm-package-react-typescript-boilerplate

まとめ

react(及びライブラリ本体が他に依存するパッケージ)はpackege.jsonpeerDependenciesdevDependenciesの両方に記載し、dependenciesには記載しない。

  • peerDependencies: このパッケージをinstallする際に必要に応じてWarningを出す
  • devDependencies: 開発時のためにnode_modulesにインストールされる

demo/node_modulesreactを含めない

  • demo/package.jsondependenciesreactを書かない

デモページのビルド時にルートのnode_modulesを参照するようにする

  • demo/webpack.config.jsresolve.modules['../node_modules', 'node_modules']を指定する
    • ../node_modules: ルートディレクトリ側のnode_modulesのこと
    • node_modules: デモページ側のdemo/node_modulesのこと

デモページから開発中のライブラリを使用できるようにする

  • demo/package.jsondependencies"react-plain-json-editor": "link:../"を記載
  • またはyarn linkを設定後"react-plain-json-editor": "latest"を記載
ex)yarn/linkを使う方法
# まずはルートディレクトリで
yarn link # このパッケージをlinkコマンドで参照可能にする(`~/.config/yarn/link`にシンボリックリンクが貼られる)
cd demo # demoへ移動
yarn link react-plain-json-editor # linkで登録したパッケージをこのプロジェクト(demo)で使えるようにする

参考にしたページ

Reactコンポーネントをnpmパッケージとして開発する
Yarnでローカルのパッケージをaddする方法
npm linkの基本的な使い方まとめ
0000-link-dependency-type.md
依存関係の種類
Webpackを使う時は、resolve.modulesの設定に気を付けよう。(特にModule not found: Error: Can't resolve~が表示された時)
Resolve

11
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4