Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Storybook can't resolve aliases #14087

Closed
DoneDeal0 opened this issue Mar 1, 2021 · 8 comments
Closed

Storybook can't resolve aliases #14087

DoneDeal0 opened this issue Mar 1, 2021 · 8 comments

Comments

@DoneDeal0
Copy link

Describe the bug
Storybook can't resolve aliases. The build crashes.

To Reproduce

import Foo from "components/foo" => crash
import Foo from "../../components/foo" => ok

Expected behavior
Storybook should build files with aliases imports.

Code snippets
My storybook config:

module.exports = {
  stories: ["../**/stories.tsx"],
  webpackFinal: (config) => {
    return {
      ...config,
      module: {
        ...config.module,
        rules: [
          {
            test: /\.(ts|js)x?$/,
            exclude: /node_modules/,
            use: { loader: "babel-loader" },
          },
          { test: /\.css$/, use: ["style-loader", "css-loader"] },
          { test: /\.(png|jpg|gif)$/, use: ["file-loader"] },
          {
            test: /\.svg$/,
            use: [
              {
                loader: "babel-loader",
              },
              {
                loader: "react-svg-loader",
                options: {
                  jsx: true,
                },
              },
            ],
          },
        ],
      },
    };
  },
  typescript: {
    check: false,
    checkOptions: {},
    reactDocgen: "react-docgen-typescript",
    reactDocgenTypescriptOptions: {
      shouldExtractLiteralValuesFromEnum: true,
      propFilter: (prop) =>
        prop.parent ? !/node_modules/.test(prop.parent.fileName) : true,
    },
  },
};

My webpack config:

/* eslint-env node */
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const Dotenv = require("dotenv-webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const isProductionMode = (mode) => mode === "production";

module.exports = () => {
  const env = require("dotenv").config({ path: __dirname + "/.env" });
  const nodeEnv = env.parsed.NODE_ENV;
  return {
    mode: "development",
    entry: "./src/index.tsx",
    output: {
      path: path.join(__dirname, "./dist"),
      filename: "[name].[contenthash].bundle.js",
      publicPath: "/",
    },
    resolve: {
      extensions: [".ts", ".tsx", ".js", "jsx", ".json"],
      alias: {
        api: path.resolve(__dirname, "src/api/"),
        assets: path.resolve(__dirname, "src/assets/"),
        components: path.resolve(__dirname, "src/components/"),
        containers: path.resolve(__dirname, "src/containers/"),
        i18n: path.resolve(__dirname, "src/i18n/"),
        models: path.resolve(__dirname, "src/models/"),
        pages: path.resolve(__dirname, "src/pages/"),
        src: path.resolve(__dirname, "src/"),
        stores: path.resolve(__dirname, "src/stores/"),
        utils: path.resolve(__dirname, "src/utils/"),
      },
    },
    module: {
      rules: [
        {
          test: /\.(ts|js)x?$/,
          exclude: /node_modules/,
          use: { loader: "babel-loader" },
        },
        { test: /\.css$/, use: ["style-loader", "css-loader"] },
        { test: /\.(png|jpg|jpeg|gif)$/, use: ["file-loader"] },
        {
          test: /\.svg$/,
          use: [
            {
              loader: "babel-loader",
            },
            {
              loader: "react-svg-loader",
              options: {
                jsx: true,
              },
            },
          ],
        },
      ],
    },
    devServer: {
      historyApiFallback: true,
      port: 3000,
      inline: true,
      hot: true,
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: "./src/index.html",
      }),
      new Dotenv(),
    ],
    optimization: {
      minimize: isProductionMode(nodeEnv),
      minimizer: isProductionMode(nodeEnv) ? [new TerserPlugin()] : [],
      splitChunks: { chunks: "all" },
    },
  };
};

My babel config:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-typescript",
    "@babel/preset-react"
  ],
  "plugins": [
    "babel-plugin-styled-components",
    "@babel/plugin-proposal-class-properties",
    [
      "@babel/plugin-transform-runtime",
      {
        "regenerator": true
      }
    ]
  ]
}

System
System:
OS: macOS High Sierra 10.13.4
CPU: (8) x64 Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz
Binaries:
Node: 12.7.0 - /usr/local/bin/node
Yarn: 1.16.0 - /usr/local/bin/yarn
npm: 6.13.6 - /usr/local/bin/npm
Browsers:
Chrome: 88.0.4324.192
Firefox: 84.0.2
Safari: 11.1
npmPackages:
@storybook/react: ^6.1.20 => 6.1.20

