@ionic/reactをParcelで動かす

# はじめに

前作：[最小構成で始める@ionic/react](https://qiita.com/_likr/items/0259014311631979a420)

前回自分でWebpackの設定を書いて@ionic/reactを動かしました。
今回は、既にParcelで作っているプロジェクトに@ionic/reactを導入したかったので、Parcelで@ionic/reactを動かせる設定について調べてみました。

# ソースコード

まずはセットアップです。

```shell-session
$ npm init -y
$ npm i react react-dom react-router@4 react-router-dom@4 @ionic/react
$ npm i -D parcel-bundler
```

以下はアプリケーションのコードです。

```src/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width">
<title>@ionic/react with Parcel</title>
</head>
<body>
<div id="content"/>
<script src="./index.js"></script>
</body>
</html>
```

```jsx:src/index.js
import '@ionic/core/css/core.css'
import '@ionic/core/css/ionic.bundle.css'

import React from 'react'
import { render } from 'react-dom'
import {
  BrowserRouter as Router,
  Route,
  Switch
} from 'react-router-dom'
import {
  IonApp,
  IonContent,
  IonFab,
  IonFabButton,
  IonHeader,
  IonIcon,
  IonItem,
  IonList,
  IonTitle,
  IonToolbar
} from '@ionic/react'

const MainPage = () => {
  const [items, setItems] = React.useState(['A', 'B', 'C'])

  return <>
    <IonContent>
      <IonList>
        {
          items.map((item, i) => {
            return <IonItem key={i}>{item}</IonItem>
          })
        }
      </IonList>
    </IonContent>
    <IonFab vertical='bottom' horizontal='end' slot='fixed'>
      <IonFabButton
        onClick={() => {
          const newItems = Array.from(items)
          newItems.push('new item')
          setItems(newItems)
        }}
      >
        <IonIcon name='add' />
      </IonFabButton>
    </IonFab>
  </>
}

const App = () => {
  return <Router>
    <IonApp>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Hello @ionic/react</IonTitle>
        </IonToolbar>
      </IonHeader>
      <Switch>
        <Route path='/' component={MainPage} />
      </Switch>
    </IonApp>
  </Router>
}

render(<App />, document.getElementById('content'))
```

# 問題点

とりあえず素直に動かしてみるといくつかの問題点に行き当たりました。

## source map関係

`npx parcel src/index.html` などで実行すると、以下のようなwarningが発生しました。

```console
⚠️  Could not load source file "../src/index.ts" in source map of "../node_modules/@ionic/react/dist/index.js".
⚠️  Could not load source file "../src/index.ts" in source map of "../node_modules/@ionic/react/dist/index.js".
⚠️  Could not load source file "../../src/components/index.ts" in source map of "../node_modules/@ionic/react/dist/components/index.js".
⚠️  Could not load source file "../../src/components/IonLoading.tsx" in source map of "../node_modules/@ionic/react/dist/components/IonLoading.js".
⚠️  Could not load source file "../../src/components/IonToast.tsx" in source map of "../node_modules/@ionic/react/dist/components/IonToast.js".
⚠️  Could not load source file "../../src/components/IonAlert.tsx" in source map of "../node_modules/@ionic/react/dist/components/IonAlert.js".
⚠️  Could not load source file "../../src/components/createComponent.tsx" in source map of "../node_modules/@ionic/react/dist/components/createComponent.js".
⚠️  Could not load source file "../../src/components/IonModal.tsx" in source map of "../node_modules/@ionic/react/dist/components/IonModal.js".
⚠️  Could not load source file "../../src/components/IonActionSheet.tsx" in source map of "../node_modules/@ionic/react/dist/components/IonActionSheet.js".
⚠️  Could not load source file "../../src/components/IonPopover.tsx" in source map of "../node_modules/@ionic/react/dist/components/IonPopover.js".
⚠️  Could not load source file "../../src/components/IonPage.tsx" in source map of "../node_modules/@ionic/react/dist/components/IonPage.js".
⚠️  Could not load source file "../../../src/components/navigation/IonTabs.tsx" in source map of "../node_modules/@ionic/react/dist/components/navigation/IonTabs.js".
⚠️  Could not load source file "../../../src/components/navigation/IonTabBar.tsx" in source map of "../node_modules/@ionic/react/dist/components/navigation/IonTabBar.js".
⚠️  Could not load source file "../../../src/components/navigation/IonRouterOutlet.tsx" in source map of "../node_modules/@ionic/react/dist/components/navigation/IonRouterOutlet.js".
⚠️  Could not load source file "../../src/components/createControllerComponent.tsx" in source map of "../node_modules/@ionic/react/dist/components/createControllerComponent.js".
⚠️  Could not load source file "../../src/components/createOverlayComponent.tsx" in source map of "../node_modules/@ionic/react/dist/components/createOverlayComponent.js".
⚠️  Could not load source file "../../../src/components/utils/index.ts" in source map of "../node_modules/@ionic/react/dist/components/utils/index.js".
```

実際にnpmで配布されている@ionic/reactにはこれらのファイルが含まれていません。
対処法は調べていませんが、無視してもとりあえず起動はできます。

## IconのSVG

ioniconの.svgファイルが全てファイルとして出力されます。
`npx parcel build src/index.html` したときにはdistに1392個のsvgファイルが出力されました。
本来は使用している最小限のアイコンのみが読み込まれます。
これも、細かいことを気にしなければ動作には影響しません。

## jsファイルのlazy loading

とりあえずparcelで実行してあげればビルドは通りますが、いざブラウザを開くと画面は真っ白で、コンソールに以下のようなエラーが表示されています。

![Screen Shot 2019-06-11 at 11.38.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4249/7f40bb92-5f1a-2f58-fcd6-f72b97eecce7.png)

Ionicのコンポーネントの遅延読み込みをParcelがうまく処理できていないような感じです。
表面的には、ブラウザから見た `/build` に `node_modules/@ionic/core/dist/esm/es5/build` の中身が存在していたらとりあえず実行できそうです。

取り急ぎ `parcel-plugin-static-files-copy` を使って対処してみます。

```shell-session
$ npm i -D parcel-plugin-static-files-copy
```

`package.json`に設定を追記します。

```diff:package.json
  {
    "name": "ionparcel",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "dependencies": {
      "@ionic/react": "0.0.5",
      "react": "^16.8.6",
      "react-dom": "^16.8.6",
      "react-router": "^4.3.1",
      "react-router-dom": "^4.3.1"
    },
    "devDependencies": {
      "parcel-bundler": "^1.12.3",
      "parcel-plugin-static-files-copy": "^2.1.2",
      "prettier": "^1.18.2"
+   },
+   "staticFiles": {
+     "staticPath": "node_modules/@ionic/core/dist/esm/es5"
    }
  }
```

これでとりあえずParcelでも@ionic/reactを動作するようになりました。

# おわりに

とりあえず動きましたが、アイコンでもコンポーネントでも、本来Ionicが必要な部分だけを読み込むように最適化してくれる部分がうまく処理できず、とりあえず全部読み込むような対処になっています。
もう少し綺麗な対処についてはまた考えてみたいと思います。