Next.js + TypeScript + linaria な環境を構築する

# はじめに

個人開発で React から Next.js へ移行しようと考えたため、その際の構築手順をまとめます。

linaria on Next.js には 今回は [next-linaria](https://github.com/Mistereo/next-linaria) を使用しました。
README.md には以下の説明文があります。

> Next.jsの組み込みCSSサポートにLinariaを追加します。
> 
> バージョン1.0.0以降はlinaria@3でのみ動作します。
> linaria@2のサポートが必要な場合は、本パッケージのバージョン0.11.0をご利用ください。
> 本パッケージのバージョン0.11.0は、next@9.5.4 以降を必要とします。
> 
> next@9.2.0-9.5.3の場合、本パッケージのバージョン0.10.0をご利用ください。
> 
> このモジュールはNext.jsのビルトインCSSサポートと併用することを想定しており、おそらくカスタムCSSの処理では動作しないでしょう。

linaria@3 は現在ベータ版のため、next-linaria の対応状況は公式の README.md を参照するようにしてください。

本記事では最初にクイックスタートとして必要な手順のみを示します。
その後に筆者が実際に試した手順を示します。
後者の手順はエラーメッセージからこの記事へたどり着けること（&エラーを解決できること）を目的としています。

コードは　[GitHubリポジトリ](https://github.com/HirotoOhria/next-js-demo/tree/main/linaria) にあります。

# 環境

各パッケージのバージョンと最終的な `package.json` を先に提示します。

### バージョン

| パッケージ | バージョン |
| ---- | ----- |
| next | 12.1.5　|
| react | 18.0.0 |
| typescript | 4.6.3 |
| linaria | 3.0.0-beta.18 |

linaria は version 3 を使用していますが、2022年4月時点では beta 版となっています。
安定版になるにあたり挙動が変更される可能性があるため、1つの参考としてください。

### package.json

```json
{
  "name": "linaria-on-next-demo",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "linaria": "3.0.0-beta.18",
    "next": "12.1.5",
    "next-linaria": "1.0.0-beta",
    "react": "18.0.0",
    "react-dom": "18.0.0"
  },
  "devDependencies": {
    "@babel/core": "^7.17.9",
    "@linaria/webpack-loader": "^3.0.0-beta.16",
    "@types/node": "17.0.25",
    "@types/react": "18.0.6",
    "@types/react-dom": "18.0.2",
    "eslint": "8.14.0",
    "eslint-config-next": "12.1.5",
    "typescript": "4.6.3"
  }
}
```

# クイックスタート

必要な手順のみを示したクイックスタートを記述します。

### Next.js の構築

create-next-app を使用して next アプリケーションを構築します。

```
$ npx create-next-app@latest --typescript
```

### パッケージのインストール

まず linaria 関係のパッケージを追加します。
ここでは beta 版の linaria@3 を使用していますが、ユースケースに合わせて変更してください。
next-linaria のバージョンは [公式の README.md](https://github.com/Mistereo/next-linaria) を参考にしてください。

```
$ yarn add linaria@3.0.0-beta.18 next-linaria@1.0.0-beta
```

次に webpack と babel 関係のパッケージをインストールします。

```
$ yarn add -D @linaria/webpack-loader @babel/core 
```

### Next.js の設定

`next.config.js` を `next-linaria` を使用するように修正します。

```javascript:next.config.js
/** @type {import('next').NextConfig} */
const withLinaria = require('next-linaria')

const nextConfig = {
  reactStrictMode: true,
}

const linariaOption = {}

module.exports = withLinaria({
  ...nextConfig,
  linaria: linariaOption,
})
```

### Babel の設定

最後に `.babelrc` に設定を追加すれば終了です。

```json:.babelrc
{
  "presets": ["next/babel", "linaria/babel"]
}
```

これで linaria によるスタイリングが適用できるようになります。

# 実際に行った手順

筆者が実際に行った手順を記述します。
暇なときの読み物として呼んでください。

## Next.js の構築

### create-next-app

[Next.js の公式ドキュメント](https://nextjs.org/docs/getting-started) を参考に作成していきます。
今回は `create-next-app` を使用してサクっと作成します。

```shell
$ npx create-next-app@latest --typescript
✔ What is your project named? … linaria-on-next-demo

...

Success! Created linaria-on-next-demo at /path/to/linaria-on-next-demo
```

構築が終わったら、 Next.js を立ち上げて localhost にアクセスしてみましょう。

```shell
$ yarn dev                                         
yarn run v1.22.17
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
wait  - compiling...
event - compiled client and server successfully in 844 ms (124 modules)
```

この状態で localhost:3000 へアクセスし、Next.js の初期ページが表示されれば成功です。

![スクリーンショット 2022-04-24 15.06.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547464/66350f14-1ad3-f0f0-7222-bdf5d1b85011.png)

### プロジェクト名の使用先

余談ですが、指定したプロジェクト名はどこで活用されるのかを説明します。

`✔ What is your project named?` ではプロジェクト名を指定します。
指定したプロジェクト名は以下で活用されます。

- カレントディレクトリ配下に、指定したプロジェクト名のディレクトリが作成される
  - このディレクトリ配下に　Next.js アプリケーションが構築される
- `pakcage.json` の application name が指定したプロジェクト名となる

```shell
$ ls
linaria-on-next-demo

$ ls linaria-on-next-demo                                                                   
README.md       next.config.js  package.json    public          tsconfig.json
next-env.d.ts   node_modules    pages           styles          yarn.lock
```

TypeScript 関連の設定ファイルも作成されていますね。非常に便利なツールです。

### 作成された package.json

もう1つ余談です。
筆者の環境で作成された `packa.json` は次のとおりとなります。

```shell
{
  "name": "linaria-on-next-demo",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "12.1.5",
    "react": "18.0.0",
    "react-dom": "18.0.0"
  },
  "devDependencies": {
    "@types/node": "17.0.25",
    "@types/react": "18.0.6",
    "@types/react-dom": "18.0.2",
    "eslint": "8.14.0",
    "eslint-config-next": "12.1.5",
    "typescript": "4.6.3"
  }
}
```

ここから linaria を動かすためのパッケージと、webpack/babel のパッケージを追加していきます。

## linaria の導入

### linaria 関連パッケージの追加

次に linaria を追加していきます。
ここでは2つのパッケージを追加します。

- [linaria](https://github.com/callstack/linaria)
- [next-linaria](https://github.com/Mistereo/next-linaria)

```shell
$ yarn add linaria@3.0.0-beta.18 next-linaria@1.0.0-beta
```

今回は linaria の version 3 を使用したかったため、 `@3.0.0-beta.18` でバージョン指定しています。
next-linaria の linaria@3 に対応しているバージョンは `@1.0.0` です。

### next-linaria の設定

[next-linaria のREADME.md](https://github.com/Mistereo/next-linaria#usage) を参考に設定を追加します。
今回はあとから設定を追加する際にわかりやすいよう、 Next.js の設定と linaria の設定のオブジェクトを分けて記述しました。

```javascript:next.config.js
/** @type {import('next').NextConfig} */
const withLinaria = require('next-linaria')

const nextConfig = {
  reactStrictMode: true,
}

const linariaOption = {}

module.exports = withLinaria({
  ...nextConfig,
  linaria: linariaOption,
})
```

next-linaria のドキュメントに書かれている手順としては以上です。

### linaria の使用

さっそく linaria を使用してスタリングを記述してみましょう。

```typescript:pages/index.tsx
import { styled } from "@linaria/react"

const P = styled.p`
  font-size: 30px;
`

const Home: NextPage = () => {
  return (
    ...
        <P>
          Styled by linaria.
        </P>
    ...
  )
}

```

これで上手くいくでしょうか？

### Cannot find module エラー

現段階では動かず、エラーとなるはずです。
筆者の場合は以下のエラーが発生しました。

```
$ yarn dev
yarn run v1.22.17
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
error - Failed to load next.config.js, see more info here https://nextjs.org/docs/messages/next-config-error
Cannot find module '@linaria/webpack-loader'
Require stack:
...
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

```

@linaria/webpack-loader がないと言われるので、次のステップで追加します。

## Webpack の設定

### パッケージのインストール

さて、ないよと言われた @linaria/webpack-loader を追加します。

```
$ yarn add -D @linaria/webpack-loader
```

これで動くでしょうか。

### Cannot find module エラー

現段階ではまだエラーとなります。

```shell
$ yarn dev
yarn run v1.22.17
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
wait  - compiling...
error - ./pages/_app.tsx
Error: Cannot find module '@babel/core'
Require stack:
...
```

今度は @babel/core がないと言われていますね。
こちらも次のステップで追加します。

## Babel の設定

### パッケージのインストール

どんどん追加していきます。

```shell
$ yarn add -D @linaria/webpack-loader
```

これでどうでしょうか。

### 構文エラー

まだエラーとなるんですね。

```shell
yarn dev
yarn run v1.22.17
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
wait  - compiling...
error - ./pages/_app.tsx:2:12
Syntax error: Unexpected token, expected "from"

  1 | import '../styles/globals.css'
> 2 | import type { AppProps } from 'next/app'
    |             ^
  3 |
  4 | function MyApp({ Component, pageProps }: AppProps) {
  5 |   return <Component {...pageProps} />

```

ふむ、 `import type` のあとに `{` が来るのは間違いで、`from` が正しいと言われています。

正直 `import type` 句を存じ上げなかったのですが TypeScript 3.8 で追加された [Type-Only Imports and Exports](https://github.com/microsoft/TypeScript/pull/35200) という機能らしいです。
型のみを import するため、値として使用しようとするとコンパイルエラーを吐いていくれるようですね。

本記事での TypeScript のバージョンは 4.6.3 です。
つまり、使用している TypeScript ではサポートされているものの、なぜかコンパイルエラーが発生しているという状況です🤔

### .babelrc

上記のエラーに関して、 [Next.jsのプロジェクトへCSS-in-JS ライブラリ Linaria（リナリア）を導入するまでの手順](https://t-cr.jp/memo/24517a85fb89591ca) を参考に解決できました。

`.babelrc` に設定を追加します。

```json:.babelrc
{
  "presets": ["next/babel", "linaria/babel"]
}
```

さて、どうでしょう、、、！！

## 成功🎉

これで起動が成功するはずです！

```shell
$ yarn dev
```

いざ localhost:3000 へ！

![スクリーンショット 2022-04-24 15.59.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547464/6ce33fa5-8199-3739-84b8-b9c6f51478df.png)

なんと既存のスタイルリングがぶっこわれている🤣
`*.css` ファイルに寄るスタリングが効いていないですね。

しかし `Styled by linaria` のスタイリングはバッチリ効いていることが確認できます。
本記事の目的は達成できたため、以上で終了とします。

もし今後 CSS ファイルによるスタイリングが必要となった場合、解決できたら記事にしたいと思います。

# 最後に

TypeScript 3.8 で追加された Type-Only Imports は初めて知りました。
今後も活用したいと思います。

また、 babel/webpack 周りの知識が弱々すぎて `.babelrc` の設定が必要な背景を理解できずにいます、、、。
babel のプリセットとして `next/babel` と `linaria/babel`　を有効化しているのはわかるのですが、なぜそれが必要なのか、もっというと「babel - webpack - next - typescript - linaria (linaria-next)」間の関係性がわかっていないです。
どこかで勉強したいと思います。

どなたかのお役に立てば幸いです。

# 参考文献

- [Getting Started | Next.js](https://nextjs.org/docs/getting-started)
- [Mistereo/next-linaria](https://github.com/Mistereo/next-linaria)
- [Next.jsのプロジェクトへCSS-in-JS ライブラリ Linaria（リナリア）を導入するまでの手順](https://t-cr.jp/memo/24517a85fb89591ca)
- [TypeScript v3.8.1-rc 変更点](https://qiita.com/vvakame/items/72da760526ec7cc25c2d)