概要
defaultImportability: 'package'
のオプションで丸ごとpackage-privateにできます
このオプションがない場合だと@package
のつけ忘れによる誤exportが起こり得ますがそれを防いでくれます 👍
dependency-cruiserとeslint-plugin-strict-depenenciesとの使い分けを考え直す必要性を感じました
1. 背景 🖼️
TypeScirptではexport宣言した変数や関数は全てのファイルからimportできてしまいます。
これにpackage-privateの機能を組み合わせれば外部のディレクトリからimportを制限できたり、モジュール管理がしやすくなると思い調査しました。
この記事でeslint-plugin-import-access
について触れてはいますが、使い込んでみたいと思い今回細かく調査します。
ESLint,typescript-eslintの設定はこちらで紹介していますのでご覧ください!
Eslintとtypescript-eslintを手動で設定してみる
こちらが紹介するライブラリの作者の方でこのライブラリについて記事も書かれています。
こちらが今回の一連の作業のリポジトリですので、確認用にお使いください
2. eslint-plugin-import-accessとは 🤔
ディレクトリ外のファイルからインポートすることを制限するESLintのプラグインです。
3. eslint-plugin-import-accessの設定 🚧
eslint-plugin-import-accessのインストール
npm i -D eslint-plugin-import-access
eslintの設定ファイルを修正
- importAccessをimport
- pluginとruleを追加
import typescriptEslintParser from "@typescript-eslint/parser";
import js from "@eslint/js";
import tseslint from 'typescript-eslint';
import importAccess from "eslint-plugin-import-access/flat-config";
export default [
{languageOptions: {
parser: typescriptEslintParser,
parserOptions: {
project: true,
sourceType: "module",
},
}
},
js.configs.recommended,
...tseslint.configs.recommended,
{
plugins: {
"import-access": importAccess,
},
},
{
rules: {
"import-access/jsdoc": ["error"],
}
}
];
4. エラーになるか試す 🚨
以下のディレクトリ構成で検証します。
.
├── call.ts
├── eslint.config.js
├── node_modules
├── package-lock.json
├── package.json
├── sub
│ ├── call.ts
│ └── called.ts
└── tsconfig.json
/**
* @package
*/
export const called = "called";
import { called } from "sub/called";
エラーが表示されました!
5. TypeScript Language Service Pluginを有効にする 🧪
{
"compilerOptions": {
// ...
"plugins": [
// ...
{
"name": "eslint-plugin-import-access"
}
]
}
}
VSCodeのコマンドパレット(Ctrl+Shift+P または Cmd+Shift+P)を開き、「TypeScript: Select TypeScript Version」を選択し、「Use Workspace Version」を選びます。
これで、「Use Workspace Version」を選択します。
すると、先ほどのcall.ts
でsubディレクトリのcalled
をimportする分がサジェスチョンで表示されなくなりました。
ですが、クイックフィックスではimportできそうです 😅
5. defaultでpackage-privateにできるルール📦
こちらで紹介されていました。
rulesにdefaultImportability: 'package'
を追加します。
{
rules: {
"import-access/jsdoc": ["error",
{
// defaultImportabilityをpackageに設定
defaultImportability: 'package',
}],
}
}
これをすると、デフォルトでexportされた宣言はpackage-privateになります。
export const called = "called";
@package
のつけ忘れによる誤exportを防げます👍
6. 紹介されていた「2種類の抜け穴」について 🫢
こちらのdocの設定を参考に進めます!
同じディレクトリ内のindex.tsから再exportすると他のディレクトリからもimport可能
import { called } from "./called";
export { called };
import { called } from "sub/index";
inde.ts
に名前を変えてみます。
import { called } from "./called";
export { called };
デフォルトの設定だと名前はindexにする必要があるそうです。
必要であれば以下の対応をするとこの抜け穴はなくなります。
indexLoophole:true
オプションを追加します。
rules: {
"import-access/jsdoc": ["error",{
// defaultImportabilityをpackageに設定
defaultImportability: 'package',
indexLoophole: false
}],
}
importできなくなりました。
ディレクトリ名と同じファイルからはディレクトリの中の package エクスポートをインポートできる
rules: {
"import-access/jsdoc": ["error",{
// defaultImportabilityをpackageに設定
defaultImportability: 'package',
filenameLoophole:true
}
],
}
.
├── call.ts
├── eslint.config.js
├── node_modules
├── package-lock.json
├── package.json
├── sub
│ ├── call.ts
│ └── called.ts
├── sub.ts
└── tsconfig.json
sub.ts
からsubディレクトリ内のcalledがimportできました。
これはtestの実装時に有効らしいです!
import { called } from "sub/called";
importできています。
↑弊社のテックブログで紹介されています!
7. default exportでもimportできない 👍
const exported = 1;
export default exported;
8. 最後に
-
defaultImportability: 'package'
のオプションで丸ごとpackage-privateにできます - このオプションがない場合だと
@package
のつけ忘れによる誤exportが起こり得ますがそれを防いでくれます 👍 - dependency-cruiserとeslint-plugin-strict-depenenciesとの使い分けを考え直す必要性を感じました
参考 ✨
- TypeScriptプロジェクトでディレクトリ単位のカプセル化をする
- eslint-plugin-import-accessで「そこからそれはimportしないでください!!」を防ぐ
- eslint-plugin-import-accessではじめるディレクトリ単位カプセル化
本記事を読んで頂き、ありがとうございました。
いいねいただけると記事執筆の励みになりますので、参考になったと思われた方は是非よろしくお願い致します🙏
他にも依存関係の管理に関する記事を書いています 💪