仕事でサンプルコードを書いていて、2つのReactアプリのプロジェクトでコードを共有したくなりました。
あれ、どうするのが良いのかな? と思いながらいろいろと試してみました。
DALL·Eで生成したlocal npm packageの画像
今回のコード
a-project
とb-project
というReactアプリのプロジェクトが以下のようなディレクトリー構成で作られています(node_modules/ディレクトリーは省略しています)。
.
├── a-project
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── message.ts
│ │ └── index.tsx
│ └── tsconfig.json
└── b-project
├── package-lock.json
├── package.json
├── public
│ └── index.html
├── src
│ ├── App.tsx
│ └── index.tsx
└── tsconfig.json
ソースコードは以下のようになっています。
- a-project/src/App.tsx
import message from './message';
const App = () => {
return (
<h2>{message()}</h2>
)
}
export default App;
- a-project/src/message.ts
const message = () => {
return "Hello React!"
}
export default message;
import from ’../../a-project/src/message’ はエラー
さてb-project
のApp.tsx
でもa-project/src/message.ts
を使いたいので、以下のように書いてみました。
- a-project/src/App.tsx
import message from '../../a-project/src/message';
const App = () => {
return (
<h2>{message()}</h2>
)
}
export default App;
VS Code上でのエラーは検出されませんでしたが、npm start
で実行すると黒い画面に以下のようなエラーが表示されました。😅
Compiled with problems:X
ERROR in ./src/App.tsx 4:0-50
Module not found: Error: You attempted to import ../../a-project/src/message which falls outside of the project src/ directory. Relative imports outside of src/ are not supported. You can either move it inside src/, or add a symlink to it from project’s node_modules/.
確かにsrc/ディレクトリー以外のファイルが参照されているのは怪しいですよね・・・・
シンボリック・リンクはWindowsでは危うい
それなら、シンボリック・リンクを使ってらどうでしょうか?
$ cd b-project
$ ls -l src/*
-rw-r--r-- 1 yy staff 118 2 6 11:11 src/App.tsx
-rw-r--r-- 1 yy staff 251 2 6 11:00 src/index.tsx
lrwxr-xr-x 1 yy staff 30 2 6 11:12 src/message.ts -> ../../a-project/src/message.ts
-rw-r--r-- 1 yy staff 40 2 6 10:45 src/react-app-env.d.ts
これは動作しました! gitにもコミット出来ました。
しかし、調べてみるとWindowsではGit for Windowsでシンボリックリンクを扱えるようにするのような設定を行わないと、正しくシンボリックリンクが扱われないようです。
local npm package
少し調べたところ、npmパッケージ(npmライブラリー)をローカルに作り、それをnpm install <npmパッケージのディレクトリー>
でプロジェクトにインストールする事ができるそうです。
1. 準備
さっそく、messageパッケージを作っていきましょう、もちろんTypeScriptで書きます。
$ mkdir message ← messageパッケージ用ディレクトリーの作成
$ cd message
$ npm init -y ← npm パッケージの作成
$ npm install typescript ← TypeScriptをインストール
$ npx tsc --init ← TypeScriptの設定ファイルを作成
2. ソースコード
このパッケージの主体であるmessage.ts
ファイルを index.ts
として移動
$ mv ../a-project/src/message.ts index.ts
3. packege.jsonを変更
以下の部分を変更しました。
- "main": "index.js",
+ "main": "build/index.js", // ← 作成されるJSファイルを指定 (変更)
+ "types": "build/index.d.ts", // ← TypeScriptの定義ファイルを指定(追加)
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "build": "tsc" // ← ビルドコマンドの定義
},
4. tsconfig.jsonを変更
以下の部分を変更しました。
- // "declaration": true,
+ "declaration": true, // ← TypeScript用定義ファイルの作成
- // "outDir": "./",
+ "outDir": "./build", // ← コンパイルされたJavaScriptファイルの入るディレクトリー
5. ビルド
$ npm run build
> message@1.0.0 build
> tsc
$
- 参照: ここでのディレクトリー構成(node_modules/ディレクトリーは省略しています)
.
├── a-project
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ └── index.tsx
│ └── tsconfig.json
├── b-project
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ └── index.tsx
│ └── tsconfig.json
└── message
├── build
│ ├── index.d.ts
│ └── index.js
├── index.ts
├── package-lock.json
├── package.json
└── tsconfig.json
6. Reactプロジェクトの変更
Reactプロジェクトにmessageパッケージをインストールし、コードを少し変更しました。
$ cd ../b-project
$ npm install ../message/ ← messageパッケージのインストール
- b-project/src/App.tsx
import message from 'message'; // ← node_modulesからの参照
const App = () => {
return (
<h2>{message()}</h2>
)
}
export default App;
- 参考: package.jsonは以下のようになっていました
{
"name": "b-project",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.12",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"message": "file:../message", // ← ローカル・パッケージ
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
// ・・・ 省略 ・・・
}
まとめ
このように、簡単にlocal npm packageを使い別プロジェクト間でのコード共有が出来るようになります。
また、npm install
コマンドはnpm install <git:// url>
や npm install <github username>/<github project>
のように社内のGitリポジトリーやプライベートなGitHubからもインストールできるので、社内特有のReact(JavaScript)コード等を共有するのに役立つと思います。
1点注意してほしいのは、npm uninstall
は
$ npm uninstall ../message
上のようにパス名ではでなく、パッケージ名を指定しないと行けないことです。
$ npm uninstall message
パス名を指定してもエラーは表示されないので、ハマりました。😅