Reputation: 853
I'm building a monorepo of UI applications using shared components and style using pnpm, typescript, vue, and vite.
While trying to leverage pnpm's workspace ecosystem to ease the development experience and deployment, I'm struggling with using alias paths when importing a package into an app.
This is my folder structure:
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
...
|- package.json
|- pnpm-lock.yaml
|- pnpm-workspace.yaml
|- tsconfig.base.json
My HomeView.vue
in my application is importing the Header.vue
component of my shared-ui package:
<script setup lang="ts">
import stuff from '@/plugins/some-logic.ts'
import Header from '@namespace/shared-ui/src/components/Header.vue';
stuff();
</script>
<template>
<div class="container">
<Header />
</div>
</template>
<style lang="scss">
@import '@/styles/app.scss';
</style>
As you can see above, @/
acts as a path alias for the src
folder of the application. This works as expected. The problem starts under the Header
component:
<script setup lang="ts">
import moreStuff from '@/plugins/another-logic.ts' // doesn't work
moreStuff();
</script>
<template>
<div class="header">
...
</div>
</template>
<style lang="scss">
// @import '@/styles/header.scss'; // doesn't work
@import '../styles/header.scss'; // works
</style>
My guess is since the entry point of vite is src/apps/app1/
, and in the vite's config, I've created an alias of @
to src/
, it's trying to resolve the @
of the package as well, and leads to wrong import as described below:
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'
root package.json
{
"name": "namespace",
"private": true,
"type": "module",
"packageManager": "[email protected]",
"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": "[email protected]",
"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": "[email protected]",
"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"
]
}
Upvotes: 9
Views: 4475
Reputation: 21
my case with nextJS where
```
# pnpm-workspace.yaml
packages:
- app
- packages/*
```
I replaced:
"@/*": ["src/*"] -> "@<package_name>/*": ["src/*"]
and in /app/tsconfig.json added:
"@<package_name>/*": ["../packages/<package_name>/src/*"]
It should translate to a vue & vite monorepo.
Upvotes: 0
Reputation: 853
As described in the following URL, vite
(and rollup
) apparently do not support resolving aliases with an array but only a string. The solution, as of this moment, is to pick different aliases per app/package.
https://github.com/vercel/turbo/discussions/620#discussioncomment-2136195
Upvotes: 0