Menu
Appearance
TurborepoではじめるReact開発's eyecatch

TurborepoではじめるReact開発

React

React-Native

Turborepo

2022.11.01

Turborepo とは

Vercel による 1 つのコードベースで複数のアプリやパッケージを管理する monorepo 環境のためのビルドツールで、ワークスペースという手法で個々にパッケージを依存させたり相互依存させます。

特徴として、タスクの結果とログをキャッシュすることで遅いタスクの速度を向上させることができます。例えばビルドコマンドを叩いても、変更を加えていない箇所のビルドはスキップされるといったことです。

monorepo プロジェクトの作成

npx create-turbo@latest

このコマンドを叩くだけで monorepo プロジェクトの作成が可能です。プロジェクト名は途中で聞かれるのでそこで入力します。 既存のプロジェクトに導入したい場合は、こちらを参照してください。

プロジェクトを作成するとこのようなディレクトリ構成になっているかと思います。(プロジェクト作成後のターミナルでも説明されています。)

- apps
  - docs // Next.jsプロジェクト
  - web // Next.jsプロジェクト
- packages
  - eslint-config-custom // 共通のESLint設定
  - tsconfig // 共通のtsconfig
  - ui // Reactコンポーネントライブラリ

それぞれの package.json の名前をもとに確認していくと、どのパッケージに依存しているのかわかります。

// /apps/web/package.json
"dependencies": {
  "next": "13.0.0",
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
  "ui": "*" // ここ
},
"devDependencies": {
  "@babel/core": "^7.0.0",
  "eslint": "7.32.0",
  "eslint-config-custom": "*", // ここ
  "tsconfig": "*", // ここ
  "@types/node": "^17.0.12",
  "@types/react": "^18.0.22",
  "@types/react-dom": "^18.0.7",
  "typescript": "^4.5.3"
}
  • docs => ui / eslint-config-custom / tsconfig に依存
  • web => ui / eslint-config-custom / tsconfig に依存
  • ui => eslint-config-custom / tsconfig に依存

今回は apps 内に reactreact-native プロジェクトを加えていきます。 他のワークスペースを指定することも可能ですが、公式ドキュメントにおいて apps packages を持つことを推奨されているため、これに従います。

他のワークスペースを作成して、そこに新規プロジェクトを作成したい場合はルートの package.jsonworkspace 設定を変更する必要があります。

"workspaces": [
  "apps/*",
  "packages/*"
],

React プロジェクトを導入

まず React プロジェクトの作成から行います。 ビルドツールには vite を使用しています。 create-react-app でも問題ありません。

npm create vite@latest --workspace=app

上記コマンドで apps フォルダ内に新しくプロジェクトを作成することができます。React の選択はターミナル内で選択することになります。 またパッケージ名はなんでも構いませんが、今回は web_react としています。

apps 内にて npm create vite@latest を実行しても同様に可能ですがお勧めはできません。 ワークスペースで管理している以上は不要でありますし、ルートディレクトリからパッケージのインストールを実行せずに個々のプロジェクト内でコマンドを叩くといったやり方だと、 思わぬプロジェクトで入れるつもりのないパッケージをインストールしていたなんでことが起こる可能性があるからです。

web_react に必要なパッケージをインストールします。 --workspace= をつけることで各ワークスペースのみにパッケージのインストールを実行するようにします。

npm install --workspace=web_react

次は作成できた React プロジェクトの package.jsonui その他の共通ワークスペースの加えます。

// /apps/react/package.json
"dependencies": {
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
  "ui": "*"
},
"devDependencies": {
  "@types/react": "^18.0.22",
  "@types/react-dom": "^18.0.7",
  "@vitejs/plugin-react": "^2.2.0",
  "eslint-config-custom": "*",
  "tsconfig": "*",
  "typescript": "^4.6.4",
  "vite": "^3.2.0"
}

このまま React プロジェクトをブラウザで立ち上げたいところですが、このまま npm run dev を実行すると webweb_react の 2 つのプロジェクトが立ち上がってしまうため、ルートの package.json にそれぞれのワークスペースを立ち上げれるようにスクリプトを加えます。

