pnpm, vue, and vite monorepo: tackling alias path imports within a workspace package

I am in the process of creating a monorepo for UI applications that utilize shared components and styles with pnpm, typescript, vue, and vite.

While attempting to streamline development and deployment using pnpm's workspace feature, I am encountering difficulties when it comes to importing packages into an application using alias paths.

Here 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

The HomeView.vue file within my application is trying to import the Header.vue component from 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 shown above, @/ serves as a path alias for the src folder of the application, functioning properly. The issue arises with 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 assumption is that due to the vite entry point being src/apps/app1/, and having set an alias in vite's configuration from @ to src/, it attempts to resolve the @ path from the package as well, resulting in incorrect imports like those 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": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d4a4baa4b994ecfae2faed">[email protected]</a>",
      "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": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="83f3edf3eec3bbadb5adba">[email protected]</a>",
      "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": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="75051b0518354d5b435b4c">[email protected]</a>",
      "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"
      ]
    }

Answer №2

my experience with implementing nextJS


pnpm-workspace.yaml
packages:
  - app
  - packages/*

I made changes to:

"@/*": ["src/*"] -> "@<package_name>/*": ["src/*"]

 and also added in /app/tsconfig.json:
"@<package_name>/*": ["../packages/<package_name>/src/*"]

This transformation was done to facilitate a vue & vite monorepo setup.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When using TypeScript, my sorting function returns a value of 0 for all time values

I have been trying to sort this JSON data by date using the provided code, but it does not seem to work as expected. Below is a snippet of my JSON: { "StatusCode":0, "StatusMessage":"OK", "StatusDescription":[ { "id":"1", ...

Angular2 - Utilizing Promises within a conditional block

I'm currently facing an issue where I need to await a response from my server in order to determine if an email is already taken or not. However, I am struggling to achieve this synchronously. TypeScript is indicating that the function isCorrectEmail( ...

Is it possible to determine the location of the "phantom image" while moving an object?

Is there a method to retrieve the location of the ghost image when an element is dragged? This is how the scenario is set up: <div id="test" v-on:dragstart="dragStart" v-on:drag="dragging" draggable="true" v-on:drop="drop" v-on:dragover="allowDrop"> ...

A guide on simulating x-date-pickers from mui using jest

I have successfully integrated a DateTimePicker into my application, but I am facing an issue with mocking it in my Jest tests. Whenever I try to mock the picker, I encounter the following error: Test suite failed to run TypeError: (0 , _material.gen ...

Is it possible to utilize a partial entity for saving with TypeORM?

My current table structure looks like this: --changeset 0004-order:ccushing create table if not exists "order"."order" ( id uuid primary key not null default uuid_generate_v4(), state uuid re ...

Converting JSON objects into TypeScript classes: A step-by-step guide

My challenge lies in converting Django responses into Angular's User array. This conversion is necessary due to variations in variable names (first_name vs firstName) and implementing specific logic within the Angular User constructor. In simple term ...

Explaining the functionality of reserved words like 'delete' within a d.ts file is essential for understanding their

I am currently in the process of generating a d.ts file for codebooks.io, where I need to define the function delete as an exported top-level function. This is what my codebooks-js.d.ts file looks like at the moment: declare module "codehooks-js" ...

Unexpected state being returned by Vuex

I am encountering an issue with a pop-up modal that is not behaving as expected. The condition for the pop-up to appear is if the user has no transactions, which is determined by checking the length of the depositHistory array. If the length is greater tha ...

Error in Angular 13: Struggling to remove the likelihood of an object being null

I am working on a piece of code that includes the following: var item = document.getElementById("div0"); item.parentNode.removeChild(item); // The error seems to be here Every time I run this code, I encounter the error message: object is p ...

Choose an option from a selection and showcase it

I need to implement a modal that displays a list of different sounds for the user to choose from. Once they select a sound, it should be displayed on the main page. Here is the code snippet for the modal page: <ion-content text-center> <ion-ca ...

Unable to transfer array from backend to vuex store

Issue: Encountered an error in the created hook (Promise/async) - "TypeError: state.push is not a function" Function used in the page created: async function () { this.$store.commit('TicketSystem/ADD_BOARDS', (await this.$axios.get('htt ...

When trying to upload a file with ng-upload in Angular, the error 'TypeError: Cannot read properties of undefined (reading 'memes')' is encountered

Struggling with an issue for quite some time now. Attempting to upload an image using ng-upload in angular, successfully saving the file in the database, but encountering a 'Cannot read properties of undefined' error once the upload queue is comp ...

Elevate the Appearance of Material UI Elements with custom CSS Sty

I am currently facing an issue while trying to customize the styling of a Material UI component using CSS. Here is the code snippet: <IconButton className="my-class"> <Close /> </IconButton> CSS: .my-class { float: right ...

Is there a way to deactivate the click function in ngx-quill editor for angular when it is empty?

In the following ngx-quill editor, users can input text that will be displayed when a click button is pressed. However, there is an issue I am currently facing: I am able to click the button even if no text has been entered, and this behavior continues li ...

Guide on how to trigger the opening of a side panel with a button click in Vue.js

Embarking on my first Vue app development journey, I find myself in need of guidance on how to trigger the opening of a panel by clicking a button within the header. Starting off with a simple HTML template, my goal is to add some interactivity upon click ...

Determining the parent type in Typescript by inferring it from a nested member

Typescript has the ability to infer the type of a value based on queries made within if statements. For instance, the type of one member of an object can be deduced based on another: type ChildType = 'a' | 'b'; type Child<T extends ...

What are the steps to utilize Webpack 4 for exporting vendor CSS?

I am currently working on a Vue.js project with Webpack 4. When I import bootstrap.scss into my main.js bundle in the style block of my App.vue component and then export it to main.css using the mini-css-extract-plugin, the Bootstrap (vendor) CSS gets mix ...

The eslint rule 'import/extensions' was not found in the definition

I'm encountering two errors across all TypeScript files in ESLint on VS Code: Not able to find definition for rule 'import/extensions'.eslint(import/extensions) Not able to find definition for rule 'import/no-extraneous-dependencies&apo ...

Holding an element in TypeScript Angular2 proved to be challenging due to an error encountered during the process

I attempted to access a div element in my HTML using ElementRef, document, and $('#ID') but unfortunately, it did not work. I tried implementing this in ngOnInit, ngAfterViewInit, and even in my constructor. Another method I tried was using @Vie ...

Neglecting the inclusion of a property when verifying for empty properties

In the code snippet below, I have implemented a method to check for empty properties and return true if any property is empty. However, I am looking for a way to exclude certain properties from this check. Specifically, I do not want to include generalReal ...