Expo Web Betaのsize limit問題から見えた、実用性への道筋

※ [公式フォーラムで回答したやつ](https://forums.expo.io/t/not-fixable-warning-on-build/25905/2?u=nakazan)の日本語版です

[Expo v34でExpo WebのBeta版がアナウンスされました](https://blog.expo.io/expo-sdk-34-is-now-available-4f7825239319)。

Alpha版はちょっと辛い感じでしたが、Betaにもなれば少しは実用性が出てきたかなと思って、デプロイパイプラインを構築してみました。

## webpackのデフォルト挙動をケアできてない問題がある

CIサーバー内でデプロイ用にビルドをしてみたところ、次のエラーが発生しました。

```console
$ expo build:web                                                                                                          
- Making sure project is set up correctly...
[====================] 100% (24.1 seconds)


Treating warnings as errors because process.env.CI = true.
Most CI servers set it automatically.

Failed to compile.

asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets: 
  ./fonts/MaterialCommunityIcons.ttf (503 KiB)

webpack performance recommendations: 
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
```

webpack勢の皆さまならピンと来たと思うのですが、これはwebpackの [`performance.maxAssetSize`](https://webpack.js.org/configuration/performance/#performancemaxassetsize) を超えた場合に起きるエラーです。デフォルトで `250000` バイト以上の静的ファイルのホスティングはパフォーマンス上の問題をもたらすのでやめなさい、という設定ですね。

デバッグ中はwarning扱いなので問題ないのですが、CIサーバーで `CI=true` の環境変数が付与されると、warningでもビルドが落ちるようになります。500KB超えの `MaterialCommunityIcons.ttf` を明示的に排除する手段が今のところない以上、デフォルト設定のままCIに載せると必ず落ちる、みたいな状況です。

## webpackで解決する

さて、このまま手詰まりかと思われたのですが、最近出てきた `expo-cli` のv3系で `expo customize:web` というコマンドが登場していました（現行の`3.0.10`では動かないので注意が必要。後述）。

* https://docs.expo.io/versions/v34.0.0/guides/customizing-webpack/

![スクリーンショット_2019-08-26_17_26_50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2276/bec26340-7e0c-b0af-1469-a0f9e1ac57fc.png)

もともと隠蔽度が高めのExpo Webですが、多くの場合でカスタマイズしたくなる設定ファイルを少しだけ露出させてくれるようです。これでwebpack.config.jsが作れます。

大事なことなのでもう一度言います。 **webpack.config.jsが作れます！！！** 
**やったぞ！　これで俺たちの勝ちだ！！！！！！（誇張表現）**

### webpack.config.jsでmaxAssetSizeを変更する

`expo customize:web` コマンドで生成したwebpack.config.jsを次のように編集します。

```diff
  const createExpoWebpackConfigAsync = require('@expo/webpack-config');
  
  module.exports = async function(env, argv) {
    const config = await createExpoWebpackConfigAsync(env, argv);
    // Customize the config before returning it.
+
+   config.performance = {
+     ...config.performance,
+     maxAssetSize: 550000,
+   };
+
    return config;
  };
```

雑に550000バイトまではOKにしました。
これで実行し直すと、次のようになります。

```console
$ CI=true expo build:web                                                                                                          
- Making sure project is set up correctly...
[====================] 100% (23.5 seconds)

Compiled successfully.
```

上手いこと成果物が作られました。これでデプロイできそうです。

まあ、500KBのフォントファイルなんかダウンロードしたくないので、デプロイはしたもののアプリ内で使わないように気をつけなきゃなあという気持ちですね……（使わない分にはブラウザがロードすることもない）

## ビルドをwebpack筋でなんとかできた

今回は雑に `maxAssetSize` を編集しましたが、やりようによっては `MaterialCommunityIcons.ttf` をwebpackが収集しないようにするような設定もできそうです。[createFontLoader](https://github.com/expo/expo-cli/blob/9feb0e2/packages/webpack-config/webpack/loaders/createFontLoader.js)に介入するとよさそう。

Expo Webの方針として、隠蔽がキツいまま、痒いところに手が届かない方針で行くのかなと思っていましたが、開発者側でwebpack.config.jsを弄っていいということであれば、それなりにカスタマイズが効きそうです。Create React Appでいう[react-app-rewired](https://github.com/timarney/react-app-rewired)と近い感覚ですね。

趣味プロダクトみたいなところから、順次導入を試してみたいと思い始めました。

## まとめ

今回は調べてませんが、[browserslist](https://create-react-app.dev/docs/supported-browsers-features)や[homepage](https://create-react-app.dev/docs/deployment#building-for-relative-paths)みたいな設定もできると、かなりCreate React App（with rewired）の使い勝手に肉薄していく気がします。

## おまけ：expo customize:webがうまく動かない場合のワークアラウンド

expo-cliの `v3.0.10` では、 `expo customize:web` コマンドがうまく動きません。

```
$ expo customize:web
Cannot read property 'readConfigJsonAsync' of undefined
TypeError: Cannot read property 'readConfigJsonAsync' of undefined
    at action (/expo-cli@3.0.10/src/commands/customize.ts:126:3)
    at Command.action (/expo-cli@3.0.10/src/exp.ts:82:13)
```

どうやらimportの仕方をミスっていたようで、すでに修正がmasterにマージされています。

* https://github.com/expo/expo-cli/pull/960

`v3.0.11` か `v3.1.0` で修正されると思いますが、それまでの間使えるワークアラウンドがあります。 `customize:web` でwebpack.config.jsを選んだときに起こることを、手動で実施します。

1. `@expo/webpack-config` をインストールする
2. ↓のwebpack.config.jsをトップレベルに配置する

```js:webpack.config.js
const createExpoWebpackConfigAsync = require('@expo/webpack-config');

module.exports = async function(env, argv) {
  const config = await createExpoWebpackConfigAsync(env, argv);
  // Customize the config before returning it.
  return config;
};
```

これで `expo customize:web` が動かなくても大丈夫です。