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

Yarn workspaces架构下Storybook跨包引入TS文件编译报错

Yarn Workspaces Monorepo 跨包引入TS文件转译失败修复方案

问题场景

基于Yarn workspaces搭建的monorepo项目,工作区配置如下:

"workspaces": [
  "app/*",
  "packages/*",
  "utilities/*"
]

在utilities目录下用TypeScript编写Storybook通用工具函数,在app/test/index.tsx中引入该工具编写组件示例,代码如下:

import React from 'react';
import { Stack, Button } from '@mui/material';
import { storybookTemplate } from '@utilities/storybook';

const template = storybookTemplate((args) => {
  return (
    <Stack spacing={2} direction="row">
      <Button variant="text">Text</Button>
      <Button variant="contained">Contained</Button>
      <Button variant="outlined">Outlined</Button>
    </Stack>
  );
});

export const ButtonTest = template();

export default {
  title: 'Testing',
};

执行yarn storybook启动服务时抛出编译错误:

ModuleBuildError: Module build failed (from ../../node_modules/babel-loader/lib/index.js):
SyntaxError: C:\Project\javascript\utilities\storybook\setup\template\index.ts: Unexpected token, expected "," (11:47)
   9 |
  10 | /** To Set up a Storybook Template */
> 11 | export const storybookTemplate = (Template: Story) => {
     |                                                ^
  12 |   return (...data: any[]): Story<Args> => {
  13 |     const variant: any = Template.bind({});
  14 |

现有配置

项目相关配置如下:

  • app/test/tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "ES5",
    "declaration": true,
    "esModuleInterop": true,
    "jsx": "react",
    "strict": true,
    "outDir": "./dist"
  },
  "exclude": ["node_modules", "**/*.test.ts"]
}
  • app/test/.babelrc
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}
  • app/test/webpack.config.js
const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');

module.exports = {
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js',
  },
  resolve: {
    modules: [path.join(__dirname, 'src'), 'node_modules'],
    alias: {
      react: path.join(__dirname, 'node_modules', 'react'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(ts|tsx|js|jsx|)$/,
        include: [/src/, /utilities/],
        resolve: {
          extensions: ['.ts', '.tsx', '.js', '.jsx'],
        },
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          { loader: 'css-loader' },
        ],
      },
    ],
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: './public/index.html',
    }),
  ],
};
  • app/test/.storybook/main.js
const custom = require('../webpack.config.js');

module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
  core: {
    builder: 'webpack5',
  },
  webpackFinal: async (config) => {
    return { ...config, module: { ...config.module, rules: custom.module.rules } };
  },
};

已确认问题根因是Yarn workspace软链的utilities目录下TS文件未被正常转译,TS类型注解被当作普通JS解析触发语法错误。将文件移动到app/test目录可正常运行,但不符合monorepo跨包复用的设计目标。

修复步骤

问题来自两个配置错误,按以下修改即可:

  1. 修正webpack转译路径匹配逻辑
    原有babel-loader的include用正则/utilities/匹配,无法命中Yarn软链后的路径,同时.babelrc默认不会处理node_modules下的依赖包。修改webpack.config.js中的babel-loader规则,直接指定utilities目录的物理绝对路径,顺便修正test正则末尾多余的分隔符:
    // 先声明monorepo根目录路径
    const monorepoRoot = path.resolve(__dirname, '../../');
    
    // babel-loader规则修改为
    {
      test: /\.(ts|tsx|js|jsx)$/,
      include: [
        path.join(__dirname, 'src'),
        path.join(monorepoRoot, 'utilities')
      ],
      resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx'],
      },
      use: {
        loader: 'babel-loader',
        options: {
          babelrcRoots: [path.join(monorepoRoot, '*')]
        }
      },
    }
    
  2. 修正Storybook的webpack配置合并逻辑
    原有配置直接覆盖config.module.rules,会冲掉Storybook内置的必要loader规则,导致转译逻辑异常。修改.storybook/main.js的webpackFinal配置,不要覆盖原有规则,追加自定义转译规则即可:
    const path = require('path');
    const monorepoRoot = path.resolve(__dirname, '../../');
    
    module.exports = {
      stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
      addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
      core: {
        builder: 'webpack5',
      },
      webpackFinal: async (config) => {
        // 追加ts/tsx转译规则,不覆盖原有规则
        config.module.rules.push({
          test: /\.(ts|tsx)$/,
          include: [
            path.resolve(__dirname, '../src'),
            path.join(monorepoRoot, 'utilities')
          ],
          use: {
            loader: 'babel-loader',
            options: {
              babelrcRoots: [path.join(monorepoRoot, '*')],
              presets: [
                '@babel/preset-env',
                '@babel/preset-react',
                '@babel/preset-typescript'
              ]
            }
          }
        });
        config.resolve.extensions.push('.ts', '.tsx');
        return config;
      },
    };
    

如果是生产环境构建,更推荐给utilities下的每个包配置独立的TS构建流程,业务包直接引入构建后的JS产物,不需要在应用侧配置跨包转译,构建速度和稳定性都会更好。开发阶段用上面的源码转译方案即可,支持跨包代码热更新。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:31:11