Rails + webpacker + Vue.js + TypeScript で `export 'default' (imported as 'mod') was not found`

Railswebpacker gem を使って、Vue.js with TypeScript な環境を作った中で長くハマった部分があったのでご紹介したい。

ハマったところ

webpacker で vue と typescript を初期設定して、Vue.js の SFC (Single File Component) で開発していた。
webpacker でコンパイルするときに、export 'default' (imported as 'mod') was not found というエラーが起きた。

結論としては、 vue-loader が ts-loader を使いたいが、ts-loader 側で .vue ファイルを受け入れる設定になっていなかった(みたいな感じ)。

初期設定の再現

$ gem install webpacker
$ rake webpacker:install
$ rake webpacker:install:typescript
$ rake webpacker:install:vue

解決法

webpacker が生成してくれる config/webpack/loaders/typescript.js を以下のように変更する。

Before

const PnpWebpackPlugin = require('pnp-webpack-plugin')

module.exports = {
  test: /\.(ts|tsx)?(\.erb)?$/,
  use: [
    {
      loader: 'ts-loader',
      options: PnpWebpackPlugin.tsLoaderOptions()
    }
  ]
}

After

const PnpWebpackPlugin = require('pnp-webpack-plugin')

module.exports = {
  test: /\.(ts|tsx)?(\.erb)?$/,
  use: [
    {
      loader: 'ts-loader',
      options: PnpWebpackPlugin.tsLoaderOptions({
        appendTsSuffixTo: [/\.vue$/]
      })
    }
  ]
}

これを調べるのに vue-loader のソースを結構読んだ。

vue-loader は Vue.js の SFC 単一ファイルコンポーネントコンパイルするために他のコンパイラ(xx-loader)へ橋渡しする役目を担ってるのかなとわかった。

SFC では HTML を書く template と JSで実装する scriptCSS を書く style と3つの領域それぞれで他の言語を使うことができる。

たとえば、template で pug を使ったり、
script で TypeScript を使ったり、
style で SCSS や SASS を使うことができる。

<template lang="pug">
</template>
<script lang="ts">
</script>
<style lang="scss">
</style>

それぞれコンパイラが違うのでそれを vue-loader さんは

  • "template は pug-loader!!"
  • "script は ts-loader!!"
  • "style は scss-loader!!"

と振り分けてくれている。

実は vue-loader のドキュメント Vue Component の仕様 を熟読するとそういうことが書いてある。

Vue Component の仕様 · vue-loader


参考資料: