pnpm+Vue+Vite Monorepo:工作区包内别名路径解析异常
Monorepo共享包内部别名路径解析失败问题解决
问题概述
使用pnpm、TypeScript、Vue和Vite构建包含共享组件的Monorepo时,主应用导入共享包组件正常,但共享包内部使用@/别名导入自身文件时失败——Vite会将共享包内的@/解析到主应用的src目录,导致文件找不到。
目录结构
src/ | |- apps/ | |- app1/ | | |- env/ | | |- node_modules/ | | |- src/ | | | |- plugins/ | | | | |- some-logic.ts | | | |- styles/ | | | | |- app.scss | | | |- views/ | | | | |- HomeView.vue | | | ... | | | |- App.vue | | | |- main.ts | | | | | |- index.html | | |- package.json | | |- tsconfig.json | | |- vite.config.ts | |- packages/ | |- shared-ui/ | | |- node_modules/ | | |- src/ | | | |- components/ | | | | |- Header.vue | | | |- plugins/ | | | | |- another-logic.ts | | | |- styles/ | | | | |- header.scss | | |- package.json | | |- tsconfig.json | |- node_modules/ ... |- package.json |- pnpm-lock.yaml |- pnpm-workspace.yaml |- tsconfig.base.json
错误示例
共享包Header.vue内使用别名导入失败:
<script setup lang="ts"> import moreStuff from '@/plugins/another-logic.ts' // 失败 moreStuff(); </script> <template> <div class="header">...</div> </template> <style lang="scss"> // @import '@/styles/header.scss'; // 失败 @import '../styles/header.scss'; // 正常工作 </style>
错误信息
import/no-unresolved Unable to resolve path to module '@/plugins/another-logic.ts' import/no-unresolved [vite] Internal server error: [sass] ENOENT: no such file or directory, open '/namespace/apps/app1/src/styles/header.scss'
相关配置文件
根目录package.json
{ "name": "namespace", "private": true, "type": "module", "packageManager": "pnpm@8.6.9", "browserslist": [ "> 1%", "last 2 versions", "not dead", "not ie <= 11" ], "devDependencies": { "@types/node": "~20.3.3", "@typescript-eslint/eslint-plugin": "~5.61.0", "@typescript-eslint/parser": "~5.61.0", "eslint": "~8.44.0", "eslint-config-prettier": "~8.8.0", "eslint-import-resolver-typescript": "~3.5.5", "eslint-plugin-import": "~2.27.5", "eslint-plugin-prettier": "~4.2.1", "eslint-plugin-vue": "~9.15.1", "prettier": "~2.8.8", "ts-node": "~10.9.1", "typescript": "~5.1.6", "vite": "~4.3.9", "vite-plugin-eslint": "~1.8.1", "vue-eslint-parser": "~9.3.1" } }
pnpm-workspace.yaml
packages: - 'apps/*' - 'packages/*'
tsconfig.base.json
{ "compilerOptions": { "target": "es6", "module": "esnext", "moduleResolution": "node", "esModuleInterop": true, "isolatedModules": true, "strict": true, "jsx": "preserve", "experimentalDecorators": true, "noEmit": false, "skipLibCheck": true, "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, "useDefineForClassFields": true, "sourceMap": true, "lib": [ "esnext", "dom", "dom.iterable", "scripthost" ] }, "references": [ { "path": "./packages/shared-ui" } ], "exclude": [ "**/node_modules", "packages/**/dist" ] }
apps/app1/package.json
{ "name": "@namespace/app1", "private": true, "type": "module", "packageManager": "pnpm@8.6.9", "scripts": { "serve": "vite" }, "dependencies": { "@namespace/shared-ui": "workspace:*", "@vee-validate/zod": "~4.10.8", "axios": "~1.4.0", "pinia": "~2.1.4", "vee-validate": "~4.10.8", "vite-plugin-vuetify": "~1.0.2", "vue": "~3.3.4", "vue-router": "~4.2.4", "vuetify": "~3.3.6", "zod": "~3.21.4" }, "devDependencies": { "@vitejs/plugin-vue": "~4.2.3", "sass": "~1.64.1", "vite-tsconfig-paths": "~4.2.0" } }
apps/app1/tsconfig.json
{ "extends": "../../tsconfig.base.json", "compilerOptions": { "baseUrl": "./", "outDir": "./dist/", "paths": { "@/*": [ "src/*" ] }, "typeRoots": [ "./node_modules/@types", "./src/types" ] }, "include": [ "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "vite.config.ts" ] }
apps/app1/vite.config.ts
import { type UserConfigExport, defineConfig } from 'vite'; import eslint from 'vite-plugin-eslint'; import vuetify from 'vite-plugin-vuetify'; import tsconfigPaths from 'vite-tsconfig-paths'; import vue from '@vitejs/plugin-vue'; export default defineConfig(({ mode }) => { const isDevelopment = mode === 'development'; const config: UserConfigExport = { root: `${process.cwd()}/`, envDir: `${process.cwd()}/env/`, plugins: [tsconfigPaths(), eslint(), vue(), vuetify()], resolve: { alias: { '@/': `${process.cwd()}/src/`, vue: 'vue/dist/vue.esm-bundler.js' } } }; if (isDevelopment) { config.server = { host: true, port: Number(process.env.PORT) }; } return config; });
packages/shared-ui/package.json
{ "name": "@namespace/shared-ui", "private": true, "type": "module", "packageManager": "pnpm@8.6.9", "dependencies": { "axios": "~1.4.0" }, "devDependencies": { "@vee-validate/zod": "~4.10.8", "vee-validate": "~4.10.8", "vue": "~3.3.4", "vuetify": "~3.3.6", "zod": "~3.21.4" } }
packages/shared-ui/tsconfig.json
{ "extends": "../../tsconfig.base.json", "compilerOptions": { "baseUrl": "./", "outDir": "./dist/", "paths": { "@/*": [ "src/*" ] }, "typeRoots": [ "./node_modules/@types", "./src/types" ] }, "include": [ "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue" ] }
解决方案
1. 为共享包添加Vite配置
在packages/shared-ui/下创建vite.config.ts,配置自身的@别名:
import { defineConfig } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ plugins: [tsconfigPaths()], resolve: { alias: { '@/': new URL('./src/', import.meta.url).pathname, }, }, });
2. 调整主应用Vite的路径解析配置
修改apps/app1/vite.config.ts中的tsconfigPaths插件,让它包含所有工作区的TS配置文件:
tsconfigPaths({ projects: [ '../../tsconfig.base.json', './tsconfig.json', '../../packages/shared-ui/tsconfig.json', ], }),
3. 更新ESLint配置(根目录)
如果根目录没有.eslintrc.js则创建,添加工作区TS配置的解析支持:
module.exports = { // ...其他原有配置 settings: { 'import/resolver': { typescript: { project: [ './tsconfig.base.json', './apps/*/tsconfig.json', './packages/*/tsconfig.json', ], }, }, }, };
4. 验证并重启服务
执行pnpm install确保依赖链接正确,然后重启主应用的开发服务,此时共享包内的@/别名应该能正确解析到自身的src目录。
内容的提问来源于stack exchange,提问作者RonZ
相关产品推荐
相关产品推荐

