Next.jsでReScript, tailwindを使う

# Next.js x ReScript

## ReasonMLを利用するNext.jsのサンプルを利用

```bash
yarn create next-app --example with-reasonml with-reasonml-app
```

## ライブラリをupgrade

```bash
yarn upgrade bs-platform@^9.0.2 reason-react@^0.9.1 react@^17.0.1 react-dom@^17.0.1
```

サンプルをコマンドで作った時はパッケージのバージョンが古いので更新する。

## ReasonML -> ReScriptに変換

```bash
for f in pages/**/*.re; do; node_modules/.bin/bsc -format $f > ${f%.re}.res && rm $f; done;
for f in components/**/*.re; do; node_modules/.bin/bsc -format $f > ${f%.re}.res && rm $f; done; 
for f in bindings/**/*.re; do; node_modules/.bin/bsc -format $f > ${f%.re}.res && rm $f; done;
```

で一括でReasonML形式(.re)からReScript形式(.res)に変換できる。

## 動作確認

以下の二つをそれぞれ別のターミナルタブ/ウィンドウで実施

```bash
yarn dev:reason
```

```bash
yarn dev: next
```

# tailwind

TailwindはCSSフレームワークの一つで、Next.jsで利用する方法が[公式に載っている](https://tailwindcss.com/docs/guides/nextjs)ので概ね従う。

## install

```bash
yarn add --dev tailwindcss@latest postcss@latest autoprefixer@latest
```

```bash
yarn tailwindcss init -p
```

## Next.jsで読めるようにする

```css:style/main.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

```javascript:pages/_app.js
import '../styles/main.css'

export default function MyApp({ Component, pageProps }) {
    return(<div>
        <Component {...pageProps} />
    </div>);
}
```

```javascript:tailwind.config.js
module.exports = {
  purge: [
      './pages/**/*.res',
      './components/**/*.res',
  ],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
```

### memo

`_app.res`から変換できないの？と思うが、
bsconfigで`suffix`を`.bs.js`にしているので`_app.res`を書いても`_app.bs.js`にしかならない。
`suffix`を書き換えれば`.js`を出力することもできるが、[`.bs.js`が強く推奨されている。](https://rescript-lang.org/docs/manual/latest/build-configuration#suffix)

また、スプレッド演算子`...`の利用が推奨されていない。
[Props Spread](https://reasonml.github.io/reason-react/docs/en/props-spread)
> You can't.

[cloneElement](https://reasonml.github.io/reason-react/docs/en/clone-element)を利用することで、回避を試みる。

```rescript:_app.res
%raw(`require("../styles/main.css")`)

let make = (~component, ~pageProps) => ReasonReact.cloneElement(
    component,
    ~props=pageProps,
    []
  );

let default = make
```


![Failed to compile](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68992/88998d14-85f3-1ad7-6859-7e6de4135783.png)

が出て, global CSSが読み込めるのはどうあがいても`pages/_app.js`だけという事実は揺るがないとわかる。
ただ、上記のファイルのようにしてsuffixを`.js`にしても動かなかったので、書き方が間違えている可能性がある[調査中]
`_app.res`の例も[Destructuring](https://rescript-lang.org/docs/manual/latest/overview#destructuring)になってないし。
まぁ、_app.jsぐらいJavaScriptでもええじゃろ。



## 動作確認

Headerを以下のように変更する。外側のclassName`border-b bg-white py-2 md:py-4`でtailwindを利用している。

```rescript:components/Header.res
@react.component
let make = () =>
    <nav className="border-b bg-white py-2 md:py-4">
        <div>
            <Next.Link href="/">{ReasonReact.string("Home")}</Next.Link>
            <Next.Link href="/about">{ReasonReact.string("About")}</Next.Link>
        </div>
    </nav>

let default = make
```

![header_change.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68992/dc0da2a4-ed94-8d63-7a0b-c3c25d743043.png)


#ちなみに

`yarn dev~`のところでbsbのエラーが出た場合は以下のスクリプトを実行するとと直ることがある。

```sh:first.sh
touch ./node_modules/bs-platform/index.js
sed -i -e 's/^{$/{\n  "main": "index.js",/g' ./node_modules/bs-platform/package.json
```

vercelにデプロイする場合はOVERRIDEを設定し、このスクリプトをbuildコマンドに含める。

```bash
./first.sh && yarn build
```

![vercel_build.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68992/a2cb7285-b9a7-ad60-c8d8-388020320572.png)


# Future works

[re-tailwind](https://github.com/phthhieu/re-tailwind)を用いて型を利用した設定が可能？
少し試した感じでは動かなかったので要調査。

# 追記

[rescript用のreactライブラリ](https://github.com/rescript-lang/rescript-react)があったので、そちらを利用する。

## ReasonReact -> RescriptReact

### yarn

```bash
yarn remove reason-react
yarn add @rescript/react
```

### migration

```diff:bsconfig.json
- "bs-dependencies": ["reason-react"],
+ "bs-dependencies": ["@rescript/react"],
```

```diff:Header.res
-             <Next.Link href="/">{ReasonReact.string("Home")}</Next.Link>
-             <Next.Link href="/about">{ReasonReact.string("About")}</Next.Link>
+             <Next.Link href="/">{React.string("Home")}</Next.Link>
+             <Next.Link href="/about">{React.string("About")}</Next.Link>
```

その他の場所についても、`ReasonReact.string`を`React.string`に置き換える。

他の表現についても、[ライブラリのドキュメント](https://rescript-lang.org/docs/react/latest/migrate-from-reason-react)に記載されているので確認されたし。

```diff:package.json
-     "dev:reason": "bsb -clean-world -make-world -w",
+     "dev:rescript": "bsb -clean-world -make-world -w",
```

`package.json`については変えなくても大丈夫だが変えたくなる。


#参考文献
[Next.js - Example app using ReasonML & ReasonReact components](https://github.com/vercel/next.js/tree/master/examples/with-reasonml)
[ReScript - Migrate from BuckleScript/Reason](https://rescript-lang.org/docs/manual/latest/migrate-from-bucklescript-reason)
[ReScript Forum - Purpose, alternatives to next-transpile-modules in next ReScript projects](https://forum.rescript-lang.org/t/purpose-alternatives-to-next-transpile-modules-in-next-rescript-projects/827/8)
[Tailwind CSS - Install Tailwind CSS with Next.js](https://tailwindcss.com/docs/guides/nextjs)
[ReScript - Configuration - suffix](https://rescript-lang.org/docs/manual/latest/build-configuration#suffix)
[ReasonReact - Props Spread](https://reasonml.github.io/reason-react/docs/en/props-spread)
[ReasonReact - cloneElement](https://reasonml.github.io/reason-react/docs/en/clone-element)
[ReScript - Destructuring](https://rescript-lang.org/docs/manual/latest/overview#destructuring)
[re-tailwind](https://github.com/phthhieu/re-tailwind)
[rescript-react](https://github.com/rescript-lang/rescript-react)
[ReScript - rescript-react - Migrate from ReasonReact](https://rescript-lang.org/docs/react/latest/migrate-from-reason-react)