builder-vite: alias not working

main.js

const path = require("path")
const reactRefresh = require("@vitejs/plugin-react-refresh")

module.exports = {
    stories: [
        // "../src/**/*.stories.@(js|jsx|ts|tsx)"
        "../src/components/**/*.stories.@(ts|tsx)",
    ],
    // addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
    core: {
        builder: "storybook-builder-vite",
    },
    resolve: {
        alias: [
            {
                find: "@",
                replacement: path.resolve(__dirname, "./src"),
            },
            {
                find: "@assets",
                replacement: path.resolve(__dirname, "./src/assets"),
            },
            {
                find: "@components",
                replacement: path.resolve(__dirname, "../src/components"),
            },
        ],
    },
    async viteFinal(config, { configType }) {
        // customize the Vite config here
        config.resolve.alias.foo = "bar"

        reactRefresh()

        return config
    },
}

If use yarn run not storybook, config like https://github.com/vitejs/vite/issues/279 work run. But i believe vite.config.ts != main.js. This plugin can’t handle some features like vite.config.ts

Requirement:

[1] show case how to use reactRefresh plugin like we did in vite.config.ts. Cuz reactRefresh is a must for react to work in ite. In vite. Also mention, there is another plugin @vite/vue should be imported if develop vue.

[2] support vite-tsconfig-paths in storybook. So, we can have alias support (there may be quite way to solve, this is the major feature needs to be, for storybook to work with alias)

vite.config.ts

import { defineConfig } from "vite"
// import tsconfigPaths from "vite-tsconfig-paths"
import reactRefresh from "@vitejs/plugin-react-refresh"
// import envCompatible from "vite-plugin-env-compatible"
import path from "path"

export default defineConfig({
    plugins: [reactRefresh()],
    build: {
        outDir: "build",
    },
    // alias: {
    //     "@": path.resolve(__dirname, "./src"),
    // },
    // resolve: {
    alias: [
        {
            find: "@",
            replacement: path.resolve(__dirname, "./src"),
        },
        {
            find: "@assets",
            replacement: path.resolve(__dirname, "./src/assets"),
        },
        {
            find: "@components",
            replacement: path.resolve(__dirname, "./src/components"),
        },
    ],
    // },
    // plugins: [tsconfigPaths(), envCompatible(/* options */), reactRefresh()],
})

tsconfig.json

{
    "compilerOptions": {
        "allowJs": true,
        "allowSyntheticDefaultImports": true,
        "baseUrl": "src",
        "paths": {
          "@components/*": ["./components/*"],
          "@assets/*": ["./assets/*"],
        },
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "isolatedModules": true,
        "jsx": "react-jsx",
        "lib": ["dom", "dom.iterable", "esnext"],
        "module": "esnext",
        "moduleResolution": "node",
        "noEmit": true,
        "noFallthroughCasesInSwitch": true,
        "resolveJsonModule": true,
        "skipLibCheck": true,
        "strict": true,
        "target": "es5",
        "types": ["node", "vite/client"]
    },
    "include": ["./src"]
}

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 15 (3 by maintainers)

Most upvoted comments

In the function viteFinal(config, { configType }), the parameter config is the config object for vite, which is used by storybook-builder-vite. You can return a new config object.

// main.js

module.exports = {
  async viteFinal(config, { configType }) {
    return {
      ...config,
      resolve: {
        alias: [
          {
            find: "@",
            replacement: path.resolve(__dirname, "./src"),
          },
          {
            find: "@assets",
            replacement: path.resolve(__dirname, "./src/assets"),
          },
          {
            find: "@components",
            replacement: path.resolve(__dirname, "./src/components"),
          },
        ],
      },
    };
  },
};

If you want to reuse the project’s vite.config.js, you can use the API mergeConfig and loadConfigFromFile exposed by vite.

const { loadConfigFromFile, mergeConfig } = require("vite");

module.exports = {
	...
  async viteFinal(config, { configType }) {
    const { config: userConfig } = await loadConfigFromFile(
      path.resolve(__dirname, "../vite.config.ts")
    );

    return mergeConfig(config, {
      ...userConfig,
      // manually specify plugins to avoid conflict
      plugins: [],
    });
  },
};

The above answer for v6.4.21 did not work for me, while this one worked fine in Stack Overflow

// .storybook/main.js
const path = require("path");
const tsconfigPaths = require("vite-tsconfig-paths").default;

module.exports = {
  "stories": [
    "../frontend/**/*.stories.mdx",
    "../frontend/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions"
  ],
  "framework": "@storybook/react",
  "core": {
    "builder": "storybook-builder-vite"
  },
  /**
   * A option exposed by storybook-builder-vite for customising the Vite config.
   * @see https://github.com/eirslett/storybook-builder-vite#customize-vite-config
   * @param {import("vite").UserConfig} config
   * @see https://vitejs.dev/config/
   */
  viteFinal: async (config) => {
    config.plugins.push(
      /** @see https://github.com/aleclarson/vite-tsconfig-paths */
      tsconfigPaths({
        // My tsconfig.json isn't simply in viteConfig.root,
        // so I've passed an explicit path to it:
        projects: [path.resolve(path.dirname(__dirname), "frontend", "tsconfig.json")],
      })
    );
    
    return config;
  },
}

☝️ this is the right answer

Perhaps loadConfigFromFile changed since this answer but now the first arg is not the path to your project’s vite.config.ts but instead an env string (which comes from configType.

    const { config: userConfig } = await loadConfigFromFile(configType,
      path.resolve(__dirname, "../src/client/vite.config.ts")
    );

Also note that storybook 7.0 will use your project’s vite.config file automatically.

import * as path from 'path'
import tsconfigPaths from 'vite-tsconfig-paths'
// or
const path = require('path');
const tsconfigPaths = require('vite-tsconfig-paths').default;

viteFinal: async (config) => {
  config.plugins.push(
    /** @see https://github.com/aleclarson/vite-tsconfig-paths */
    tsconfigPaths({
      projects: [path.resolve(path.dirname(__dirname), 'tsconfig.json')],
    })
  );
  
  return config;
},

I came across a similar problem recently but for plugins not pulling through and fixed it with the following:

async viteFinal(config) {
    // pull in our root config
    const { config: mainConfig } = await loadConfigFromFile(
      path.resolve(__dirname, "../vite.config.ts")
    );

    // we need to exclude @vitejs/plugin-react from the plugins to prevent a conflict
    // @vitejs/plugin-react is an array of objects so we can exclude it by checking for a name key
    const filteredPlugins = mainConfig.plugins.filter(item => item.name);

    return mergeConfig(config, {
      resolve: {
        alias: { "@": path.resolve(__dirname, "../src") },
      },
      plugins: [...filteredPlugins]
    });
  },

If you want to reuse the project’s vite.config.js, you can use the API mergeConfig and loadConfigFromFile exposed by vite.

// .storybook/main.cjs
const { loadConfigFromFile, mergeConfig } = require("vite");

module.exports = {
	...
  async viteFinal(config, { configType }) {
    const { config: userConfig } = await loadConfigFromFile(
      path.resolve(__dirname, "../vite.config.ts")
    );

    return mergeConfig(config, {
      ...userConfig,
      // manually specify plugins to avoid conflict
      plugins: [],
    });
  },
};

Good Work!

"vite": "^4.0.0",
"@storybook/react": "^6.5.16",

My config is somehow neater than what’s mentioned but still works: https://stackoverflow.com/a/73105465/9814737

const tsconfigPaths = require("vite-tsconfig-paths");
...
module.exports = {
  ...
  async viteFinal(config) {
    return {
      ...config,
      plugins: [...config.plugins, tsconfigPaths.default()],
    };
  },
};

Where does configType come from?

It can be destructured from the second callback parameter of viteFinal:

async viteFinal(config, { configType }) {