Vite 2 + TypeScript で @/ のようなパスエイリアスを使いたい

**注意**: ~~vite 2 はまだ beta版なので，~~[公式ページ](https://vitejs.dev) にて最新の情報をチェックしてください．
記事作成時点での最新バージョンは `2.0.0-beta.50` です．
記事更新時点での最新バージョンは `2.1.3` です．

## 概要

1. Vite 入門
    - [Vite とは](#vite-とは)
    - [Vite init](#vite-init)
        - [VSCode の設定](#vscode-の設定)
        - [TypeScript の設定](#typescript-の設定)
2. **Vite 2** + **TypeScript** で `import '@/hoge'` のようなパスエイリアスを使いたい
    - [パスエイリアスの設定](#パスエイリアスの設定) 

## Vite とは

Vue.js 作者の Evan You が夜中に思いついたアイデアをうっかり朝までに完成させてしまったフロントエンドビルドツール[^1]
[^1]: Vue に特化しているが，React や Svelte などでも使える（そして速い）

<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">As I was going to bed, I had an idea about a no-bundler dev setup (using native browser ES imports), but with support for Vue SFCs **with hot reload**. Now it&#39;s almost 6AM and I have PoC working. The hot reload is so fast it&#39;s near instant.</p>&mdash; Evan You (@youyuxi) <a href="https://twitter.com/youyuxi/status/1252173663199277058?ref_src=twsrc%5Etfw">2020年4月20日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ホットリロード（HMR）がめちゃくちゃ速いことで有名で，ついでにビルドも esbuild 並に高速[^2][^3]（というかビルドは内部で ~~esbuild~~ rollup を使っているのでそれはそう）

[^2]: esbuild は webpack の 100倍とか 200倍速いと言われている．実測は 数10倍程度であることが知られているが，まあ速い
[^3]: 型チェック機能がないので，それを含めると vue-cli と同じくらい．手元の環境だと，vuedx-typecheck が 10秒，ビルドが 3秒程度だった．vue-cli だと 12秒程度．

他にも，Lazy Components のコード分割にデフォルトで対応していたり，vue-cli に代わって「Vue ならとりあえずこれ」感が出てきています．
それだけでなく，React もこれでいいかもしれません．


Vite を使う以前は vue-cli に不満などありませんでしたが，一度 Vite の HMR を体感してしまった今となっては，webpack には戻れないと感じています．

公式サイト：<https://vitejs.dev>

## Vite init

まずプロジェクトテンプレート[^vite-app-var] のインストールです

[^vite-app-var]: この記事では Vue を使いますが、React / preact や Svelte のテンプレートもあります．

```shell
npm init @vitejs/app --template vue-ts  your-project-name

# Or

yarn create @vitejs/app --template vue-ts your-project-name
```

### VSCode の設定

拡張機能を入れます

- ~~Vetur~~
    - ~~<https://marketplace.visualstudio.com/items?itemName=octref.vetur>~~
- ~~VueDX~~
    - ~~typecheck とかしてくれる~~
    - ~~<https://marketplace.visualstudio.com/items?itemName=znck.vue-language-features>~~

**(2021年3月29日 編集)** VueDX が使われなくなり（[ac04ebe](https://github.com/vitejs/vite/commit/ac04ebeed10ee09b8f0464d9f0fd4e9ae6913d95#diff-4f8dc21b7fd659a1525795444753eba0b63c7fc434c0d0f0a4cd28032e8d2082)）型チェックには代わりに vue-tsc を用いるようになっていました（[b3b3c01](https://github.com/vitejs/vite/commit/b3b3c017d90ac86ba8d02deb93dec443a4fa4bad#diff-4f8dc21b7fd659a1525795444753eba0b63c7fc434c0d0f0a4cd28032e8d2082)）
またそれに伴い，vue-tsc がデフォルトの TypeScript server になっている VSCode 拡張 [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) が Vetur と並んで推奨されるようになりました（[79dd32c](https://github.com/vitejs/vite/commit/79dd32cbb61b2aeedfd6b1081867a98cc7c73a68#diff-62e86c9dd3d99306d5f8f4f75ef717159487d6017c0c0ca27577d4eba6d35fd8)）

Volar は Vetur より軽快な感じがしますが，vue-tsc の型チェックはおよそ 10秒ほどで特に vuedx-typecheck との差は感じませんでした．

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Vite+Vue3 でやるとき、VueDX+tsc+Vetur だと shims-vue.d.ts が必要だったのですが、vue-tsc+Volar だと不要になってやや嬉しいです。<br>ただし、Vetur のフォーマットが使えなくなるので ESLint だけでなく Prettier を追加する必要があるかもです。</p>&mdash; u-sho (@u_sho_engineer) <a href="https://twitter.com/u_sho_engineer/status/1375932873778208770?ref_src=twsrc%5Etfw">March 27, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

```diff_json:.vscode/extensions.json
 {
   "recommendations": [
-    "octref.vetur",
-    "znck.vue-language-features"
+    "johnsoncodehk.volar"
   ],
+  "unwantedRecommendations": [
+    "octref.vetur",
+    "znck.vue-language-features"
+  ]  
 }
```

### TypeScript の設定

`tsconfig.json` を少しだけ書き換えます．

特に `esModuleInterop` は設定推奨です．React を使うとき特に便利です．
（参考：<https://www.typescriptlang.org/ja/tsconfig#esModuleInterop>）


```diff_json:tsconfig.json
 {
   "compilerOptions": {
     "target": "esnext",
     "module": "esnext",
     "moduleResolution": "node",
+    "esModuleInterop": true,
+    "resolveJsonModule": true,
+    "newLine": "lf",
     "strict": true,
     "jsx": "preserve",
     "sourceMap": true,
     "lib": ["esnext", "dom"],
     "types": ["vite/client"],
-    "plugins": [{ "name": "@vuedx/typescript-plugin-vue" }]
   },
   "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
+  "exclude": ["node_modules"]
 }
```

## パスエイリアスの設定

本題．

`@/` から始まるパスを `./src/` のエイリアスとして設定する

```typescript:つまり
import HelloWorld from '../../components/HelloWorld.vue'
// って書くより
import HelloWorld from '@/components/HelloWorld.vue'
// って書きたい！
```

まず TypeScript の方にエイリアスを設定します．
（参考：<https://www.typescriptlang.org/ja/tsconfig#paths>）

これで，`*.vue`（や `*.tsx`）ファイルではエイリアスが効くようになります．

```diff_json:tsconfig.json
 {
   "compilerOptions": {
-    "root": true,
+    "baseUrl": "./",
+    "paths": {
+      "@/*": ["src/*"],
+      // 追加するならこんな感じ。@ はじまりでなくても良い
+      // "c/*": ["src/components/*"]
+    },
     "target": "esnext",
     "module": "esnext",
```

次に Vite の方に設定します．
（参考：<https://vitejs.dev/config/#alias>）

これで `src/main.ts` などでもエイリアスが効くようになります (\*' '*)

```diff_typescript:vite.config.ts
 import { defineConfig } from 'vite'
 import vue from '@vitejs/plugin-vue'
 
 export default defineConfig({
+  alias: [
+    {
+      find: '@/',
+      replacement: `./src/`
+    },
+    // {
+    //   find: 'c/',
+    //   replacement: `./src/components/`
+    // }
+  ],
   plugins: [vue()]
 })
```

### おまけ（ESLint ルールで矯正する）

せっかく `@/*` で書けるようになったのに，それに気づかずに相対パスで書いてしまう人もいるかもしれません．ESLint ルールでパスエイリアスを使うように強制（矯正）してしまいましょう．


まずは ESLint のインストールです．
以下のコマンドで設定まで含めてインタラクティブにやってくれます．

```shell:まずはeslintをインストール
npm i -D eslint
# Or
# yarn add -D eslint
npx eslint --init
# Or
# yarn eslint --init
```

そして，無限ループ等が起こらないように `ignorePatterns` を設定します．
（参考：[nuxt-ts のビルドが終わらない or 重い - Qiita](https://qiita.com/u-sho/items/27a7d200211e192f49b9) @u-sho ）

```diff_json:.eslintrc.json
 {
+  "ignorePatterns": [
+    ".git",
+    "node_modules",
+    "dist",
+    "package-lock.json",
+    "yarn.lock.json"
+  ],
+
   "root": "true",
   "parser": "vue-eslint-parser",
```

続いてルールの設定です．
（参考：<https://eslint.org/docs/rules/no-restricted-imports>）

```diff_json:.eslintrc.json
   "extends": [
     "eslint:recommended",
     "plugin:vue/vue3-recommended",
     "@vue/typescript"
   ],
   "rules": {
+    "no-restricted-imports": [
+      "error",
+      {
+        "patterns": [
+          "../*",
+          "./assets/*",
+          "./components/*",
+          // "@/components/*",
+          "./pages/*"
+        ]
+      }
+    ],
```

これで

```typescript
import HelloWorld from '../../components/HelloWorld.vue'
```

はエラーを吐くようになりました．


## まとめ

以下をよく読みましょう．

- TSConfig リファレンス
    - <https://www.typescriptlang.org/ja/tsconfig>
- Vite2 Config
    - <https://vitejs.dev/config>
- ESLint rule `no-restricted-imports`
    - <https://eslint.org/docs/rules/no-restricted-imports>