homehome

importのエイリアスとソートを設定する

Published

TocMenu目次

Reactではimport文を多用します。これは機能ごとにファイルを分けているため、メンテンナスしやすいコードを書いている証拠です。しかし、インポート自体はごちゃこちゃしてメンテナンスしづらいのも事実です。特に以下のケースが考えられます。

⚡ネスト地獄

インポートするコンポーネントの階層構造が深いと、メンテナンスが悪くなります。例えば、ファイルの物理的な位置を変更した場合に手直しが必要になります。

// ネストが深い
import someComponent from "../../../../components/someComponent";

// ファイルの位置を1つ上に移動すると手直しが必要になる
import someComponent from "../../../components/someComponent";

// こうなれば楽なのに...
import someComponent from "~components/someComponent"

⚡ファイルごとにインポートの順番がバラバラ

自分の中でインポート順序を決めることはありますが、強制力はないため即破綻してしまいます。順番がバラバラでもコードは動きますが、コードの保守性を考えると無視できない問題でもあります。以下のように、グループ化できると便利です。

// グループ1: Reactのパッケージ
import React from "react";

// グループ2: 外部ライブラリ
import { View, Text } from "react-native";
import { useDispatch, useSelector } from "react-redux";

// グループ3: 内部コンポーネントやユーティリティ
import { HeaderIcon } from "~components/header/HeaderMenuIcon";
import WrapContainer from "~components/layout/WrapContainer";
import ImageModalView from "~components/modal/ImageModalView";

この記事では、React Nativeにおける上記2つのソリューションを記載します。

インポートエイリアス

インポート時のエイリアスの設定を行っていきます。ここでは以下のようなディレクトリ構造を想定しています。

root/
┣ index.js
┣ assets/
┃ ┗ image.jpg
┗ src/
  ┣ components/
  ┣ api/
  ┣ hooks/
  ┗ App.js

assets/にはアセットファイル、src/にはすべてのソースコードが含まれています。後者は配下にいくつかの子ディレクトリが存在するものとします。今回はこの2つのディレクトリのエイリアスを設定します。

Expoの場合

Expoマネージドワークフローの場合、App.jsはルート直下にあるため、上記のディレクトリ構造にするには、app.jsonentryPoint: "./src/App.js"を記述する必要があります。EASの場合はindex.jsを以下のようにします。また、Expoマネージドワークフローから移行する場合は、前述のentryPointを削除する必要があります。

// index.js
import { registerRootComponent } from "expo";

import App from "./src/App";

registerRootComponent(App);

💡上記は2021年12月時点の設定方法です。近々、カスタムルートの設定方法が追加されるそうです。

エイリアスの指定

エイリアスは指定ディレクトリ階層までのパスを省略することができます。例えば、src/components/apiまでの階層を~components/apiと記述できます。この場合のエイリアスのプレフィックスは~です。どのような階層からでも、~components/apiと記述できるので、ファイルの物理的な位置が変わってもインポート文のfrom "..."を変更する必要はなくなります。

// これが
import someComponent from "../../../../components/someComponent";

// こうなる
import someComponent from "~components/someComponent"

それでは、必要なパッケージをインストールします。

yarn add --dev babel-plugin-module-resolver

そして、babel.config.jsに設定を追加します。

module.exports = function (api) {
	// ...
  return {
    presets: ["babel-preset-expo"],
    plugins: [
      [
        "module-resolver",
        {
          root: ["./"],
          alias: {
            "^~(.+)": "./src/\\1",
            "@assets": "./assets",
          },
          extensions: [
            ".js",
            ".jsx",
            ".ts",
            ".tsx",
            ".android.js",
            ".android.tsx",
            ".ios.js",
            ".ios.tsx",
          ],
        },
      ],
    ],
  };
};

rootで基準となるディレクトリを指定し、aliasでエイリアスを設定します。ここではsrcassetsディレクトリのエイリアスを設定しています。src/配下は子ディレクトリが存在するため、それらに合わせるように正規表現を使用する必要があります。assetsは直下にはディレクトリがないので、@assetsという単純なエイリアスを指定しています。

これでインポートのエイリアスは動作します。

VSCode

VSCodeを利用している場合は、エイリアスをオートコンプリートさせることができます。

jsconfig.jsonpathsに以下のような設定を記述します。

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "~*": ["src/*"],
      "@assets": ["./assets"]
    }
  },
  "exclude": ["node_modules"]
}

これでimportのオートコンプリート時にエイリアスが候補に入るようになります。

import-alias-in-vscode-autocomplete

インポート文の順序

次にインポート文の記載順序という問題に移ります。これはESLintを使用します。

以下をインストールします。

# eslintが未導入の場合は、eslintもインストールする必要があります
yarn add --dev eslint-plugin-import

eslintrc.jsに以下を記述します。

module.exports = {
  //...
  extends: [
    "eslint:recommended",
    "plugin:import/recommended",
  ],
  rules: {
    "import/no-unresolved": "off",
    "import/named": "warn",
    "import/namespace": "warn",
    "import/no-named-as-default": "off",
    "import/export": "warn",
    "import/order": [
      "error",
      {
        // グループの定義、尊重する順番
        groups: [
          "builtin",
          "external",
          "internal",
          "parent",
          "sibling",
          "index",
        ],
        // インポートグループ間の新しいラインを強制または禁止します
        "newlines-between": "always",
        // 各グループはアルファベット順でソートされます
        alphabetize: {
          order: "asc",
          caseInsensitive: true,
        },
        // パスグループ
        pathGroups: [
          {
            pattern: "react",
            group: "external",
            position: "before",
          },
        ],
        // pathGroupsでは扱われないインポートタイプを定義
        pathGroupsExcludedImportTypes: ["builtin"],
      },
    ],
  },
  settings: {
    "import/ignore": ["react-native"],
  },
};

簡易なコメントは入れてありますが、ソートのオプションの詳細はこちらで確認できます。

これで一般的なLint同様、順序がおかしいと警告が表示されます。Prettierを導入してあれば、保存時にインポート文の自動補正も行われます。以下はVSCodeでの動作画面になります。

import-eslint-result

認知負荷を軽減するだけでなく、ファイルのスキャンも高速化されるそうです。