【メモ】Google Closure Compilerを使う

# Google Closure Compilerとは?

[https://developers.google.com/closure/compiler/]()
[https://github.com/google/closure-compiler]()

JavaScriptをJavaScriptに変換するコンパイラー。コードをより効率的な形に変換してくれる。ClojureScriptが使っていたりする。

その昔は独自のモジュールシステムを持っていたのだが、いつのまにかnpm経由で使えるようになっていたり、nodeモジュールに対応していたり、いろいろ進歩している模様。


# `package.json`

```
{
    "name": "teach-yourself-closure-compile",
    "version": "1.0.0",
    "scripts": {
        "build": "..."
   },
    "dependencies": {
        "immutable": "^4.0.0-rc.12",
        "luxon": "^1.8.2",
        "react": "^16.6.3",
        "react-dom": "^16.6.3"
    },
    "devDependencies": {
        "@babel/cli": "^7.2.0",
        "@babel/core": "^7.2.0",
        "@babel/preset-react": "^7.0.0",
        "babel-loader": "^8.0.4",
        "cpx": "^1.5.0",
        "google-closure-compiler": "^20181210.0.0",
        "rimraf": "^2.6.2"
    }
}
```

今回はReactでごく簡単なサンプルを作り、動作させることを目標とする。ついでなのでいくつかライブラリーを入れてみる。
ビルド手順は後述。


# `index.jsx`

```jsx:index.jsx
const React = require("react")
const ReactDOM = require("react-dom")
const ImMap = require("immutable/dist/immutable").Map
const DateTime = require("luxon/build/node/luxon").DateTime

class Application extends React.Component {

    constructor(props) {
        super(props);
        this.state = { date: DateTime.local().toISO() }
    }

    componentDidMount() {
        this.timer = setInterval(() => this.setState({ date: DateTime.local().toISO() }), 500)
    }

    componentWillUnmount() {
        clearTimeout(this.timer)
    }

    render() {
        return (
            <div>
                <h1>hello, closure compiler!</h1>
                <p>User: {this.props.user.get("firstName")} {this.props.user.get("lastName")}</p>
                <p>Current date: {this.state.date}</p>
            </div>
        )
    }
}

ReactDOM.render(
    <Application user={ImMap({ firstName: "John", lastName: "Smith" })} />,
    document.getElementById("app")
)
```

どうもデフォルトインポートをうまく扱えないようで断念。`require`でロードする。
また`package.json`の指定ファイルを見てくれないのか、`require("immutable")`ではだめで、`require("immutable/dist/immutable")`とファイルのパスを指定する必要があった。

immutable.jsの`Map`を`ImMap`にリネームしているのは、バインディングが重複しているというエラーが出たため。


# ビルド


## Babel

まずJSXを普通のJSに変換する。ここではBabelを使用。

```json:.babelrc
{
    "presets": [
        "@babel/preset-react"
    ],
    "compact": false,
    "comments": true
}
```

```
babel src --out-dir ./out/babel
```


## コンパイル

```
google-closure-compiler --js out/babel/index.js --js node_modules/react --js node_modules/react-dom/index.js --js node_modules/react-dom/cjs/react-dom.production.min.js --js node_modules/react-dom/cjs/react-dom.development.js  --js node_modules/object-assign --js node_modules/prop-types --js node_modules/scheduler --js node_modules/luxon/build/node/luxon.js --js node_modules/immutable/dist/immutable.js --entry_point out/babel/index.js --js_output_file out/closure/index.js --module_resolution NODE --dependency_mode LOOSE --process_common_js_modules --language_in ECMASCRIPT_NEXT
```

`--js`フラグで入力ファイルを指定。見てのとおり、モジュールもすべて指定する必要がある。そんなわけないだろうと思うのだが、それらしいオプションを発見できず断念。知っている方教えてください。

`--entry_point`でコンパイルを始めるエントリーポイントを指定する。今回はBabelでコンパイルされたファイルを指定。

`--js_output_file`は出力先。

`--module_resolution NODE` `--process_common_js_modules`でnodeモジュールを使っていることを伝える。

`--language_in ECMASCRIPT_NEXT`でECMAスクリプトバージョンを指定する。


[フラグ一覧はこちら](https://github.com/google/closure-compiler/wiki/Flags-and-Options)


# その他

* React DevToolsが動かなかった。これは悲しい。
* ライブラリーもコンパイルされるので、警告が大量に出力される。`--warnings_whitelist_file`フラグでホワイトリストファイルを作ればよさそうだが、さすがに面倒くさすぎる。
* [今回のソースのリポジトリはこちら](https://bitbucket.org/minebreaker_tf/teach-yourself-closure-compiler)