How to add typescript paths to storybook

Viewed 3029

I have a react application with a custom Webpack configuration. After adding Webpack aliases that matches tsconfig.json file compilerOptions->paths field the aliases were recognized by webpack.

Since storybook comes with a built in Webpack configuration, my aliases are not read by Storybook and I'm getting the following error:

Module not found: Error: Can't resolve <path with typescript alias> in <some folder path>
3 Answers

In Storybook main.js file, add the following:

const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
  ...,
  webpackFinal: async (config, { configType }) => {
       config.resolve.plugins = [new TsconfigPathsPlugin()];<-- this line
       return config;
  }

};

You can install tsconfig-paths-webpack-plugin using the following command from the folder in which your application's package.json file resides:

npm i tsconfig-paths-webpack-plugin -D

Solution was derived from this discussion: https://github.com/storybookjs/storybook/issues/6316

For future vistors of this question, since 15th July of 2022 storybooks is using Vite instead Webpack.

In that case I recommend using vite-tsconfig-paths instead of tsconfig-paths-webpack-plugin. If you are using TS paths in Vite, you probably already have this package installed.

Add this to your .storybook/main.js

const { mergeConfig } = require("vite")
const { default: tsconfigPaths } = require('vite-tsconfig-paths')

module.exports = {
  // your previous configs and more...
  viteFinal(config, { configType }) {
    return mergeConfig(config, {
      plugins: [
        tsconfigPaths()
      ]
    })
  }
}

An alternative to accepted solution:

If you prefer not to install an external library such as tsconfig-paths-webpack-plugin, you can create a custom file, say: tsconfig-webpack-utils.js

and do something similar to the following:

const { compilerOptions } = require('../tsconfig.json');

function getAliases() {
    const baseUrl = getTSBaseUrl();
    return Object.fromEntries(Object.entries(compilerOptions.paths).map(([key, value]) => {
            return [
                key.replace(/\/\*\*?$/,''),
                value.map(entryPath => path.resolve(__dirname, baseUrl, entryPath.replace(/\/\*\*?$/,'/')))
            ]
        }));
    }

function getTSBaseUrl() {
    return path.resolve(__dirname, `../${compilerOptions.baseUrl}`);
}

exports.addTsDefinitionsToWebpack = function(webpackConfig) {
    if (!webpackConfig.resolve.modules) {
        webpackConfig.resolve.modules = ['node_modules'];
    }
    webpackConfig.resolve.modules.push(getTSBaseUrl());
    webpackConfig.resolve.alias = {
        ...webpackConfig.resolve.alias,
        ...getAliases()
    };
}

This solution only works for very simple aliases. It is recommended to use an appropriate library or to expand this solution according to your needs.

You can then use it as follows in every webpack config you require it:

addTsDefinitionsToWebpack(webpackConfig);
Related