【ビルド不要】HTML単体でES6+をトランスパイルしIE11へ対応

(2020/07/03 ... 記事を全面リニューアルしました)

**令和の時代、わが国の企業は大部分が未だIEの呪縛に囚われている･･･**

MicrosoftがいくらIEを非推奨としてEdge(Chromium)を推奨しても、御社の情シスはそれを許しません。

そんな闘う社内エンジニアの方々に朗報です。


# HTMLサンプル
```html:index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
        <meta http-equiv="x-ua-compatible" content="ie=edge">

        <script src="https://cdn.jsdelivr.net/npm/@babel/polyfill@latest/dist/polyfill.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/@babel/standalone@latest/babel.min.js"></script>

        <title>Babel</title>
    </head>

    <body>
        Babel Test
    </body>

    <style>
        html{
            color: #d8d8d8;
            background-color: #242424;
        }
    </style>

    <script type="text/babel" data-presets="env,stage-3">
        class FileReaderEx extends FileReader{
            constructor(){
                super();
            }

            #readAs(blob, ctx){
                return new Promise((res, rej)=>{
                    super.addEventListener("load", ({target}) => res(target.result));
                    super.addEventListener("error", ({target}) => rej(target.error));
                    super[ctx](blob);
                });
            }

            readAsArrayBuffer(blob){
                return this.#readAs(blob, "readAsArrayBuffer");
            }

            readAsDataURL(blob){
                return this.#readAs(blob, "readAsDataURL");
            }

            readAsText(blob){
                return this.#readAs(blob, "readAsText");
            }
        }

        (async()=>{
            const blob = new Blob([new Uint8Array(1024)]);
            const buffer = await new FileReaderEx().readAsArrayBuffer(blob);
            console.log(buffer);
        })();
    </script>
</html>
```
バリバリなES6+ですが、このHTMLはそっくりそのままIE11で動きます。


# 解説
- [@babel/standalone - npm](https://www.npmjs.com/package/@babel/standalone)
- [@babel/polyfill - npm](https://www.npmjs.com/package/@babel/polyfill)

`@babel/standalone`というBabelのスタンドアロン版を使用します。
通常版はWebpackやParcelといったビルドツール経由で受動的に使用されることを前提としているのに対し、スタンドアロン版はそれ単体で能動的に動作します。

`@babel/polyfill`はBabelがトランスパイル時に使用する機能も含まれているので`@babel/standalone`より先にロードする必要があります。

具体的な動作としては、HTML内の`<script type="text/babel">`タグを検出し、その中のソースコードをトランスパイルした後にHTML内へ挿入します。

通常であればbabelrcに記載するコンフィグは`data-xxx`属性へ記載します。

```json:babelrc
{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-stage-3"
    ]
}
```

```html
<script type="text/babel" data-presets="env,stage-3">
```

また、Babelは`window.Babel`としてグローバルスコープへ展開されているので、ソースコードを任意のタイミングでトランスパイルすることも可能です。

```js
const src = "(async() => await new Promise(res => res(1)))();";

const transpiled = Babel.transform(src, {
    presets: ["env", "stage-3"]
}).code;
```

なお、通常版は`7.4.0`から`@babel/polyfill`の使用が非推奨となり、代わりにポリフィル内部で使用されていた`core-js`と`regenerator`を直接ロードする方法が推奨となりました。
しかし、この2つは現時点ではバンドル済のコードが存在しないため、スタンドアロン版ではバンドル済の`@babel/polyfill`を使用する必要があります。


# おわりに
御社が1日も早くIEの呪縛から解放されますように...

# SpecialThanks
- [HTMLファイルだけでCDNでBabelをリンクしてES2015/2016/2017を動かす - Qiita](https://qiita.com/standard-software/items/f6c18fa6017fbb218cce)