.vueファイルのesm.js化及びwebcomponent.esm.js化に成功

## ファイル名の意図
- TodoApp: Exampleファイル。
- TodoApp.vue: 通常のvueファイル
- TodoApp.vue.esm.js: vueファイルをjsに変換したもの。esmodule形式。vueコンポーネントがexportされている。
- TodoApp.vue.shadowmode.esm.js: vueファイルをjsに変換したもの。esmodule形式。vueコンポーネントがexportされている。styleがheadではなくtemplateにinjectされる番(shadowdomのため)。
- TodoApp.esm.js: vueコンポーネントをwebcomponentに変換したもの。esmodule形式。
- todo-app.js: webcomponentをcustomeElement.defineした、そのままタグとして利用できるもの。

## TodoApp.vue -> TodoApp.vue.esm.js

rollup + rollup-plugin-vue

```javascript:rollup.config.js
import VuePlugin from 'rollup-plugin-vue'

export default {
  input: 'TodoApp.vue'
  output: {
    file: 'TodoApp.vue.esm.js',
    format: 'esm'
  },
  plugins: [
    VuePlugin()
  ]
}
```

## TodoApp.vue -> TodoApp.vue.shadowmode.esm.js

webpackでvue-loaderを使えばshadowmode: trueというoptionを使うだけで良いのだが、webpackではesmは作れない。
rollupで頑張る。
今回の記事のキモ。

via: https://github.com/vuejs/rollup-plugin-vue/issues/284#issuecomment-499009722

```javascript:rollup.config.js
import VuePlugin from 'rollup-plugin-vue'

export default {
  input: 'TodoApp.vue'
  output: {
    file: 'TodoApp.vue.shadowmode.esm.js',
    format: 'esm'
  },
  plugins: [
    VuePlugin({
      normalizer: '~./normalize-component.js'
    })
  ]
}
```

```javascript:normalize-component.js
'use strict';
function createInjectorShadow(shadow) {
    return (inject, { source, map, media }) => {
        const style = document.createElement('style');
        style.appendChild(document.createTextNode(source));
        shadow.appendChild(style);
    };
}

function normalizeComponent(
    template,
    style,
    script,
    scopeId,
    isFunctionalTemplate,
) {
    var options = typeof script === 'function' ? script.options : script; // render functions

    if (template && template.render) {
        options.render = template.render;
        options.staticRenderFns = template.staticRenderFns;
        options._compiled = true; // functional template

        if (isFunctionalTemplate) {
            options.functional = true;
        }
    } // scopedId

    if (scopeId) {
        options._scopeId = scopeId;
    }

    var hook;

    if (style) {
        hook = function() {
            style.call(
                this,
                createInjectorShadow(this.$root.$options.shadowRoot),
            );
        };
    }

    if (hook) {
        if (options.functional) {
            // register for functional component in vue file
            var originalRender = options.render;

            options.render = function renderWithStyleInjection(h, context) {
                hook.call(context);
                return originalRender(h, context);
            };
        } else {
            // inject component registration as beforeCreate hook
            var existing = options.beforeCreate;
            options.beforeCreate = existing
                ? [].concat(existing, hook)
                : [hook];
        }
    }

    return script;
}

export default normalizeComponent;
```

## TodoApp.vue.shadowmode.esm.js -> TodoApp.esm.js

これは単にファイルを作るだけで、rollupは使わない。

```javascript:TodoApp.esm.js
import Vue from 'vue/dist/vue.esm.js'
import wrap from '@vue/web-component-wrapper'
import TodoAppVueComponent from './TodoApp.vue.js'

const TodoApp = wrap(Vue, TodoAppVueComponent)

export default TodoApp
```

## TodoApp.esm.js -> todo-app.js
これは単にファイルを作るだけで、rollupは使わない。

```javascript:todo-app.js
import TodoApp from './TodoApp.js'

window.customElements.define('todo-app', TodoApp);
```

---

## 使用方法

ここで作ったすべてのファイルは、通常のフロントエンドnodeパッケージと同様に、そのままwebpackなどに読み込ませて使えるはず。