You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Storybook中动态导入自定义私有npm包?

动态导入私有npm包时Webpack解析失败的问题

我有一个自定义私有npm包,想通过以下方式动态导入:

export default function SelectedIcon({path, ...props}: Props) {
  const {data: icon} = require(`@mypackagelib/icons/${path}`)
  return <Icon icon={icon} {...props} />
}
// 也尝试过另一种写法:
export default function SelectedIcon({path, ...props}: Props) {
  const Component = React.lazy(() => import(`@mypackagelib/icons/${path}`))
  return <Component {...props} />
}

两种写法都报错,问题出在动态导入上。

运行Storybook时,收到大量相同错误:

Module parse failed: Unexpected token (2:101)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| /// <reference types="react" />
| import { PropA, PropB } from '../../File';
> declare function NameOfAComponent(props: PropA): JSX.Element;
| declare namespace NameOfAComponent {
|     var data: PropB;

奇怪的是静态导入完全正常:

import OneOfTheComponent from '@mypackagelib/icons/OneOfTheComponent'

但我的场景必须用动态导入。以下是私有包的tsconfig.json配置:

{
  "compilerOptions": {
    "outDir": "dist",
    "target": "ES2020",
    "module": "commonjs",
    "jsx": "react",
    "lib": ["es6", "dom", "ES2020"],
    "moduleResolution": "node",
    "declaration": true,
    "strict": true,
    "skipLibCheck": true,
    "removeComments": true,
    "preserveConstEnums": true,
    "sourceMap": false,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmitHelpers": true,
    "importHelpers": true,
    "pretty": false,
  },
  "ts-node": {
    "swc": true
  },
  "exclude": [
    "node_moduls_local",
    "node_modules",
    "dist"
  ],
  "include": [
    "types.d.ts",
    "src/icons/**/*"
  ]
}

Storybook的./storybook/main.js配置:

module.exports = {
  stories: [
    "../src/**/*.stories.@(ts|tsx)"
  ],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions",
    "@storybook/preset-create-react-app",
    {
      name: 'storybook-addon-swc', // 以为这个能解决,但没用
      options: {
        enable: true,
        enableSwcLoader: true,
        enableSwcMinify: true,
      },
    },
  ],
  framework: "@storybook/react",
  core: {
    builder: 'webpack5',
  },
  experiments: {
    topLevelAwait: true,
  }
}

想问:

  • 如何解决这个问题?需要添加什么loader?
  • main.js该怎么配置?
  • 为什么动态导入需要loader而静态导入正常?

问题原因与解决方案

核心原因

静态导入时,Webpack在构建阶段就能精准定位到具体文件,自动应用项目中已有的TS/TSX loader处理;但动态导入使用变量路径(${path})时,Webpack会把整个@mypackagelib/icons目录作为扫描范围,会尝试加载你用tsc生成的.d.ts声明文件——而Webpack默认没有配置处理这类文件的loader,因此抛出解析错误。

你的私有包开启了declaration: true,打包后dist目录会生成对应的.d.ts文件,动态导入时这些文件被纳入扫描范围,最终导致报错。

解决方案

1. 调整私有包构建配置,隔离声明文件

修改私有包的tsconfig.json,将声明文件单独输出到子目录,并在package.json中指定类型入口,避免Webpack扫描到声明文件:

// 私有包tsconfig.json
{
  "compilerOptions": {
    // ...其他原有配置
    "declarationDir": "dist/types" // 将声明文件输出到dist/types子目录
  }
}

同时在私有包的package.json中添加:

{
  "types": "dist/types/index.d.ts",
  "main": "dist/index.js" // 确保主入口指向编译后的JS文件
}

2. 配置Storybook的Webpack规则,处理或忽略声明文件

在./storybook/main.js中添加Webpack自定义配置,要么忽略.d.ts文件,要么用loader处理它们:

module.exports = {
  // ...原有配置
  webpackFinal: async (config) => {
    // 方案一:直接忽略.d.ts文件
    config.module.rules.push({
      test: /\.d\.ts$/,
      use: [{ loader: 'ignore-loader' }]
    });

    // 方案二:用swc-loader处理.d.ts文件(如果需要保留类型检查)
    // config.module.rules.push({
    //   test: /\.d\.ts$/,
    //   use: [{
    //     loader: 'swc-loader',
    //     options: { jsc: { parser: { syntax: 'typescript', tsx: false } } }
    //   }]
    // });

    // 确保TS/TSX loader能处理私有包的文件
    config.module.rules.forEach(rule => {
      if (rule.test.toString().includes('tsx|ts')) {
        rule.include.push(require.resolve('@mypackagelib').replace('index.js', ''));
      }
    });

    return config;
  },
};

如果使用ignore-loader,需要先安装依赖:npm install ignore-loader --save-dev

3. 缩小动态导入的文件范围

在动态导入时明确文件后缀,让Webpack只扫描目标类型文件,避免碰到声明文件:

// 添加.tsx后缀,限定扫描范围
const Component = React.lazy(() => import(`@mypackagelib/icons/${path}.tsx`))

静态导入正常的原因

静态导入时,Webpack能精准定位到具体的组件文件(比如@mypackagelib/icons/OneOfTheComponent),会根据文件后缀自动匹配项目中已有的loader(如SWC/TS loader)处理;而动态导入的变量路径会触发Webpack的模糊扫描,遍历目录下所有文件,包括无对应loader的声明文件,因此报错。

内容的提问来源于stack exchange,提问作者utopia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 07:15:32