Browser上でtextlintを実行する

2020/02/17
より汎用的にルールをバンドルできるように[ツール化](https://qiita.com/georgeOsdDev@github/items/74c7ee2dd69de107121d)した。

# アイデア

チャットアプリケーションなどで、ユーザー入力中の`textarea`などに対して、[textlint](https://github.com/textlint/textlint)を実行させたい。
アイデア自体は[@textlint/browser](https://github.com/textlint/textlint/issues/299) でも議論されている。
Chrome拡張として、[textlint-proofreader](https://chrome.google.com/webstore/detail/textlint-proofreader/hdongmdneapmhfblomidbafplpanpdmm?hl=ja)が存在するが、今回はSPAを利用するすべてのユーザーに機能としての提供を行いたい。

## できたもの
[textlint-browser-runner](https://github.com/mobilusoss/textlint-browser-runner/tree/master/packages/textlint-browser-runner)
[デモ](https://mobilusoss.github.io/textlint-browser-runner/demo.html)


## 実装

`texlint` コマンド自体は `.textlintrc` に記載されたルールモジュールの解決や、対象ファイルの読み込みなどを行っており、lintの実行処理自体は、[@textlint/kernel](https://github.com/textlint/textlint/tree/master/packages/@textlint/kernel) が担っている。
そこに[TextlintKernelOptions](https://github.com/textlint/textlint/blob/54873c02a2f9267806b402566105d546ee5339e8/packages/%40textlint/kernel/src/textlint-kernel-interface.ts#L147-L162)を正しく渡してあげれば実行できる。

```typescript
export interface TextlintKernelOptions {
    // file type
    // For example) .md
    ext: string;
    // file path
    filePath?: string;
    // plugins
    plugins?: TextlintKernelPlugin[];
    // rules
    rules?: TextlintKernelRule[];
    // filterRules
    filterRules?: TextlintKernelFilterRule[];
    // config base directory
    // It is a value of context.getConfigBaseDir.
    configBaseDir?: string;
}

```

## ビルド設定

`TextlintKernelOptions` の `plugins`や`rules` はそれぞれ`node_modules`として実装されているので、
それらも含めてトランスパイル済みの各ルールをバンドルしたJavaScriptとして出力してあげればIE11を含めてブラウザで動くはず。

core-js/babel/webpack の最適解がわからなさすぎだが、
browserで読み込む[ファイルのエントリーポイントの先頭](https://github.com/mobilusoss/textlint-browser-runner/blob/master/packages/textlint-browser-runner/browser.js#L2-L3)で

```javascript
import 'core-js/stable';
import 'regenerator-runtime/runtime';
```

して、`webpack.config`は[以下の設定](https://github.com/mobilusoss/textlint-browser-runner/blob/master/packages/textlint-browser-runner/webpack.config.js)にすることでIE11でも動くものがビルドできた。


```javascript
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');
module.exports = {
  mode: 'production',
  entry: {
    'textlint': './browser.js',
  },
  output: {
    filename: '[name].bundle.min.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /@babel(?:\/|\\{1,2})runtime|core-js/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', {
                useBuiltIns: 'entry',
                corejs: 3,
                targets: {
                  ie: '11',
                },
              }]
            ]
          }
        }
      }
    ]
  },
  optimization: {
    minimize: true,
      minimizer: [new TerserPlugin({
        parallel: true,
        terserOptions: {
          ecma: 5,
          compress: true,
          output: {
            comments: false,
            beautify: false
          }
        }
      })]
  },
  plugins: [
    new MomentLocalesPlugin({
      localesToKeep: ['en', 'ja', 'es', 'fr', 'zh-cn', 'zh-hk', 'zh-tw',], // https://github.com/azu/textlint-rule-date-weekday-mismatch/blob/master/src/textlint-rule-date-weekday-mismatch.js#L10
    }),
  ]
};
```



## 問題点

- [bundleサイズが大きい](https://github.com/mobilusoss/textlint-browser-runner/issues/4)
- `fs` アクセスするルールがbundleに失敗する。[babel-plugin-static-fsをビルド時に利用すれば解決するかもしれない。](https://github.com/mobilusoss/textlint-browser-runner/issues/19)