遇到的问题描述
遇到这个问题的项目是一个用create-react-app(webpack)来搭建的 monorepo 项目。
其中的目录结构大概是
|-scripts
|-start.js
|-paths.js
|-webpack.config.js
|-packages
|-package1
|- src
|- package.json
tsconfig.json
package.json
pnpm.workspace.yaml
...
在我们普通 src 结构的项目中,通过npm run start去执行scripts中的start.js,项目会以webpack.config.js去启动项目。
在 package1.json 中的start的 script 配置是这样。很好理解,就是去调用总项目根目录下的start.js文件。
start:"node ../../scripts/start.js"
在 package.json 中的start就是pnpm run start --filter package1
那么问题来了,我在 package1 的 src 下index.tsx来引用另外一个App.tsx,报错为module not fount, ..../App ...,那么当我给.App加上.tsx的后缀之后,就不报错了。
根据这一点,很多适合会想到是配置文件tsconfig.ts的问题,结果在package1中添加tsconfig.js之后,问题真的好了,但是我希望的是整个项目只有一个tsconfig.js,子包里面不用另外的配置,因为我这些项目都是关联起来的。
虽然说添加了tsconfig.js问题就好了,但是真的是它的原因吗?想一想发现,不会,因为 ts 是不会在项目运行时起作用的,也就是说就算报错了项目也能跑起来。那这样,就只能是打包工具的问题了,打包工具没有正确的解析指定后缀名的文件。
分析 webpack,定位问题
resolve: {
modules: ["node_modules", paths.appNodeModules].concat(
modules.additionalModulePaths || [],
),
extensions: paths.moduleFileExtensions
.map((ext) => `.${ext}`)
.filter((ext) => useTypeScript || !ext.includes("ts")),
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/
其中 resolve.extensions,针对一些配置后缀名进行了一些预处理,例如 babel 等。
extensions: paths.moduleFileExtensions
.map((ext) => `.${ext}`)
.filter((ext) => useTypeScript || !ext.includes("ts")),
由上面这一段配置可以看出,paths.moduleFileExtensions中导出了被处理的一些模块的后缀名,后面 filter 是根据useTypescript这个参数来判断是否要包含带有.ts后缀的,其中也就包含了.ts,.tsx等等。
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
然后从上面发现,useTypescript 就是判断一个路径是否存在,也就是paths.appTsConfig是否存在。继续查看,发现这个配置就是
appTsConfig: resolveApp("tsconfig.json"),,也就是查看当前运行路径下是否有tsconfig.json这个配置文件。
解决问题
上面说了,运行子项目的时候,实际上是调用的子项目下的start的 script,那么process.cwd也就是返回的子项目的根路径,自然 webpack 中来判断是否支持 ts 的时候,就会从子项目路径下寻找 tsconfig.json 的配置文件了。
结合我们只希望整个项目只有一个tsconfig.json,我们有了一个方法,就是修改webpack.config.js的配置,让它只去寻找项目根目录的 tsconfig.json。这样就不能使用process.cwd,而是使用__dirname来确定脚本所在位置,然后回退到根目录下。
module.exports = {
...
- appTsConfig: resolveApp("tsconfig.json")
+ appTsConfig: path.resolve(__dirname,"..","tsconfig.json"),
...
}
我们直接把appTsConfig中寻找当前运行项目下tsconfig.json的路径,改成从配置文件的路径下定位根目录下tsconfig.json。这样,无论从哪里运行,只要使用了这个webpack.config.js,就会寻找同一个tsconfig.json。
现在重新运行子项目,发现又报错了...
Cannot find the "xxxxx\package1\tsconfig.json" file.
Please check webpack and ForkTsCheckerWebpackPlugin configuration.
Possible errors:
- wrong `context` directory in webpack configuration (if `configFile` is not set or is a relative path in the fork plugin configuration)
- wrong `typescript.configFile` path in the plugin configuration (should be a relative or absolute path)
看来要使用 ts,还是要在每个项目下使用配置文件,但是现在我们不是单独的配置一个配置文件了,而是采用继承的方式来继承根目录下的tsconfig.json。
{
"extends": "../../tsconfig.json"
}
然后再次运行,项目成功跑通不报错。