create-react-app で作った TypeScript の React アプリで Next.js でやって便利だと感じたパスのエイリアス設定しようとして盛大にハマったのでメモ。
ゴール
create-react-app --template teypescript
で作った TypeScript の React アプリで /src
ディレクトリのパスのエイリアスを作成して、何処からでも import { XXXX } from '@/components/XXXX'
のような形で import できるようにする。../
が連続する相対パスを使わなくて済むようにする
環境
react: ^17.0.2 react-scripts: 4.0.3 typescript: ^1.0.1 eslint: ^7.2.0
/root |- /.vscode |- /public |- /src |- .eslintignore |- .eslintrc.json |- .gitignore |- .prettierrc.json |- package.json |- README.md |- tsconfig.json |- yarn.lock
🐞 tsconfig.json
に paths
でパスのエイリアスを設定してもアプリ実行時にオプションが消されてしまう問題
Next.js では tsconfig.json
に baseUrl
と Paths
オプションでパスのエイリアスが設定できました。
同じ様に tsconfig.json
に設定を書いてみました
# tsconfig.json { "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], + "baseUrl": './src', + "paths": { + "@/*": ["./*"] + }
これでパスのエイリアスで import できると思ったのですが、 yarn start
でアプリを起動すると tsconfig.json
の paths
オプションが丸っと削除されてしまいました。
どうやら react-scripts が自動的に tsconfig.json
の内容を書き換えてしまうようです… どうして…
方針
extends
を使って別ファイルから paths のオプションを読み込ませる- TypeScript は パスの解決は行わないので、webpack の設定を上書きしてパスの解決を行う
- ESLint でのエイリアスのパスを使っていると
import/no-unresolved
なエラーになる問題を解消する - VS Code でエイリアスのパスで補完が効くようにする
1. extends を使って tsconfig.json
に別ファイルから paths のオプションを読み込ませる
tsconfig.json
の "compilerOptions": {}
の内容がビルド時に cleanup されるのは正常な挙動のようです。
このファイルで対処することができないので、 "extends"
を使って別ファイルから設定を読み込ませるようにします。
# 読み込ませる設定ファイルを作成 (ファイル名は何でも良い) $ touch tsconfig.paths.json
tsconfig.paths.json
に エイリアスで使いたいパスの設定を書く
{ "compilerOptions": { "baseUrl": "./src", "paths": { "@/*": ["./*"] } } }
tsconfig.json
に tsconfig.paths.json
を読み込ませる
{
+ "extends": "./tsconfig.paths.json",
"compilerOptions": {
cf.
- Add baseUrl and paths in tsconfig.json and jsconfig.json · Issue #5645 · facebook/create-react-app · GitHub
- 「create-react-app」+ 「TypeScript」環境で絶対パスのaliasを指定する
- VS Code + create-react-app v3でtsconfig.jsonのpathsを使用する - Qiita
この状態で パスのエイリアスが使える様になった思ったのですが yarn start
でアプリを起動すると @/components/XXXX Module not found
というモジュールが読み込めないエラーが表示されてしまいました。
ビルドされたコードを見てみるとエイリアスのパス部分がそのまま @/componrnts/XXXX
と出力されており、エイリアスのパスが正しいパスに解釈されていない = パスの解決がなされてないのが原因のようです。
2. TypeScript は パスの解決は行わないので、webpack の設定を上書きしてパスの解決を行う
tsconfig
の compilerOptions.paths
オプションで TypeScript のパスのエイリアスの設定はできるが、バンドルする際のパスの解決は TypeScript の責務ではないので、tsconfig
に設定を書くだけではダメということっぽい。
create-react-app
で作ったアプリは webpack でバンドルされているので、webpack の設定を上書きしてエイリアス設定しているパスの解決をする必要がある。eject
することなく webpack の設定などを上書きすることができる react-app-rewired というライブラリを使ってエイリアス設定のパスを解決する設定を作成する。
$ yarn add -D react-app-rewired
ドキュメントを参考に上書きする設定ファイルを config-overrides.js
という名前で作成する。
config-overrides.js
にパス解決の設定を記述する
const path = require('path'); module.exports = (config) => { config.resolve = { ...config.resolve, alias: { ...config.alias, // エイリアスにするパスをココに書く '@': path.resolve(__dirname, './src/'), }, }; return config; };
react の実行を react-scripts
から react-app-rewired
を変更して設定の上書きを読み込むように変更する
# package.json "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject", + "start": "react-app-rewired start", + "build": "react-app-rewired build", + "test": "react-app-rewired test", + "eject": "react-app-rewired eject",
config-overrides.js
は設定ファイルでアプリからは import しないので .eslintignore
に追加して ESLint の対象から外しておく (エラーが表示されてウザいので)
.eslintignore
/config-overrides.js
cf
- TypeScript の paths はパスを解決してくれないので注意すべし! – 自主的20%るぅる
- Absolute paths with Create React App + Typescript (without ejecting) | by Gustavo Graeff | Medium
- Reactのcreate-react-appで作成したサイトのimportを絶対パスにするしVScodeでパス補完もするし - Qiita
- react-app-rewired - npm
これでパスの解決がされエイリアスで import できるようになったが、ESLint がエイリアスでのインポートに import/no-unresolved
(Unable to resolve path to module '@/component/XXXX' import/no-unresolved
) のエラーを表示するので修正する必要があります。ESLintくんの設定めんどい…
3. ESLint でのエイリアスのパスを使っていると import/no-unresolved
なエラーになる問題を解消する
.eslintrc.js
の parserOptions.project
に tsconfig のパスを指定していても、paths の設定を理解させるには settings.import/resolver
に TypeScript の設定が別途必要なようです。(理由まで調べてなし)
TypeScript 用の import/resolver
のパッケージをインストール
$ yarn add -D eslint-import-resolver-typescript
.eslintrc.js
に設定を追加
settings: { 'import/resolver': { + typescript: { + project: './tsconfig.json', + }, },
設定は typescript: {}
だけでも上手く動作するっぽい。(デフォルトで tsconfig.json
を探してきて読み込むから?)
cf.
- [import/no-unresolved] when using with typescript "baseUrl" and "paths" option · Issue #1485 · import-js/eslint-plugin-import · GitHub
- JavaScript や CSS で ~/ から始まるパスをマッピングして import する方法 2020
- eslint-import-resolver-typescript - npm
ここまで設定するとエイリアスのパスが問題なく使用できるようになり、/src
の内のファイルからは何処からでも @/
を /src
として import のパスを書けるようになりました!
4. VS Code でエイリアスのパスで補完が効くようにする
目的は達成できていますが、エディタでパスの補完ができれば生産性がよくなるので VS Code でエイリアスのパスの補完が効くように設定します。
./vscode/setting.json
{ "path-autocomplete.pathMappings": { "@": "${folder}/src" }, }
cf.
これで import from '@/'
と書けば /src
からのパスのサジェストが表示されるようになりました!
₍ ᐢ. ̫ .ᐢ ₎ つかれた〜
おわりに
Next.js だとこれだけだったのに…
Next.js のフレームワークがいい感じにしてくれてたのですね。感謝〜
モックとかで新規プロジェクト作ることが多いので一生環境作ってる気がします…
[参考]
- Add baseUrl and paths in tsconfig.json and jsconfig.json · Issue #5645 · facebook/create-react-app · GitHub
- TypeScript の paths はパスを解決してくれないので注意すべし! – 自主的20%るぅる
- Absolute paths with Create React App + Typescript (without ejecting) | by Gustavo Graeff | Medium
- react-app-rewired - npm
- [import/no-unresolved] when using with typescript "baseUrl" and "paths" option · Issue #1485 · import-js/eslint-plugin-import · GitHub
- JavaScript や CSS で ~/ から始まるパスをマッピングして import する方法 2020
- Reactのcreate-react-appで作成したサイトのimportを絶対パスにするしVScodeでパス補完もするし - Qiita
- React: import時のaliasを設定するときはWebpack、TypeScript、ESLintの3つを対応しなければならない件 - Qiita
- VS Code + create-react-app v3でtsconfig.jsonのpathsを使用する - Qiita
- react-create-appで作成したプロジェクトにimportのaliasを設定する
- @craco/craco - npm