@adhenrique
Copy link

adhenrique commented Mar 1, 2021

@DoneDeal0

Try changing your aliases by adding an @ as a prefix. Eg: @components/my-component/...

This happened to me when I used aliases without a prefix. The webpack confuse aliases with aliases from third-party libraries.

@DoneDeal0
Copy link
Author

@adhenrique So I've just tried to update my webpack + ts config with "@" before all my aliases and carefully updated all my files. It doesn't work. I still get the error:

Module not found: Error: Can't resolve '@components/form/label' in blablabla

@DoneDeal0
Copy link
Author

DoneDeal0 commented Mar 3, 2021

Ok I found the solution. Just use "tsconfig-paths-webpack-plugin".

The config now looks like this:

const { TsconfigPathsPlugin } = require("tsconfig-paths-webpack-plugin");

module.exports = {
  stories: ["../**/stories.tsx"],

  webpackFinal: async (config) => {
    [].push.apply(config.resolve.plugins, [
      new TsconfigPathsPlugin({ extensions: config.resolve.extensions }),
    ]);

    return {
      ...config,
      module: {
        ...config.module,
        rules: [
          {
            test: /\.(ts|js)x?$/,
            exclude: /node_modules/,
            use: { loader: "babel-loader" },
          },
          { test: /\.css$/, use: ["style-loader", "css-loader"] },
          { test: /\.(png|jpg|gif)$/, use: ["file-loader"] },
          {
            test: /\.svg$/,
            use: [
              {
                loader: "babel-loader",
              },
              {
                loader: "react-svg-loader",
                options: {
                  jsx: true,
                },
              },
            ],
          },
        ],
      },
    };
  },
};

I would recommend to officially support this plugin for typescript users, it saves so much time.

@ghost
Copy link

ghost commented Mar 23, 2021

Two methods for handling I've seen now to address this issue here and in #13184 are:

[].push.apply(config.resolve.plugins, [
  new TsconfigPathsPlugin({ extensions: config.resolve.extensions }),
]);
config.resolve.plugins = [new TsconfigPathsPlugin({ extensions: config.resolve.extensions })]

The first seems more robust because it's pushing the plugin onto the existing array whereas the second doesn't. I was able to use the second version without issue. But I'm wondering if it's prone to breakage due to not being as robust. @DoneDeal0 any thoughts?

@shilman
Copy link
Member

shilman commented Mar 24, 2021

@balibebas Definitely prefer the most robust one, maybe:

config.resolve.plugins = [...(config.resolve.plugins || []), new TsBlah()] 

Or the equivalent

@robertpiosik
Copy link

.storybook/main.js

const path = require('path');

module.exports = {
  core: {
    builder: 'webpack5',
  },
  stories: ['../src/**/*.stories.tsx'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  webpackFinal: async (config) => {
    config.resolve.alias = {
      '@': path.resolve(__dirname, '..', 'src'),
    };

    return config;
  },
};

allows for aliased imports such import BaseWrapper from '@/components/base/BaseWrapper';

@zenatshockbyte
Copy link

.storybook/main.js

const path = require('path');

module.exports = {
  core: {
    builder: 'webpack5',
  },
  stories: ['../src/**/*.stories.tsx'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  webpackFinal: async (config) => {
    config.resolve.alias = {
      '@': path.resolve(__dirname, '..', 'src'),
    };

    return config;
  },
};

allows for aliased imports such import BaseWrapper from '@/components/base/BaseWrapper';

We're using Vite and ts project references and something like;

    viteFinal: async (config) => {
        config.resolve.alias = {
            ...config.resolve.alias,
            '@some-alias': path.resolve(__dirname, '../path/to/external/dir'),
        };
		return config;
	},

worked for us!

@JeffGuKang
Copy link
Contributor

JeffGuKang commented Dec 6, 2022

I added TsconfigPathsPlugin for config.resolve.plugins

.storybook/main.js

const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')


module.exports = {
 ...,
  webpackFinal: async (config, {configType}) => {
    ...
    config.resolve.plugins = [
      ...config.resolve.plugins,
      new TsconfigPathsPlugin(),
    ]
    
    return config
  },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants