【モノレポ入門】Next.js + TypeScript + Nx で環境構築をする

# はじめに

###  この記事で書くこと

今回は、Next.js + TypeScript + Nx を使用して環境構築をする方法を書きます。

経緯としては、あるプロジェクトを作るにあたり、
- 汎用的な部分を使いまわせるようにしたい
- 以前作ったような物を再度作る際に、スピード感を持って開発できるようにしたい

といった意図から、フロントエンドの開発にモノレポを採用することになった為、
今回環境構築をして、学んだことをまとめようと思います。

## モノレポとは

### メリット
- **コードを共有できる**
プロジェクトを跨いでコードを共有できる為、コンポーネントやTypeScriptの型を共有して効率化できる。

- **ツールの標準化がしやすい**
tsconfigやeslintなどはルートディレクトリにあるbaseファイルを継承した形で作られるため、
プロジェクト毎の差異が出ずに標準化がしやすい

### デメリット

- 個人的に未知の領域だったので、学習コストがかかりそう

## Nxとは

https://nx.dev/

> Nx は、ファーストクラスの monorepo サポートと強力な統合を備えた、スマートで高速かつ拡張可能なビルド システムです。
> 引用元　https://nx.dev/getting-started/intro

### メリット

- **開発環境のセットアップが容易**
今回はNext.jsで環境を作成したのですが、コマンド一つで Next.js、TypeScript、ESLint、Prettier、SCSS、Jest、Cypress、などのツールが入ったプロジェクトが作成ができ、とても便利でした。



## 環境構築

まずはじめに、 下記のNxの公式ドキュメントのSetting up Next.jsという欄を進めていきます。

https://nx.dev/packages/next/documents/overview#setting-up-next.js



下記コマンドで、モノレポ環境のワークスペースと１つ目のアプリケーションの作成が開始します。
```shell
$ npx create-nx-workspace@latest --preset=next
```

ワークスペース名（リポジトリ名）を入力
```shell
>  NX   Let's create a new workspace [https://nx.dev/getting-started/intro]

? Repository name                       ›  sample
```

１つ目に作成する、アプリケーション名を入力
```shell
>  NX   Let's create a new workspace [https://nx.dev/getting-started/intro]

✔ Repository name                       · sample
? Application name                      ›　　admin                   ›  
```

使用するCSSを選択
```shell
>  NX   Let's create a new workspace [https://nx.dev/getting-started/intro]

✔ Repository name                       · sample
✔ Application name                      · admin
? Default stylesheet format             … 
CSS
SASS(.scss)       [ http://sass-lang.com   ]
LESS              [ http://lesscss.org     ]
Stylus(.styl)     [ http://stylus-lang.com ]
styled-components [ https://styled-components.com            ]
emotion           [ https://emotion.sh                       ]
styled-jsx        [ https://www.npmjs.com/package/styled-jsx ]             ›  
```

