npm workspace, Create React Appでmonorepo作成してたら詰まった話
はじめに
Create React Appで作成したアプリケーションでmonorepo構成を扱いたかったため、npm workspaceでmonorepoの作成をしていましたが、それプラスtranspileにCRACOを使用する必要があったのでその備忘録となります。
ちなみにですが、Viteで同様にmonorepo作成を行った場合は本章のclient_aからcommon_packageを呼び出してみるでエラーにならずコンポーネントが表示されるので、さっさとViteに移行しましょう。
構成
今回は以下のように独立して動くアプリケーションclient_a
, client_b
と単体では動かず共通のライブラリとなるcommon_package
の構成で作成していきます。
client_a
, client_b
はCreate React App
で作成されたアプリケーションです。
.
├── package.json
├── client_a
│ └── package.json
├── client_b
│ └── package.json
└── common_package
└── package.json
workspaceの作成
既にディレクトリの中身は作成済みだったためnpm init -y
でルートのpackage.jsonを作成し、以下の設定を追加。
"workspaces": [
"client_a",
"client_b",
"common_package"
]
その後ルートでnpm install
を実行するとルートのnode_modulesにワークスペースのシンボリックリンクができます。
.
├── node_modules
│ ├── client_a -> ../client_a
│ ├── client_b -> ../client_b
│ └── common_package -> ../common_package
├── package-lock.json
├── package.json
├── client_a
│ └── package.json
├── client_a
│ └── package.json
└── common_package
└── package.json
ルートにてclient_a
にcommpn_package
をinstall
npm install common_package -w client_a
これにて準備は完了です。
client_aからcommon_packageを呼び出してみる
import { HelloWorld } from "common_package/src/HelloWorld"
function App() {
return <HelloWorld />;
}
export default App
export function HelloWorld() {
return <div>Hello World</div>;
}
client_aでnpm run start
を実行すると、次のようなエラーが。
setPrototypeOf.js:5 Uncaught Error: Module build failed (from ../node_modules/babel-loader/lib/index.js):
SyntaxError: /common_package/src/HelloWorld.js: Support for the experimental syntax 'jsx' isn't currently enabled (2:10): (at setPrototypeOf.js:5:1)
[0m [90m 1 |[39m [36mexport[39m [36mfunction[39m [33mHelloWorld[39m() {
[31m[1m>[22m[39m[90m 2 |[39m [36mreturn[39m [33m<[39m[33mbutton[39m[33m>[39m[33mHello[39m [33mWorld[39m[33m<[39m[33m/[39m[33mbutton[39m[33m>[39m[33m;[39m
[90m |[39m [31m[1m^[22m[39m
[90m 3 |[39m }
[90m 4 |[39m[0m
Add @babel/preset-react (https://github.com/babel/babel/tree/main/packages/babel-preset-react) to the 'presets' section of your Babel config to enable transformation.
If you want to leave it as-is, add @babel/plugin-syntax-jsx (https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-jsx) to the 'plugins' section to enable parsing.
If you already added the plugin for this syntax to your config, it's possible that your config isn't being loaded.
You can re-run Babel with the BABEL_SHOW_CONFIG_FOR environment variable to show the loaded configuration:
npx cross-env BABEL_SHOW_CONFIG_FOR=common_package/src/HelloWorld.js <your build command>
どうやらJSXのトランスパイルをしてあげる必要があるっぽい。
CRACOでtranspile
トランスパイル(transpile)はあるプログラミング言語を別の言語に変換することで、今回の場合は、JSXをJSに変換してあげる必要がある。
そこでCRACO(Create React App Configuration Override) の出番です。
CRACOは、Create React App (CRA) の設定をejectせずにカスタマイズするためのツールです。
ESLint、Babel、PostCSS などの構成をカスタマイズに加え、他のパッケージのトランスパイル設定やエイリアス設定などを行うことができます。
client_aにCRACOをインストール
npm i @craco/craco craco-babel-loader --save-dev
package.jsonの設定を変更
"scripts": {
- "start": "react-scripts start"
+ "start": "craco start"
- "build": "react-scripts build"
+ "build": "craco build"
}
craco.config.jsを作成。craco-babel-loaderを用いてトランスパイルします。
const fs = require("fs");
const path = require("path");
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
module.exports = {
plugins: [
{
plugin: require("craco-babel-loader"),
options: {
// 相対パスを指定
includes: [resolveApp("../common_package/")],
},
},
],
};
さて、再度npm run start
で実行してみましょう。
ちゃんとHello Worldが表示されましたね。
これにて終了です。
Discussion