## 目的 以下の内容から『[[Neovim]]に関係なく、**型安全に[[Nuxt]]の開発をする部分**のみを抽出したスライドを作成する』ために、情報を整理する。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/favicon-64.png" /> <span class="link-card-v2-site-name">Minerva</span> </div> <div class="link-card-v2-title"> 📘完全武装をしてNeovimでも安全で快適なNuxt3の開発をするのだ </div> <div class="link-card-v2-content">NeovimでNuxt3とTypeScriptを型安全かつ快適に開発するための設定方法を解説します。Auto-importsの無効化や未使用importの自動削除、未定義タグの検知、フォールスルー属性対応など、VSCodeやWebStormでは得られないNeovim特有の課題とその解決策を詳しく紹介しています。詳しい手順や設定例は記事でご確認ください。</div> <img class="link-card-v2-image" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/%F0%9F%93%98Articles/attachments/2024-07-26.webp" /> <a data-href="📘完全武装をしてNeovimでも安全で快適なNuxt3の開発をするのだ" class="internal-link"></a> </div> %%[[📘完全武装をしてNeovimでも安全で快適なNuxt3の開発をするのだ]]%% 特に - [[Nuxt4]]を使う - 上記を執筆以降に - 新しく登場した実用的な新機能の導入を検討する - 非推奨となっている箇所のフォロー ### 環境 | 対象 | バージョン | | ------------------------------------------- | ------ | | [[macOS]] | 15.7.2 | | [[Bun]] | 1.3.5 | | [[Neovim]] | 0.11.5 | | [[vtsls]] | 0.3.0 | | [[@vue language-server\|vue_ls]] | 3.2.2 | | [[vue-tsc]] | 3.2.2 | | [[Vue - Official (VSCode)\|Vue - Official]] | 3.2.2 | ``` ├── @fsouza/[email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] └── [email protected] ``` ## プロジェクト作成 ```console toki nuxt nuxt4-sandbox ``` ``` ◇ Templates loaded │ ◇ Which template would you like to use? │ minimal – Minimal setup for Nuxt 4 │ ◇ Creating project in nuxt4-sandbox │ ◇ Downloaded minimal template │ ◇ Which package manager would you like to use? │ bun │ ◇ Initialize git repository? │ Yes │ ◇ Dependencies installed │ ◓ Initializing git repositoryInitialized empty Git repository in /Users/tadashi-aikawa/tmp/nuxt4-sandbox/.git/ ◇ Git repository initialized │ ◇ Would you like to install any of the official modules? │ No │ └ ✨ Nuxt project has been created with the minimal template. ``` 起動確認。 ```console bun dev -o ``` ### 初期ファイル作成 `app/app.vue` ```html <template> <NuxtPage /> </template> ``` `app/pages/top.vue` ```ts <template> <Header1 text="hello!" /> </template> ``` `app/components/Header1.vue` ```html <script setup lang="ts"> defineProps<{ text: string; }>(); </script> <template> <h1 v-text="text"></h1> </template> ``` `package.json` の `scripts` ```json { "scripts": { "build": "nuxt build", "dev": "nuxt dev", "generate": "nuxt generate", "typecheck": "nuxt typecheck", "preview": "nuxt preview", "postinstall": "nuxt prepare" }, } ``` ## 元記事との比較 ### [[Auto-imports (Nuxt)|Auto-imports]]を無効化したい https://nuxt.com/docs/4.x/guide/concepts/auto-imports **今は使ったほうが楽かも。** - ソースコードから型定義をビルドし、それがIDEに認識されるまでは正しく動かない - [[ホットリロード]]していればOK - 最近の[[LSP]]ならリアルタイムに認識してくれる - import不要にもかかわらず、ソースコードによってはimport文が混在する - **パスがついていない方を選択すれば平気** - コードジャンプのときに型定義ファイルを経由する必要がある - v3から直接ジャンプできるようになった - [[📰Vue Language ToolsのGo to Definitionはいつからvueファイルへ直接ジャンプできるようになったか]] #### コンポーネント名がファイル名と異なる点 `components/nest/Hoge.vue` が `<NestHoge>` でしか解決できないのは好きじゃない。解決方法としては 1. `nest/NestHoge.vue` のようにディレクトリと一致するprefixをつける - その場合 `NestNestHoge.vue` にはならない 2. [[Auto-imports (Nuxt)|Auto-imports]]を無効化する #### [[Auto-imports (Nuxt)|Auto-imports]]を無効化する段階 `components/` 以外の無効化。`composables/` や `utils/` などはこれで無効化される。 ```ts export default defineNuxtConfig({ imports: { scan: false, }, }) ``` `components/` も含めた無効化。 ```ts export default defineNuxtConfig({ imports: { scan: false, }, components: { dirs: [], }, }) ``` ### 未使用[[import (ESM)|import]]を削除したい **[[vue-tsc]]が必要。**[[VSCode]]も[[Neovim]]も。 - scriptタグで利用されないモノが消されてしまう問題 - これは大丈夫そうだった - scriptタグでもtemplateタグでも利用されていないものが残ってしまう - [[vue-tsc]]インストールで解決 自動にしたいなら[[ESLint]]や[[Prettier Plugin Organize Imports]]など。 ### 未定義タグ/属性問題にコーディング段階で気づきたい エラーは出ない。[[VSCode]]も。 ```html <template> <Header100 text="hoge" /> <Header1 text="hoge" hyaku="take" /> </template> ``` [[nuxt.config]]に[[vueCompilerOptions.strictTemplates]]を追加する。ここは変わらず。 ```ts export default defineNuxtConfig({ typescript: { tsConfig: { vueCompilerOptions: { strictTemplates: true, }, }, }, }); ``` ちゃんとエラーが出るようになった。もちろんエディタでも。 ```console $ bun typecheck app/pages/top.vue:2:4 - error TS2339: Property 'Header100' does not exist on type '{}'. 2 <Header100 text="hoge" /> ~~~~~~~~~ app/pages/top.vue:3:24 - error TS2353: Object literal may only specify known properties, and 'hyaku' does not exist in type '{ readonly text: string; } & VNodeProps & AllowedComponentProps & ComponentCustomProps'. 3 <Header1 text="hoge" hyaku="take" /> ~~~~~ Found 2 errors. ``` > [!hint] > [[VSCode]]だと `Vue: Restart Vue and TS servers` コマンドが必要。 ### フォールスルー属性を使いたい `id` のように通常の[[HTML]]で使用できる[[属性名 (HTML)|属性名]]を指定してもエラーになってしまう。 ```html <template> <Header1 id="h1" text="hoge" /> </template> ``` ```error app/pages/top.vue:2:12 - error TS2353: Object literal may only specify known properties, and 'id' does not exist in type '{ readonly text: string; } & VNodeProps & All owedComponentProps & ComponentCustomProps'. 2 <Header1 id="h1" text="hoge" /> ``` [[nuxt.config]]で[[vueCompilerOptions.fallthroughAttributes]]を有効にすると、一般的な[[HTML]]属性などはすべて通るようになる。 ```ts export default defineNuxtConfig({ typescript: { tsConfig: { vueCompilerOptions: { strictTemplates: true, fallthroughAttributes: true, }, }, }, }); ``` ```html <Header1 id="ok" class="ok" role="ok" text="ok(Header1のproperty)" unknown="NG" @click="ok" @unknown="NG" /> ``` `data-testid` など正規ではないものは受け付けないが、[[vueCompilerOptions.dataAttributes]] を指定すれば型チェックをスキップできる。 ```ts export default defineNuxtConfig({ typescript: { tsConfig: { vueCompilerOptions: { strictTemplates: true, fallthroughAttributes: true, dataAttributes: ["data-testid"], }, }, }, }); ``` ```html <template> <Header1 data-testid="エラーにならない" /> </template> ``` #### [[キャメルケース]]への変換抑制 以下のケース。 ```html <template> <Header1 aria-atomic /> </template> ``` [[Vue]]としては `aria-atomic` を `ariaAtomic` と認識するはずだが、上記はちゃんと `aria-atomic` として認識される。これは [[vueCompilerOptions.htmlAttributes]] のデフォルト値が `["aria-*"]` であり、[[キャメルケース]]へ変換されないから。 試しに以下のように設定するとエラーになる。 ```ts export default defineNuxtConfig({ typescript: { tsConfig: { vueCompilerOptions: { strictTemplates: true, fallthroughAttributes: true, htmlAttributes: [], }, }, }, }); ``` ## 個人的な意見 ### [[Auto-imports (Nuxt)|Auto-imports]]はフレームワーク機能だけに留める 個人の趣向を含めなければ、どっちでもいい。 ```ts export default defineNuxtConfig({ imports: { scan: false, }, components: { dirs: [], }, }) ``` - **暗黙的**な場合のデメリット - ファイル名や定義が[[TypeScript]]の方針に従っていない - [[React]]との頭の切り替えも大変そう - 個々人の環境で不便が生じそうな気がする - IDE環境や[[Vue Language Tools]]のバグの影響を受けやすくなりそう - 状況によっては不具合の特定が難しくなりそう - **明示的**な場合のデメリット - import挿入と掃除が面倒 - 追加はオートコンプリートでいける - コピペしたコードは面倒だけど... - 掃除は[[ESLint]]や[[Prettier Plugin Organize Imports]]でいける - 構成が変わったときに面倒 - 編集の仕方次第だけど面倒なことには変わりない ### タグや属性は型安全にする これは絶対にやったほうがいい。人間でもAIでも品質が上がる。 ```ts export default defineNuxtConfig({ typescript: { tsConfig: { vueCompilerOptions: { strictTemplates: true, fallthroughAttributes: true, dataAttributes: ["data-testid"], }, }, }, }); ``` - **メリット** - **圧倒的シフトレフト** 1. IDE(静的チェック) `人間はここで気付ける` 2. CLI(静的チェック) `AIはここで気付ける` 3. 実行 `いままではここ` - 具体的には - IDEの段階でエラーに気づける - commit/push前のhooksでエラーに気づける - CIでエラーに気づける - AIが実行権限をもたなくてもエラーに気づける - **デメリット** - ほぼない