// /package.json
"scripts": {
  "build": "turbo run build",
  "dev": "turbo run dev --parallel", // parrallelで並行実行す流ようになります
  "dev:web_next": "turbo run dev --filter=web",
  "dev:web_react": "turbo run dev --filter=web_react",
  "lint": "turbo run lint",
  "format": "prettier --write \"**/*.{ts,tsx,md}\""
},

filter を使用することで各ワークスペースに対してのスクリプトの実行が可能になります。

  • "dev:web_next" で web のワークスペース
  • "dev:web_react" で web_react のワークスペース

に対して dev を実行するようにしています。 スクリプト実行時の turbo run <task>turbo.jsontask を実行することができます。

ここまでできたら、 npm run dev:web_react をすることで web_react プロジェクトのみを立ち上げることができるはずです。 また、 ui パッケージの Button コンポーネントの使用も可能になっているかと思います。

番外編 ~ create-react-app で作ってみる

create-react-app を利用して web_cra というプロジェクトを作成します。

npm init react-app web_cra --workspace=apps だとローカルホスト立ち上げで失敗してしまうため、やむなく apps ワークスペースからプロジェクト作成を行いました。

npx create-react-app web_cra --template typescript // apps ワークスペース内から

web_craui 等のパッケージを追加して、ルートの package.json に立ち上げ用のスクリプトの追加をします。

// /apps/web_cra/package.json
"dependencies": {
  // ~~略
  "ui": "*"
},
"devDependencies": {
  "eslint-config-custom": "*",
  "tsconfig": "*"
}
// /package.json
"scripts":{
  "dev:web_next": "turbo run dev --filter=web",
  "dev:web_cra": "turbo run start --filter=web_cra",
}

crate-react-app の立ち上げスクリプトが start のため、合わせる必要があり turbo.json にも start タスクを加えなければいけません。

// /turbo.json
{
  {
    // ~~ 略
    "dev": {
      "cache": false
    },
    "start": {
      "cache": false
    }
  }
}

さらに ui パッケージから Button コンポーネントを使用したいのですが、そうするとエラーが生じます。 これは crate-react-app では実行時に TypeScript のトランスパイルを行っていないからだと思います。

では、なぜ Next.js と Vite ではエラーが生じなかったかですが、Next.js では next.config.jstranspilePackages: ["ui"]を追加することでトランスパイルを実行しており、Vite では標準でトランスパイルを行っているからだと考えられます。

// next.config.js
experimental: {
  transpilePackages: ["ui"],
},

ただ、この next.config.js における設定ですが最近(執筆日時 2022/11/1)加えられたようで、それ以前は next-transpile-modules を使用してトランスパイルがされていました。

詳細はそれぞれ下記リンクを一読するといいかもしれません。

そのため、 create-react-app を使用する場合は別でトランスパイルをする必要があります。

今回は tsup というライブラリを使用して、 ui パッケージをトランスパイルします。

こちらの記事を参考にしました。
npm install tsup --workspace=ui

tsup をインストールしたら、uisrc フォルダを作成して、そこにコンポーネントをまとめる形にします。

- ui
  - src
    - Button.tsx
    - index.tsx

さらに package.json を書き換えます。

// /ui/package.json

"name": "ui",
"version": "0.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"scripts": {
  "build": "tsup src/index.tsx --format esm,cjs --dts",
  "dev": "tsup src/index.tsx --format esm,cjs --watch --dts",
  "lint": "TIMING=1 eslint \"**/*.ts*\""
}

こうすることでビルド後に dist フォルダが作成され、ビルドされたファイルを利用できるようになります。

ただ、ビルドと開発中のコンポーネントの変更が即時反映されるようにルートの package.json にもスクリプトを追加しなければなりません。

// /package.json

"scripts": {
  "build": "turbo run build",
  "build:ui": "turbo run build --filter=ui", // uiのビルド
  "dev": "turbo run dev --parallel",
  "dev:web_next": "turbo run dev --filter=web",
  "dev:ui": "turbo run dev --filter=ui", // uiの変更を検知
  "dev:web_cra": "turbo run start --filter=web_cra",
  "lint": "turbo run lint",
  "format": "prettier --write \"**/*.{ts,tsx,md}\""
},

これで web_cra でもエラーが起きることなく、ローカルホストを立ち上げることに成功したのではないかと思います。

しかし、必要のない設定やパッケージを追加する必要が生じてしまうため、やむを得ない事情がない限りでは Vite を使用することをお勧めします。

SHARE