【TypeScript】importの記述を不要にするunplugin-auto-import
JavaScript/TypeScriptで開発していると、よく使うモジュールを何度もimport
する必要が出てきます。
// これを何度も書く必要がある
import { useState } from 'react'
エディターが自動で補完してくれる場合も多いですが、何回も同じことを繰り返すのはDRYではありません。
そこで本記事ではこのimport文の記述を不要にするunplugin-auto-importについて使い方と簡単な仕組みをご紹介します!
環境
- unplugin-auto-import: 0.7.1
- TypeScript: 4.6.3
unplugin-auto-importとは?
unplugin-auto-importは事前に設定しておいたモジュールのimport文を不要にするJavaScriptのビルドツールプラグインです。Vue界隈やvitest、slidevの開発で有名なAnthony Fuさんによって開発されています。
実際の例を見てみましょう。
Reactでよく使うuseState
をunplugin-auto-importを使って書くと、useState部分の記述が不要になります。
// import { useState } from 'react' ← この記述が不要に 🙌
export function Counter() {
const [count, setCount] = useState(0)
return <div>{ count }</div>
}
また、unplugin-auto-importはunpluingの一種で、Vite、Rollup、Webpack、esbuildなど様々なビルドツールで利用することができるのも嬉しいポイントです。
使い方
基本的な使い方を最小のプロジェクトで確認してみます。
事前準備
事前にプロジェクトを作成しておきます。
yarn create vite
yarn create v1.22.17
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Installed "create-vite@2.9.1" with binaries:
- create-vite
- cva
✔ Project name: … auto-import-sample
✔ Select a framework: › react
✔ Select a variant: › react-ts
Scaffolding project in /Users/matsumoto.kazuya/lab/auto-import-sample...
Done. Now run:
cd auto-import-sample
yarn
yarn dev
✨ Done in 9.86s.
以下のコマンドを入力して、ビルドが通って画面が表示されることを確認しておきましょう。
cd auto-import-sample
yarn
yarn dev
自動生成されたApp.tsx
の冒頭で、useStateが使われているのでunpluing-auto-import
を使って、この記述を不要にしていきます。
import { useState } from 'react' // この行を書かなくていいようにしていく
当たり前ですが、この時点ではimport行をコメントアウトすると、コンパイルエラーになります。
インストール
-
unplugin-auto-import
をプロジェクトにインストールします。yarn add -D unplugin-auto-import
-
vite.config.tsのpluginsにAutoImportを追加します。
import vue from '@vitejs/plugin-vue' import AutoImport from 'unplugin-auto-import/vite' import { defineConfig } from 'vite' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), AutoImport({/* ここに設定を書く */}) ] })
以上で利用する準備ができました!
設定を書く
続いて、プラグインの設定を書いていきます。
AutoImport({
include: [/\.[tj]sx?$/], // .ts, .tsx, .js, .jsx
imports: ['react'],
dts: './src/auto-imports.d.ts'
})
include
AutoImportの変換対象のファイルの拡張子を正規表現で指定します。
/\.[tj]sx?$/
がわかりにくと感じる方は
[/\.ts$/, /\.tsx$/, /\.js$/, /\.jsx$/]
のように書くと長くはなりますが、読みやすくなります👌
imports
グローバルにimportするモジュールを指定します。
ライブラリ側でvue
やreact
など、いくつかpresetsを用意してくれており、今回はreact
のpresetsを使用しました。
個別にimportすることも可能です。
imports: [
// presets
'react',
// カスタム
{
'@vueuse/core': [
// named importsを使いたい場合
'useMouse', // import { useMouse } from '@vueuse/core',
// aliasを使いたい場合
['useFetch', 'useMyFetch'], // import { useFetch as useMyFetch } from '@vueuse/core',
],
'axios': [
// defaultを使いたい場合
['default', 'axios'], // import { default as axios } from 'axios',
],
'[package-name]': [
'[import-names]',
// alias
['[from]', '[alias]'],
],
},
]
dts
global declareで型を定義した .d.ts
ファイルの出力先を指定します。
大概の場合はtsconfig.jsonの"include": ["src"]
に合わせて、以下の記述になることが多いかと思います。
dts: './src/auto-imports.d.ts'
動作を確認
一通り設定できたので、再度ビルドしてみましょう。
useStateをコメントアウトしても、ビルドが通るようになりました🎉
仕組み
仕組みが全くわからないまま使うのも気持ち悪いので、仕組みについても簡単に説明します。
プラグインに渡されたオプションはソースコードと共にcreateUnpluginの関数に渡され、transform関数でコードの変換処理が行われます。
transformの中では以下の2つの処理を行っています。
- transform(core/transform)でソースコードを変換
- generateConfigFiles()で.d.tsファイルを生成
export default createUnplugin<Options>((options) => { // オプションを引数で受けとる
...
return {
...
async transform(code, id) {
// 1. ソースコードを変換
const res = await transform(code, id, resolved)
if (res)
// 2. .d.tsファイルを生成
generateConfigFiles()
return res
}
...
}
})
transform(core/transform)でソースコードを変換
core/transform関数でソースコードの変換を行います。
この中でimportsのオプションを読み取って、ビルド後のソースにimport文を差し込んでいることがわかります。
export async function transform(
code: string,
id: string,
{
imports,
sourceMap,
resolvers,
resolvedImports = {},
ignore = [],
}: TransformOptions,
) {
// modulesのRecord
const modules: Record<string, ImportInfo[]> = {}
for (const name of Array.from(identifiers)) {
// importsの情報からmodulesのレコードを生成
let info = getOwn(resolvedImports, name) || getOwn(imports, name)
...
addToModules({
from: info.from,
name: info.name,
as: name,
})
...
}
// modulesからimport文を作成
const importStatements = Object.entries(modules)
....
// コードの頭にimprot文を差し込む
// ex) import { useState } from 'react'
const s = new MagicString(code)
s.prependLeft(0, importStatements)
return {
code: s.toString(),
...
}
}
先ほどのreactの例だとビルド後のソースの頭にimport { useState } from 'react'
が追加されます。
import { useState } from 'react';import "./App.css"; // ←この行が自動で追加される
import logo from "./logo.svg";
import { jsx as _jsx } from "react/jsx-runtime";
....
generateConfigFiles()で.d.tsファイルを生成
generateConfigFilesではimportsのオプションから動的に型宣言ファイルを生成します。
import type { ImportsFlatMap } from '../types'
export function generateDeclaration(imports: ImportsFlatMap, resolvedImports: ImportsFlatMap = {}) {
const body = [
...Object.entries(imports),
...Object.entries(resolvedImports),
]
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([name, info]) => ` const ${name}: typeof import('${info.from}')${info.name !== '*' ? `['${info.name || name}']` : ''}`)
.join('\n')
return `// Generated by 'unplugin-auto-import'\n// We suggest you to commit this file into source control\ndeclare global {\n${body}\n}\nexport {}\n`
}
結果としてimportsに定義したモジュールを declare global
で宣言したauto-imports.d.tsが生成され、importなしで、グローバルに型が利用できるようになります。
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
declare global {
...
const useState: typeof import('react')['useState']
...
}
export {}
まとめ
import文を不要にするunplugin-auto-import
についてご紹介しました。
実際プロジェクトに導入してみると、長いimport文が消えてかなりコードの見通しがよくなり、予想以上にDXが向上しました!
ぜひ開発中のプロジェクトで試してみてください🔥
Discussion