3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GameWithAdvent Calendar 2022

Day 14

SFC と TS モジュールの import 文を省略してコードを減らす

Last updated at Posted at 2022-12-14

前提条件

  • VueCLI v5.0.8
  • TypeScript v4.8.1
  • Vue v2.7
  • unplugin-auto-import v0.11.2
  • unplugin-vue-components v0.22.9

import 文省略

.vue や、 .tsimport 文を省略できるようにしていきます。

.ts は、 import 文なしで参照できるようになります。

参照されたモジュールは、ビルド時にオンデマンドで自動インポートされます。

.tsimport 文省略の方法は、こちらunplugin-auto-import を利用した説明をしています。

SFC

.vue は、 <template> 内で記述された場合にオンデマンドで自動インポートされます。

.vue の自動インポートは、 unplugin-vue-components で行えるようになります。

まずは、 unplugin-vue-components をインストールします。

pnpm add -D unplugin-vue-components

unplugin-vue-components で SFC の自動インポートを有効にするには、 vue.config.js の中で webpack の設定に以下を追加します。

module.exports = {
  configureWebpack: {
    plugins: [
      require("unplugin-vue-components/webpack")({
        dirs: ["src/packages"], // コンポーネントを探索するディレクトリ
        deep: true, // サブディレクトリを探索
        extensions: ["vue"], // コンポーネントの拡張子
        globs: ["src/packages/**/!(temp)/*.vue"], // glob パターンを用いて自動インポートの対象から外すことが可能 
        dts: "src/components.d.ts", // volar を使っていれば、 TypeScript のサポートを受けれれます
        version: 2.7, // Vue.js のバージョン
      }),
    ],
  },
};

dts オプションで指定したファイルに SFC の定義がグローバル空間に追加されます。

tsconfiginclude で指定した範囲内に出力されるようにします。

VSCode で volar をインストール済みであれば、 <template> 内で、コンポーネントの補完が有効になり便利です。

また、 props の型サポートも有効になります。

型定義の import 文省略

unplugin-auto-import では、 型エイリアス export type や インタフェース export interface は、 import 文を省略できません。

そのため、 export されている 型定義をグローバル定義にして import 文を省略できるようにします。

src/global-types.d.ts
import * as packages from "@/packages";

declare global {
 // グローバル型定義を追記していく
 type SampleType = pacakges.SampleType,
}

index.tsexport を集約でていないと、 import 文を都度追記して代入する必要があるので、手間がかかりすぎてしまいます。

サブディレクトリも含めてすべての export を集約できるように index.ts を自動生成しておきます。

こうしておくことで、 packages ディレクトリ内の全ての型定義を参照できるようなっています。

index.ts を再帰的に自動生成するには、 ctix を使います。

export するモジュールの命名は、ファイル名をプレフィックスにするなど、グローバルネームスペースで衝突を避ける工夫が必要です。

index.ts は、バンドラーでは全てのファイルがバンドルされてビルド後のファイル容量が大きくなってしまいます。

しかし、ここでは型定義を集めているだけなのでバンドルサイズは影響を受けません。

ESLint ルール

型定義をグローバル定義すると import 文を省略してもビルドは通るようになりますが、 ESLint を導入済みであれば no-undef エラーが検出されてしまいます。

エラーを防ぐため、 import 文を省略した型定義は、グローバル定義済みであることを ESLint ルールに追加する必要があります。

.eslintrc-global-types.json
{
  "globals": {
    "SampleType": true
  }
}

.eslintrc.js から .eslintrc-global-types.json のルールを extens します。

.eslintrc.js
module.exports = {
  extends: [
    ".eslintrc-global-types",
  ]
}

ESLint ルール .eslintrc-global-types.json を用意する手間を省くため、グローバル型定義ファイルsrc/global-types.d.ts から生成させるスクリプトを実装しました。

tools/generate-eslint-rule-global-types.mjs
import { $ } from "zx";
import fire from "js-fire";
import { render } from "template-file";

const root = process.cwd();

const template = `{
  "globals": {
    {{typesJson}}
  }
}`;

const generateEslintRuleGlobalTypes = {
  __description__: "📦  global-types.d.ts から ESLint ルールのグローバル型を生成する",
  execute: async () => {
    const globalTypesFile = await $`cat ${root}/src/global-types.d.ts`;
    const globalTypes = globalTypesFile.stdout.split("\n")
      .map((line) => line.trim())
      .filter((line) => line.length > 0 && line.match(/^type\s/) !== null)
      .map((line) => {
        //型名を取得
        return line.split(" ")[1];
      })
      .map((line) => {
        // ジェネリスク型を除外;
        return line.replace(/<.*>/, "").replace(/<.*/, "");
      });
    const typesJson = globalTypes.map((line) => {
      return `"${line}": true`;
    }).join(",\n    ");
    const result = await render(template, { typesJson });
    await $`echo ${result} > ${root}/.eslintrc-global-types.json`;
  },
};

fire(generateEslintRuleGlobalTypes);

以上のスクリプトを npm scripts から実行できるようにしておきます。

package.json
  "scripts": {
    "gen-eslint-globals": "node tools/generate-eslint-rule-global-types.mjs execute"
  }

新たに import 文を省略したい型がある場合は、以下の手順で省略可能にします。

  1. src/global-types.d.ts に型定義を追加。
  2. $ pnpm gen-eslint-globals を実行。
  3. .eslintrc-global-types.json に ESLint ルールが追加されて no-undef エラー回避。

まとめ

export しているモジュールは、 import 文を省略しつつコード補完が効いて実装が快適になったので概ね良かったと思います。

ローカルコンポーネントの SFC は使用時に import 文が不要になり、 .ts ファイルは export されているモジュールや型定義の import 文を省略することができました。

今回は SFC や、 .ts ファイルが合計で 9万行程度のプロジェクト内で import 文を省略し、合計で 3,000行のimport 文を省略させることができました。

コードを減らせるということはレビューコストが減ると思います。

また、import 文が省略され自動インポートとなるので、誤って粒度が大きい index.ts をインポートしてしまいバンドルファイルサイズが増えるリスクも少なくなります。

注意しなければいけないのは、 export するモジュールがグローバル定義となることです。

プロジェクト内のディレクトリ階層はあまり深くせず、 named export の命名はファイル名をプレフィックスにするなど、コンフリクトしないような命名規則にしておく必要があります。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?