分散キャッシングを有効にしてCIを高速化するか？
※詳しくは[こちら](https://nx.dev/recipes/ci/ci-setup#distributed-ci-with-nx-cloud)
```shell
? Enable distributed caching to make your CI faster … 
Yes I want faster builds
No
```

作成完了
```shell
✔ Installing dependencies with npm
✔ Nx has successfully created the workspace: sample.
✔ NxCloud has been set up successfully

 >  NX   Successfully initialized git.
```


上記のコマンドで作成されたリポジトリ構成が下記のようになります。
```shell
sample
　　└ apps               - 各プロジェクトのアプリケーション
　　└ libs               - ライブラリ(共通の処理やコンポーネント、UIなど)
　　└ tools              - コードベースで動作するスクリプト
 - workspace.json     - ワークスペースの設定
 - nx.json            - プロジェクトの追加情報
 - tsconfig.base.json - 共通のTypeScriptの設定
```


下記の用に**admin**という名前で１つ目のアプリケーションが作成され、
自動でe2eテスト用のリポジトリも作成されました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475069/e94e34a5-f0e0-575b-2c4e-b5cb54fd2de8.png" width=50%>



## ワークスペースにアプリケーションを追加する

下記コマンドで、ワークスペース内にアプリケーションを追加します。
```shell
$ npx nx g @nrwl/next:app アプリ名

例) npx nx g @nrwl/next:app liff
```
例のコマンドをワークスペース内で実行すると、
下記の用に**liff**という名前でNext.jsで構築されたアプリケーションが追加されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475069/8e4814a2-c76a-5b9c-33d4-421c9a5e87d7.png" width=50%>

## ライブラリの作成

アプリケーション間での、共通の処理やコンポーネントを格納するリポジトリを作成できます。
```shell
$ npx nx g @nrwl/next:lib my-new-lib
```

## ページやコンポーネントの作成
#### ページの追加
**admin**リポジトリにログインページを追加してみます。
```shell
$ npx nx g @nrwl/next:page ページ名 --project=追加するアプリケーション名

例) npx nx g @nrwl/next:page login --project=admin
```

#### コンポーネントの作成
**admin**リポジトリにlogin-formを追加してみます。
```shell
$ npx nx g @nrwl/next:component コンポーネント名 --project=追加するアプリケーション名

例) npx nx g @nrwl/next:component login-form --project=admin
```

**admin**リポジトリに上記コマンドのみで、
テスト用のspacファイルを含む、loginページとlogin-formのコンポーネントの追加ができました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475069/b39a2311-1e11-2eea-e21f-6fc72cd2d47a.png" width=50%>

コンポーネントとページの作成に関しては、
コマンド一つで、関連するscssファイルから、テスト用のspecファイルまで作成され、
自動でルーティングも組まれるため、大変便利だと感じました。

## 開発サーバーを立ち上げる

下記のコマンドを実行して開発サーバー立ち上げを実行

```shell
$ npx nx serve 立ち上げるアプリケーション名

例) npx nx serve admin
```
ローカルサーバが立ち上がり、下記のような初期ページが表示されました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475069/ab517fc4-515a-417e-adbd-b3c5f1b4a5ee.png" width=100%>

試しに/loginにアクセスしてみると、
やはり自動でルーティングが設定されており、コマンドで作成したログインページを開くことができました。
![スクリーンショット 2023-01-24 1.13.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475069/9593c4f4-a661-4fca-a7a7-43edbfe4192f.png)

## 汎用ライブラリを両方のアプリケーションから呼び出してみる

試しにcommon-headerという汎用ライブラリを作成し([ライブラリの作成](#ライブラリの作成))、
**admin**リポジトリと、**liff**リポジトリからコンポーネントを呼び出してみます。

下記のコマンドを実行して汎用ライブラリを作成

```shell
$ nx g @nrwl/next:lib common-header
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475069/a2fa0eb5-f5b2-97ae-1775-698f3c5747b4.png" width=50%>

ライブラリの作成コマンドで作成したライブラリの中身
```jsx:common-header.jsx
import styles from './common-header.module.scss';

export interface CommonHeaderProps {}

export function CommonHeader(props: CommonHeaderProps) {
  return (
    <div className={styles['container']}>
      <h1>ここは汎用ヘッダーです！</h1>
    </div>
  );
}

export default CommonHeader;

```

adminのログインページ

```jsx:admin/pages/login
import styles from './index.module.scss';
/* import { CommonHeader } from '@<作成したworkspace名>/common-header';*/
import { CommonHeader } from '@sample/common-header';

export interface LoginProps {}

export function Login(props: LoginProps) {
  return (
    <div className={styles['container']}>
      <CommonHeader></CommonHeader>
      <h1>Welcome to Login!</h1>
    </div>
  );
}

export default Login;
```
![スクリーンショット 2023-01-24 1.37.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475069/83db4deb-3746-d52d-50dc-429e581c49cc.png)

liffリポジトリのliff-mypage
```jsx:sample/liff/liff-mypage
import styles from './index.module.scss';
import { CommonHeader } from '@sample/common-header';

/* eslint-disable-next-line */
export interface LiffMypageProps {}

export function LiffMypage(props: LiffMypageProps) {
  return (
    <div className={styles['container']}>
      <CommonHeader></CommonHeader>
      <h1>Welcome to LiffMypage!</h1>
    </div>
  );
}

export default LiffMypage;
```
![スクリーンショット 2023-01-24 1.37.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475069/551ab77c-30cf-7616-fbac-d94528045a44.png)


両方のアプリケーションから汎用コンポーネントを呼び出す事ができました！


## まとめ

使いこなせば便利な機能がたくさんありそうなので、
公式ドキュメントにある下記のようなチュートリアルを進めてみて、
使いこなせるようにいろいろ触ってみようと思います。

> 引用
> その他のドキュメント
> Next.js と Nx の詳細を学ぶのに役立つその他のリソースを次に示します。
> ブログ投稿: Juri Strumpflohner によるNext.js と Nx シリーズを使用したブログの構築

https://blog.nrwl.io/create-a-next-js-web-app-with-nx-bcf2ab54613

> ビデオチュートリアル: Typescript NX Monorepo with NextJS and Express by Jack Herrington

https://www.youtube.com/watch?v=WOfL5q2HznI

Visual Studio CodeにNxに関するプラグインもあるみたい

https://nx.dev/core-features/integrate-with-editors#vscode

## 少し詰まったポイント
初めてnxコマンドを叩こうとして下記のエラーに遭遇

```shell
nx: command not found error
```
下記記事のnpxを付ける方法で解決

https://bobbyhadz.com/blog/npm-command-not-found-nx