#16487·medusa

[Bug]: `medusa plugin:build` does not resolve TypeScript path aliases in admin extensions

Author: SalahAdDinCreated Aug 19, 2026Updated Sep 16, 2026
Labelstype: bugstatus: needs triagingversion: 2.0help-wanted

Package.json file

json
{
  "name": "@vicacha-devs/medusa-plugin-b2b",
  "version": "0.0.1",
  "description": "B2B commerce capabilities for Medusa v2: companies, employees, quotes, approvals, spending limits, and B2B cart workflows.",
  "author": "Vicacha Devs",
  "license": "MIT",
  "files": [
    ".medusa/server",
    "src",
    "README.md",
    "LICENSE",
    "tsconfig.json"
  ],
  "exports": {
    "./package.json": "./package.json",
    "./workflows": "./.medusa/server/src/workflows/index.js",
    "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
    "./modules/*": "./.medusa/server/src/modules/*/index.js",
    "./providers/*": "./.medusa/server/src/providers/*/index.js",
    "./*": "./.medusa/server/src/*.js",
    "./admin": {
      "import": "./.medusa/server/src/admin/index.mjs",
      "require": "./.medusa/server/src/admin/index.js",
      "default": "./.medusa/server/src/admin/index.js"
    }
  },
  "keywords": [
    "medusa",
    "plugin",
    "medusa-plugin-b2b",
    "medusa-plugin",
    "medusa-plugin-other",
    "medusa-v2"
  ],
  "scripts": {
    "build": "medusa plugin:build && tsc-alias -p tsconfig.json",
    "publish:local": "medusa plugin:publish",
    "dev": "medusa plugin:develop",
    "lint": "medusa lint",
    "prepublishOnly": "medusa plugin:build",
    "test": "pnpm test:unit && pnpm test:integration:modules && pnpm test:integration:http",
    "test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
    "test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit",
    "test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit",
    "clean": "rm -rf .medusa node_modules"
  },
  "devDependencies": {
    "@hookform/resolvers": "5.7.1",
    "@medusajs/admin-sdk": "2.18.0",
    "@medusajs/admin-shared": "2.18.0",
    "@medusajs/cli": "2.18.0",
    "@medusajs/core-flows": "2.18.0",
    "@medusajs/framework": "2.18.0",
    "@medusajs/icons": "2.18.0",
    "@medusajs/js-sdk": "2.18.0",
    "@medusajs/medusa": "2.18.0",
    "@medusajs/test-utils": "2.18.0",
    "@medusajs/types": "2.18.0",
    "@medusajs/ui": "4.2.0",
    "@radix-ui/react-popover": "1.1.23",
    "@radix-ui/react-radio-group": "1.4.7",
    "@radix-ui/react-slot": "1.3.3",
    "@swc/core": "1.7.28",
    "@tanstack/react-table": "8.20.5",
    "@types/node": "20.0.0",
    "@types/react": "18.3.2",
    "@types/react-dom": "18.2.25",
    "cmdk": "1.1.1",
    "date-fns": "4.4.0",
    "jiti": "2.0.0",
    "lodash": "4.18.1",
    "prop-types": "15.8.1",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "ts-node": "10.9.2",
    "tsc-alias": "^1.9.2",
    "typescript": "5.6.2",
    "vite": "5.2.11"
  },
  "peerDependencies": {
    "@medusajs/admin-sdk": "2.18.0",
    "@medusajs/admin-shared": "2.18.0",
    "@medusajs/core-flows": "2.18.0",
    "@medusajs/framework": "2.18.0",
    "@medusajs/icons": "2.18.0",
    "@medusajs/js-sdk": "2.18.0",
    "@medusajs/medusa": "2.18.0",
    "@medusajs/types": "2.18.0",
    "@medusajs/ui": "4.2.0",
    "@tanstack/react-table": "8.20.5",
    "@uiw/react-json-view": "2.0.0-alpha.43",
    "react-hook-form": "7.85.0",
    "zod": "4.4.3"
  },
  "engines": {
    "node": ">=20"
  },
  "packageManager": "[email protected]",
  "dependencies": {
    "@medusajs/dashboard": "2.18.0"
  }
}

Node.js version

v25.9.0

Database and its version

PostgreSQL 16.2

Operating system name and version

Deepin 25

Browser name

Firefox

What happended?

Description

When building a Medusa plugin with admin UI extensions, TypeScript path aliases configured following the official TS aliases documentation are not resolved during the admin extensions build step. The aliases work correctly for the backend plugin source, but fail silently at build time for the admin UI.

Steps to Reproduce

  1. Create a Medusa plugin with admin UI extensions under src/admin/.
  2. Add a tsconfig.json inside src/admin/ with path aliases as documented:
json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./*"]
    }
  }
}
  1. Use @/ aliases in admin source files, e.g.:
typescript
import { useCompanies } from "@/hooks/api";
import { DataTable } from "@/components";
  1. Run medusa plugin:build (or medusa plugin:publish).

Root Cause

The @medusajs/admin-bundler plugin() command generates a Vite config internally without configuring resolve.alias for path aliases, and without including a plugin like vite-tsconfig-paths. The generated config is approximately:

const pluginConfig = {
  build: {
    lib: { entry: "src/admin/__admin-extensions__.js", ... },
    ...
  },
  plugins: [
    react(),
    medusa({ pluginMode: true, sources: ["src/admin"] }),
    clearPluginBuild({ outDir })
  ],
  // ❌ No resolve.alias for @/ 
  // ❌ No vite-tsconfig-paths
};
await vite.build(pluginConfig);

Because process.cwd() is the plugin package root (not src/admin/), Vite reads the root tsconfig.json which does not contain the @/* alias — only the admin-specific tsconfig does. As a result, @/ is treated as a bare package specifier (similar to @scope/package) and Rollup fails to resolve it.

The backend build (compiler.buildPluginBackend) works correctly because tsc is invoked with the correct tsconfig path, which includes the aliases. The admin build does not benefit from this.

Workaround

Replace all @/ alias imports with relative imports in every admin source file. This is tedious but works. Example:

// Before (breaks build)
import { useCompanies } from "@/hooks/api";

// After (works)
import { useCompanies } from "../../hooks/api/index";

Suggested Fix

The plugin() function in @medusajs/admin-bundler should read the src/admin/tsconfig.json (or the tsconfig pointed to by the plugin's admin source directory) and apply any paths entries as Vite resolve.alias entries. Alternatively, adding vite-tsconfig-paths to the plugin config pointed at the admin tsconfig would work:

typescript
import tsconfigPaths from "vite-tsconfig-paths";

const pluginConfig = {
  plugins: [
    react(),
    tsconfigPaths({ projects: [path.resolve(options.root, "src/admin/tsconfig.json")] }),
    medusa({ pluginMode: true, sources: [...] }),
  ],
  ...
};

Expected behavior

The admin extensions build should resolve @/ path aliases the same way the TypeScript compiler does, as the aliases are configured in the src/admin/tsconfig.json following the documented pattern.

Actual behavior

The build fails with Rollup resolution errors for every @/ alias import:

Plugin admin extensions build failed: [vite]: Rollup failed to resolve import "@/hooks/api" 
from "src/admin/routes/b2b/page.tsx".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to `build.rollupOptions.external`

Link to reproduction repo

https://github.com/Vicacha-Devs/vicacha-medusa-plugins/commit/8c299b68221422d2047de189df7ec34d8245dae2