Nuxt3のAuto-imports機能 (components/composables) を拡張する
はじめに
Nuxt3 には Auto-imports 機能というありがたい機能があります。/components
、/composables
直下のファイルや Vue3 API であるref
、computed
などが import 不要で利用でき非常に便利です。
下記記事では「Pinia で Auto-imports できないか?」という点で調査を行いました。
本記事では機能のベースになっているunjs/unimportの README も見ながらcomponents
、imports
オプションを拡張していきます。
実際の利用ケースを考えながら拡張していきます。
対象ユーザー
-
nuxt.config.ts
のcomponents
、imports
オプションを拡張したい人 - Vitest、Storybook で利用するunplugin-auto-importの設定を拡張したい人
- unjs/unimportを眺めたい人
本記事はタイトルに Nuxt3 と書いてますが半分くらいはunjs/unimport に関する内容です。そのため、nuxt.config.ts
のみならずvite.config.ts
、vitest.config.ts
を拡張したいケースでも利用できるかと思います。
Nuxt3 の Auto-imports とは
Auto-imports 機能の説明は公式ドキュメントが一番分かりやすいです。まずはご一読頂けると良いかと思います(この記事を眺めてる時点で n 回は読んでる方だと思いますが)。
components 設定を拡張する
下記で紹介しない設定値、詳細設定については公式ドキュメント、公式 GitHub を参照ください。
デフォルトで利用できるもの
デフォルトで以下が設定されています。
-
/components
ディレクトリの Auto-imports -
/components
ディレクトリの Nested scan
例えば、実際の利用ケースでは下記のようなネストしたディレクトリになるかと思います。
| components/
--| base/
----| foo/
------| Button.vue
--| ui/
----| Input.vue
これは Template で呼び出す際に以下のような命名になります。
<template>
<div>
<BaseFooButton />
<UiInput />
</div>
</template>
case1: pathPrefix を無効にする
デフォルトの pathPrefix の付け方は非常に便利ではあるものの、ネストが深くなった時に可読性が下がる要因になります。
例えば、以下のようなネスト構造です。
| components/
--| product1/
----| base/
------| buttons
--------| BaseButton.vue
この場合非常に長い命名となります。非常に煩雑な名前となっており不必要な情報が混じっています。
<template>
<!-- Product1 や Buttons は含めなくても良い -->
<!-- Baseが重複している -->
<Product1BaseButtonsBaseButton />
</template>
本件の解決策はCustom directoriesに記載されています。
export default defineNuxtConfig({
components: [
{
path: "~/components",
+ pathPrefix: false
},
],
});
利用イメージは以下のようになります。
<template>
<!-- Auto-imports /components/product1/base/buttons/BaseButton.vue -->
<BaseButton />
</template>
case2: 任意の pathPrefix を設定する
case1 の設定では全てのpathPrefix
を無効にしました。case2 では任意の pathPrefix を設定したい場合を想定していきます。
例えば、以下のように設定したい場合です。
<template>
<!-- Auto-imports /components/ui/button.vue -->
<UiButton />
</template>
v0.devやshadcn/uiでは@/components/ui/*
というディレクトリ構造をしており、コピペしたコンポーネント / プロダクト固有のコンポーネントを分けたい というユースケースは徐々に増えていくと思われます。
export default defineNuxtConfig({
components: [
{
// eg: /components/ui/Button.vue -> <UiButton />
// eg: /components/ui/Select/index.vue -> <UiSelect />
path: "~/components/ui",
+ prefix: "Ui"
},
],
});
本設定方法はshadcn-vueの Nuxt チュートリアルにも書かれています。
case3: 特定のファイルだけを含めたい
components
オプションで設定した内容は.nuxt/components.d.ts
ファイルにエクスポートされます。
以下のようなディレクトリ構造の場合、コンポーネントではないtype.ts
やprops.ts
、起動時に Nuxt Config を経由しないindex.stories.ts
は含めたくないですよね。
| components/
--| ui/
----| button/
------| index.vue
------| type.ts
------| props.ts
------| index.stories.ts : Story ファイル
------| schema.ts : zod / yup スキーマ
コンポーネントではないtype.ts
やprops.ts
は.nuxt/imports.d.ts
ファイルから参照する方が責務的に自然だと思います。そこでextensions
オプションを用いて.vue
ファイルのみを含めるようにします。
export default defineNuxtConfig({
components: [
{
path: "~/components/ui",
+ extensions: [".vue"],
},
],
});
公式ドキュメントではComponent extensionsに記載があります。
case4: 特定のファイルを除外したい
case3 の場合、Storybook の*.stories.ts
は除外できたものの、Histoire の*.story.vue
は除外できません。本件は公式ドキュメントに解決方法が記載されていなかったため、unimportの docs を眺めてみます。
するとignore
というオプションが確認できました。
The ignore option is used to filter out the exports, it can be a string, regex or a function that returns a boolean.
こちらを用いると除外できるようですね。
export default defineNuxtConfig({
components: [
{
path: "~/components/ui",
extensions: [".vue"],
+ ignore: ["**/*.story.vue"],
},
],
});
(そもそもignore
オプションだけでも良さそう)。
export default defineNuxtConfig({
components: [
{
path: "~/components/ui",
- extensions: [".vue"],
+ ignore: ["**/*.story.vue", "**/*.stories.ts", "**/*.ts"],
},
],
});
imports 設定を拡張する
こちらについても下記で紹介しない設定値、詳細設定については公式ドキュメント、公式 GitHub を参照ください。
デフォルトで利用できるもの
以下はデフォルトで設定されている内容です。
-
/composables
ディレクトリの Auto-imports -
/utils
ディレクトリの Auto-imports
case1: Built-in Presets 導入
Nuxt3 の Auto-imports は unjs/unimport をベースにしているため unjs/unimport が提供する presets を利用できます。
export default defineNuxtConfig({
imports: {
+ presets: ["@vueuse/core", "pinia"],
},
});
case2: Custom Presets
では、ライブラリ側で提供していないものはどうすればいいのか。よく利用するものだとzod
、yup
などですかね。
こちらについては Nuxt3 公式のAuto-import from third-party packagesに明記されています。
export default defineNuxtConfig({
imports: {
+ presets: [
+ {
+ from: "zod",
+ imports: ["string", "object"],
+ }
+ ],
},
});
unimport 側はこちらです。
case3: 特定ディレクトリを含める
Pinia/Vuex を利用する場合は/stores
、スキーマや type ファイルを 1 箇所でまとめたい場合は/schemas
、/types
など、ディレクトリレイヤーを増やす運用ケースがあるかと思います。しかし、悲しいことにこのディレクトリが Auto Imports されることはありません。
特定ディレクトリを含めたい場合imports.dirs
オプションを利用します。
export default defineNuxtConfig({
imports: {
+ dirs: ["stores", "schemas"],
},
});
Nuxt3 公式の/composables#How Files Are Scannedに記載があります。
case4: 特定ファイルを含める
case3 のケースはディレクトリを増やす場合のユースケースでした。しかし、プロダクトによっては関心ごとを 1 つのディレクトリにまとめることがあると思います。例えば、components 設定 case3: 特定のファイルだけを含めたいのようなケースです。
| components/
--| ui/
----| button/
------| index.vue
------| type.ts
------| props.ts
------| schema.ts : zod / yup スキーマ
components 設定 case3: 特定のファイルだけを含めたいの設定済みであれば components/ui/button/index.vue
は.nuxt/components.d.ts
に export 済みです。本ケースで対象にしたいのはそれ以外のtype.ts
、props.ts
、schema.ts
などです。
本ケースは下記の設定で大丈夫そうでした。
export default defineNuxtConfig({
imports: {
dirs: [
"stores",
"schemas",
+ "components/ui/*"
],
},
});
「なぜこれでいけるんだろう?」と思い unimport の GitHub を眺めました。するとScanDirExportsOptions["filePatterns"]
というオプションを見つけました。auto-import のスキャン対象として*.{ts,js,mjs,cjs,mts,cts}
がデフォルト定義されていました。これのおかげですね。
Q. Vitest や Storybook で利用する場合はどうすればいいのか?
本設定はnuxt.config.ts
に設定しており Vitest や Storybook 実行時には反映されません。
解決策として挙げられるのはunplugin/unplugin-auto-importを用いてvite.config.ts
、vitest.config.ts
を拡張することです(Nuxt Config を流用できる方法あれば知りたい)。
/// <reference types="vitest" />
import Vue from "@vitejs/plugin-vue";
import AutoImport from "unplugin-auto-import/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
Vue(),
AutoImport({
imports: [
"vue",
"vue-router",
"pinia",
"vue-i18n",
{ from: "zod", imports: ["string", "object"] },
],
dirs: ["src/stores", "src/schemas"],
dts: "src/.nuxt/auto-imports.d.ts",
}),
],
});
おわりに
本記事では Nuxt3 の Auto-imports 設定、ベースとなっている unjs/unimportについて調べてみました。
Auto-imports 機能の良いところとして
- 人によってインポートの仕方・順番が違う
- インポート漏れをレビューで指摘された
などの本質でない議論・指摘を避けられる点があると思ってます。
本質的ではない点はリンター、フォーマッター、ジェネレーターなど機械的に処理し、本質的な部分に注力して行けたら良いですね。
余談
今回調べてて面白かったのはisland
オプションがあったことです。コンポーネント単位でアイランドアーキテクチャの可否を設定できたりするんですかね?
Nuxt におけるアイランドアーキテクチャは Experimental feature のため今後の展開が楽しみです(早く使いたい)。
Discussion
{vite,vitest}.config.{ts,mts}
においてsrc/
の重複が煩わしい場合、resolverを用意しても良いかと思います。// case1: node:path 利用
// case2: nuxt/kitのcreateResolver 利用
createResolver() は内部で
pathe
を利用してます。