webpackでnpmでないCDNのライブラリを読み込んでESLint対応

# 最初に結論から
現時点で僕が考えるベストプラクティスはこの組み合わせ。

* CDNのURLはwebpackからうまく指定できないので、HTML側の`<script>`タグ内に書く
* `webpack.config.js`で`externals`を使ってHTML側のモジュールを利用
* アプリ側のJSコードで`import`する
* `eslint-import-resolver-webpack`を使ってESLintにwebpackの設定を認識させる

# 具体例（コード抜粋)

[Google API Client Library for JavaScript](https://developers.google.com/api-client-library/javascript/start/start-js)をCDNで使う場合の例です。

## eslint-plugin-importを使っていない場合
これがwebpackのオーソドックスな方法
`externals`で外部に`gapi`が存在することを教えてあげると、
アプリ側のJSコードで`import`することができます。
https://webpack.js.org/configuration/externals/

```html:gapi.html
<!-- 前略 -->
<script src="https://apis.google.com/js/api.js"></script>
```

```js:webpack.config.js
const webpack = require('webpack');

module.exports = {
  // 中略

  externals: {
    gapi: 'gapi',
  },
};
```

```js:app.js
import gapi from 'gapi'

gapi.auth.authorize(
  // 後略
```


## eslint-plugin-importを使っている場合
上記のやり方で`import`すると、ESLintの`import/no-unresolved`に叱られます。
https://github.com/benmosher/eslint-plugin-import/blob/HEAD/docs/rules/no-unresolved.md

これはESLintが`externals`(というかwebpackの設定全体)を読み込めていないのが理由で、
`eslint-import-resolver-webpack`を使ってwebpackの設定を読めるようにしてあげればOK。
https://www.npmjs.com/package/eslint-import-resolver-webpack

これが冒頭に箇条書きした「ベストプラクティス」です。

```html:gapi.html
<!-- 前略 -->
<script src="https://apis.google.com/js/api.js"></script>
```

```js:webpack.config.js
const webpack = require('webpack');

module.exports = {
  // 中略

  externals: {
    gapi: 'gapi',
  },
};
```

```js:app.js
import gapi from 'gapi' // ここでエラーが出る

// エラーを気にしなければwebpackのビルド自体は通る
gapi.auth.authorize(
  // 後略
```

```yaml:.eslintrc.yml
# 前略
extends:
  - standard
  - plugin:import/errors
# ここまでが既に設定されていると仮定

# eslint-import-resolver-webpackパッケージをインストールして、
# 以下の2行を足してあげる
settings:
  import/resolver: webpack
```

## eslint-config-airbnbを使っている場合
記事公開時、最初に書いていた方法です。
この記事を公開した時点では、ESLintのプリセットとして
`standard`ではなく`airbnb-base`を使ってました。
https://www.npmjs.com/package/eslint-config-airbnb-base

これ厳しすぎて逆に開発効率落ちるので使うのやめたんですが、
`import`に関するエラーも独特の厳しさがあり、
2番目に書いた方法ではエラーが取れなかったので、こんな風にしてたのでした。

```html:gapi.html
<!-- 前略 -->
<script src="https://apis.google.com/js/api.js"></script>
```

```js:webpack.config.js
const webpack = require('webpack');

module.exports = {
  // 中略

  externals: {
    gapi: 'gapi',
  },

  plugins: [
    new webpack.ProvidePlugin({ gapi: 'gapi' }),
  ],
};
```
```js:app.js
// importはしない
gapi.auth.authorize(
  // 後略
```
```yaml:.eslintrc.yml
# 前略
globals:
  gapi: true
```
[no-extraneous-dependencies](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-extraneous-dependencies.md)とか[extensions](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md)あたりが面倒臭いんですよねー。
真っ当な解決方法があるのかもしれませんが、そこまで付き合ってられないので、
そもそも`import`するの自体を諦めて、こんなアプローチでやってます。

* `webpack.config.js`で`externals`を使ってHTML側のモジュールを利用
* `webpack.config.js`で`webpack.ProvidePlugin`を使ってグローバルっぽく使えるように設定
* `.eslintrc`の`globals`に該当モジュール名を記載してエラーが出ないように

しかしねぇ、気づいたんですよ。
「`import`せず`globals`に追加」なんていう禁じ手を犯すんなら、
最初っからwebpackの仕組みは無視して、↓で十分なのですわ。。


## 【シンプル版】eslint-config-airbnbを使っている場合

```html:gapi.html
<!-- 前略 -->
<script src="https://apis.google.com/js/api.js"></script>
```
```js:app.js
// importはしない
gapi.auth.authorize(
  // 後略
```
```yaml:.eslintrc.yml
# 前略
globals:
  gapi: true
```
`webpack.config.js`自体は存在するけど、その中で`gapi`については一切触れなくてもOK。
`import`しないのでビルドもしれっと通ります。


## webpackでGoogle APIを使う方法について
GitHubにこんなIssueが立ってて、
https://github.com/google/google-api-javascript-client/issues/319
こんな非公式ライブラリも紹介されてるんですが、
https://github.com/ryanjohnston/Load-Google-API
何となく将来性が疑問なので、webpackの標準方法でやってみました。

~~てゆーかwebpackからはNode.jsライブラリ使えるんだから、
公式のNode版を使えば良さそうにも思いますけどね。。これはまだ未検証。
https://github.com/google/google-api-nodejs-client~~
追記：リポジトリのIssueで、Googleの中の人が「[サーバー専用だから無理](https://github.com/google/google-api-nodejs-client/issues/741#issuecomment-299930534)」って回答してますね。
強引に使うハックもあるんでしょうが、そこまでがんばる必要ないと思うので、試すのはやめておきまーす。
> @jmdobry commented on 9 May
> It's not meant to be, as it's meant to be used on the server only. It doesn't need to be passed through any Webpack build.


## 参考サイト
* [Webpackのexternalsを一部試してみた](https://qiita.com/youkaiantena/items/be4fb46ac7f5af2de2fb)
* [webpackでCDNから取ってきたりした外部のjQueryなどを利用する方法](http://frontend-takuyan.hateblo.jp/entry/jquery-in-webpack)
* [ESLintについてのメモ#Globals](https://qiita.com/makotot/items/822f592ff8470408be18#globals)
* [TypeScript + eslint で import/no-unresolved が出る場合の2つの対処法 ](https://qiita.com/euxn23/items/e2b9226ab1e02a9b1f20)
* [Twitter@tarot - import/no-extraneous-dependenciesとimport/extensionsがうざいな…](https://twitter.com/tarot/status/892351533001527296)