transpileOnly trueにしたts-loader でNuxt.jsを書く

# 目的

- nuxt.jsをtypescriptで書きたい
- ビルドを高速化するために ts-loaderの `transpileOnly: true` にしたい
- ただし型のチェックはしたいので `fork-ts-checker-webpack-plugin` を使いたい

# 前提
- nuxt: 2.3.4
- もしくは nuxt-edge: ^2.0.0-25598366.31ec2a2

# nuxt v2.3.4 版

### transpileOnly = true, fork-ts-checker-webpack-plugin の導入

typescript.js を修正します

```js:typescript.js
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

module.exports = function() {
  this.extendBuild(config => {
    const tsLoader = {
      loader: 'ts-loader',
      options: {
        appendTsSuffixTo: [/\.vue$/],
        transpileOnly: true // トランスパイルのみの設定をいれる
      },
    };

    ..略
    if (config.name === 'client') { // サーバ・クラアイント用両方で型検査する必要はないのでどちらか一方だけ実施
      config.plugins.push(new ForkTsCheckerWebpackPlugin({
        workers: ForkTsCheckerWebpackPlugin.ONE_CPU,
        tslint: true,
        vue: true,
      }));
    }
    ..略
  })
}
```

### export 'XXXX' was not found in './path/to/file.ts' を抑止する

#### 開発環境用ビルドの際の抑止

typescript.js を修正します

```js:typescript.js
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
// ※1
class IgnoreNotFoundExportPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('warnfix-plugin', (stats) => {
      const messageRegExp = /export '.* was not found in/
      stats.compilation.warnings = stats.compilation.warnings.filter((warn) => {
        if (warn.name === 'ModuleDependencyWarning' && messageRegExp.test(warn.message)) {
          return false
        }
        return true
      })
    })
  }
}

module.exports = function() {
  this.extendBuild(config => {
    ..略
    config.plugins.push(new IgnoreNotFoundExportPlugin())
  })
}
```

- ※1 参考: https://github.com/TypeStrong/ts-loader/issues/653#issuecomment-390889335 

#### 本番環境用ビルドの際の抑止

nuxt.config.js を修正します

```js:nuxt.config.js
  build: {
    stats: {
      warningsFilter: /export .* was not found in/,
    }
  }
```


# nuxt-edge 版
## webpackの設定をカスタマイズする

- https://github.com/nuxt-community/typescript-template の `nuxt.confit.js` と `modules/typescript.js`　を参考にカスタマイズしていきます

### transpileOnly = true, fork-ts-checker-webpack-plugin の導入

typescript.js を修正します

```js:typescript.js
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

module.exports = function() {
  this.extendBuild(config => {
    const tsLoader = {
      loader: 'ts-loader',
      options: {
        appendTsSuffixTo: [/\.vue$/],
        transpileOnly: true // トランスパイルのみの設定をいれる
      },
    };

    ..略
    config.plugins.push(new ForkTsCheckerWebpackPlugin({
      workers: 2, // ここはお好みで
      tslint: true,
      vue: true,
    }));
    ..略
  })
}
```

これで導入完了ですがwebpack4 では https://github.com/TypeStrong/ts-loader/issues/751 でissueにあがっているようにexportのwarningが出てしまうのでノイズを減らすためにこれを抑止します

### export 'XXXX' was not found in './path/to/file.ts' を抑止する

#### 開発環境用ビルドの際の抑止

typescript.js を修正します

```js:typescript.js
module.exports = function() {
  this.extendBuild(config => {
    ..略
    if (config.entry.app && config.entry.app instanceof Array) {
      // ※1
      config.entry.app = config.entry.app.map((entry) => entry.replace(/webpack-hot-middleware\/client\?/, 'webpack-hot-middleware/client?quiet=true&'));
    }

    // ※2
    const friendlyErrorsWebpackPlugin = config.plugins.find((plugin) => plugin.constructor.name === 'FriendlyErrorsWebpackPlugin');
    if (friendlyErrorsWebpackPlugin) {
      friendlyErrorsWebpackPlugin.logLevel = 2;
    }
    ..略
  })
}
```

- ※1 開発環境ではhmrを利用していますのでブラウザのconsole.warn に吐かれないように抑止します
  - パラメータ `quiet=true` を追加します
     - 参考：https://github.com/webpack-contrib/webpack-hot-middleware#config
  - nuxt内では下記のようなコードになっており `webpack-hot-middleware` に対してオプションを追加できないコードになっているため `webpack-hot-middleware/client?` を `webpack-hot-middleware/client?quiet=true&` に置換するようにしています（かなり力技）

```js:node_modules/nuxt-edge/dist/nuxt.js
     // Add HMR support
     if (this.options.dev) {
       config.entry.app.unshift(
         // https://github.com/glenjamin/webpack-hot-middleware#config
         `webpack-hot-middleware/client?name=client&reload=true&timeout=30000&path=${
           this.options.router.base
         }/__webpack_hmr`.replace(/\/\//g, '/')
       );
     }
```

- ※2 npm run nuxtを実行しているターミナルにwarningが出力されないように抑止します
  - nuxt内では `FriendlyErrorsWebpackPlugin` というプラグインでエラーメッセージを出力しています. そのログレベルを `ERROR` に変更します

```js:node_modules/nuxt-edge/dist/nuxt.js
    // Add friendly error plugin
    if (this.options.dev && !this.options.build.quiet) {
      config.plugins.push(
        new FriendlyErrorsWebpackPlugin({
          clearConsole: true,
          logLevel: 'WARNING'
        })
      );
    }
```

```js:node_modules/@nuxtjs/friendly-errors-webpack-plugin/src/friendly-errors-plugin.js
const logLevels = {
  'INFO': 0,
  'WARNING': 1,
  'ERROR': 2,
  'SILENT': 3
}
```

#### 本番環境用ビルドの際の抑止

nuxt.config.js を修正します

```js:nuxt.config.js
  build: {
    stats: {
      warningsFilter: /export .* was not found in/,
    }
  }
```

これで設定は完了です．コードの保存の度に型のチェックとlintが走ります

![名称未設定.png](https://qiita-image-store.s3.amazonaws.com/0/248262/5b022948-4438-81a6-6343-5d1cdbccdc45.png)

# まとめ

- 最終的な設定ファイルはこちら
  - nuxt v2版: [nuxt.config.js](https://github.com/Yama-Tomo/vue-vuex-typescript-sample/blob/9308eb1ef1cb6a3d98a1671484f8297c57e47a9c/nuxt.config.js) [typescript.js](https://github.com/Yama-Tomo/vue-vuex-typescript-sample/blob/9308eb1ef1cb6a3d98a1671484f8297c57e47a9c/src/modules/typescript.js)

  - nuxt-edge版: [nuxt.config.js](https://github.com/Yama-Tomo/vue-vuex-typescript-sample/blob/7306eb65bdf44724f8d6097887f1e24cec7f5cc6/nuxt.config.js), [typescript.js](https://github.com/Yama-Tomo/vue-vuex-typescript-sample/blob/c6733759b907a67917a44f9eec25a1913354186a/src/nuxt_with_typescript.js) 

- 自分の環境では `transpileOnly: false` と比較してビルドの時間が15%くらい短縮されました！

- nuxt内でどういったwebpackのビルドがされているのかソースを読まないとやりたいことができない場合もありますが，どのようにnuxtがビルドしているかの理解を深めるには良いと思います