Typescript + Webpack5 + Jest 的路徑命名問題
前言
因為在專案中,原本只有在 webpack
中使用路徑重命名,發現在 vscode 中還是沒辦法找到該路徑,最後發現是 Typescript
的編譯問題;解決之後,在用Jest
作單元測試的時候,發現還是有路徑問題,因此又花了點時間解決。本文就是來紀錄一下解決方案。
目標
假設專案結構如下:
- src
- themes
- index.ts
- …
- plugins
- index.ts
- …
- App.tsx
- helper.ts
- …
- themes
我們的目標是想要讓在 src/*
底下的檔案路徑都為 @base/*
、themes/*
底下的檔案路徑都為 @themes/*
、plugins/*
底下的檔案路徑都為 @plugins/*
,而不用因為相對路徑的緣故而變成../../../plugins/xxx.ts
。
以下會先分別介紹在webpack、typescript、jest要怎麼樣設置,然後在提供現階段專案中的統一設置方法。
webpack5 的別名
在webpack執行module resolution時,需要在webpack.config.js中設定alias
,如下:
webpack.config.js 設定
const path = require('path') module.exports = { ... resolve:{ alias: { '@base': path.resolve(__dirname, 'src'), '@plugins': path.resolve(__dirname, 'src/plugins'), '@themes': path.resolve(__dirname, 'src/themes'), ... } }, ... }
Typescript 定義路徑別名:
如果只在webpack中設定alias、而沒有在tsconfig.json中另外設定,會導致vscode沒辦法辨別路徑
tsconfig.json 設定
{ "compilerOptions": { "paths": { "@base/*": ["src/*"], "@plugins/*": ["src/plugins/*"], "@themes/*": ["src/themes/*"], ...... } } }
Jest中定義路徑名
由於Jest執行的時候,不是投過設定好的Webpack打包後執行,而是另外編譯的,所以如果沒有在為Jest設定module resolution,Jest也是沒辦法辨別路徑
jest.config.js
module.exports = { ..., moduleNameMapper: { '^@base/(.*)': '<rootDir>/src/$1', '^@plugins/(.*)': '<rootDir>/src/plugins/$1', '^@themes/(.*)': '<rootDir>/src/themes/$1', } }
統一設定
雖然說上述方法都解決了路徑別名問題,但如果某個別名的資料夾要修改名稱、或者要新增一個資料夾然後想創別名,這樣一個改動要改三個檔案,實在太麻煩了吧?所以下面為一勞永逸、只要改動一個檔案、其他都會連動更改。
先在根目錄定義一個
tsconfig.alias.json
{ "compilerOptions": { "baseUrl": ".", "paths": { "@base/*": ["src/*"], "@themes/*": ["src/themes/*"], "@plugins/*": ["src/plugins/*"], } } }
tsconfig.json設定 利用
tsconfig.json
的可拓展特性,來extendstsconfig.alias.json
{ "compilerOptions": { ..., "extends": "./tsconfig.alias.json", } }
webpack.config.json設定
在webpack中建立一個函數來處理tsconfig.alias.json
中的字符串,以符合webpack alias的格式:
function getWebpackAliasesFromPaths(configPaths) {
const alias = Object.entries(configPaths).reduce(
(webpackAliases, [configAlias, configPathList]) => {
const [aliasKey] = configAlias.split('/')
const [relativePathToDir] = configPathList[0].split('/*')
return {
...webpackAliases,
[aliasKey]: path.resolve(__dirname, `../${relativePathToDir}`),
}
},
{},
)
return alias
}
然後就可以先把tsconfig.alias.json
給import進來,然後透過函數處理來寫入alias
const aliases = require('../tsconfig.alias.json')
module.exports = {
...,
resolve:{
alias: getWebpackAliasesFromPaths(aliases.compilerOptions.paths)
},
}
jest.config.js設定 在jest中,已經有一個npm包
ts-jest
可以幫忙處理這個路徑轉換,如下:const { pathsToModuleNameMapper } = require('ts-jest') const aliases = require('./tsconfig.alias.json') module.exports = { ..., moduleNameMapper: { ...pathsToModuleNameMapper(aliases.compilerOptions.paths, { prefix: '<rootDir>/', }), } }
這樣一來,如果之後有要新增其他資料夾然後增加別名、或者有什麼要更動的,就只要修改tsconfig.alias.json
裡面的設定即可!
ChangeLog
- 20221013 - 更改初稿