Storybook webpack 反复重建(一遍又一遍地热重载?)

Storybook webpack repeatedly rebuilding (hot reloading over and over?)

我正在将 Storybook 用于 NextJS 项目,当我 运行 它时,它不断地重建,永不停止。我的 CPU 发疯了,它耗尽了我的电池。构建完成后,它会立即开始重建。好像一直在热重载

webpack building...
webpack built preview 4f726ddcef7523fb95db in 20394ms
webpack building...
webpack built preview 4f726ddcef7523fb95db in 8835ms
webpack building...
webpack built preview 4f726ddcef7523fb95db in 8763ms
webpack building...
webpack built preview 4f726ddcef7523fb95db in 7692ms
webpack building...
webpack built preview 4f726ddcef7523fb95db in 10166ms
webpack building...
webpack built preview 4f726ddcef7523fb95db in 8239ms
webpack building...
webpack built preview 4f726ddcef7523fb95db in 8956ms
webpack building...
webpack built preview 4f726ddcef7523fb95db in 7353ms
webpack building...

我的main.js

const path = require('path');

module.exports = {
  stories: ['../**/*.stories.mdx', '../**/*.stories.@(js|jsx|ts|tsx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  presets: [path.resolve(__dirname, './next-preset.js')],
};

我的next-preset.js

const path = require('path');

module.exports = {
  webpackFinal: async (baseConfig, options) => {
    // Modify or replace config. Mutating the original reference object can cause unexpected bugs.
    const { module = {} } = baseConfig;

    const newConfig = {
      ...baseConfig,
      module: {
        ...module,
        rules: [...(module.rules || [])],
      },
    };

    // Absolute imports
    newConfig.resolve.modules.push(path.resolve(__dirname, '../'));

    // TypeScript with Next.js
    newConfig.module.rules.push({
      test: /\.(ts|tsx)$/,
      include: [path.resolve(__dirname, '../')],
      use: [
        {
          loader: 'babel-loader',
          options: {
            presets: ['next/babel'],
            plugins: ['react-docgen'],
          },
        },
      ],
    });
    newConfig.resolve.extensions.push('.ts', '.tsx');

    // Less
    // Remove original less loader
    newConfig.module.rules = baseConfig.module.rules.filter(
      (f) => f.test.toString() !== '/\.less$/'
    );
    newConfig.module.rules.push({
      test: /\.less$/,
      include: [
        // Include antd to rebuild
        /[\/]node_modules[\/].*antd/,
        path.resolve(__dirname, '../'),
      ],
      use: [
        'style-loader',
        'css-loader',
        {
          loader: 'less-loader',
          options: {
            javascriptEnabled: true,
          },
        },
      ],
    });

    //
    // CSS Modules
    // Many thanks to https://github.com/storybookjs/storybook/issues/6055#issuecomment-521046352
    //

    // First we prevent webpack from using Storybook CSS rules to process CSS modules
    newConfig.module.rules.find(
      (rule) => rule.test.toString() === '/\.css$/'
    ).exclude = /\.module\.css$/;

    // Then we tell webpack what to do with CSS modules
    newConfig.module.rules.push({
      test: /\.module\.css$/,
      include: [path.resolve(__dirname, '../')],
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 1,
            modules: true,
          },
        },
      ],
    });

    newConfig.resolve.alias['/images'] = path.resolve(
      __dirname,
      '../public/images'
    );

    return newConfig;
  },
};

我的preview.js

import React from 'react';
import { RouterContext } from 'next/dist/next-server/lib/router-context';
import * as NextImage from 'next/image';

require('../src/assets/styles/antd.less');

export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
};

export const decorators = [
  (Story) => (
    <RouterContext.Provider
      value={{
        push: () => Promise.resolve(),
        replace: () => Promise.resolve(),
        prefetch: () => Promise.resolve(),
      }}
    >
      <Story />
    </RouterContext.Provider>
  ),
];

Object.defineProperty(NextImage, 'default', {
  configurable: true,
  value: (props) => {
    const height = props.height;
    const width = props.width;
    const quotient = height / width;
    const paddingTop = isNaN(quotient) ? '100%' : `${quotient * 100}%`;
    let wrapperStyle;
    let sizerStyle;
    let sizerSvg;
    let toBase64;
    let imgStyle = {
      position: 'absolute',
      top: 0,
      left: 0,
      bottom: 0,
      right: 0,
      boxSizing: 'border-box',
      padding: 0,
      border: 'none',
      margin: 'auto',
      display: 'block',
      width: 0,
      height: 0,
      minWidth: '100%',
      maxWidth: '100%',
      minHeight: '100%',
      maxHeight: '100%',
      objectFit: props.objectFit ? props.objectFit : undefined,
      objectPosition: props.objectPosition ? props.objectPosition : undefined,
    };

    if (
      width !== undefined &&
      height !== undefined &&
      props.layout !== 'fill'
    ) {
      if (props.layout === 'responsive') {
        wrapperStyle = {
          display: 'block',
          overflow: 'hidden',
          position: 'relative',
          boxSizing: 'border-box',
          margin: 0,
        };
        sizerStyle = {
          display: 'block',
          boxSizing: 'border-box',
          paddingTop,
        };
      } else if (props.layout === 'intrinsic' || props.layout === undefined) {
        wrapperStyle = {
          display: 'inline-block',
          maxWidth: '100%',
          overflow: 'hidden',
          position: 'relative',
          boxSizing: 'border-box',
          margin: 0,
        };
        sizerStyle = {
          boxSizing: 'border-box',
          display: 'block',
          maxWidth: '100%',
        };
        sizerSvg = `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg" version="1.1"/>`;
        toBase64 = Buffer.from(sizerSvg).toString('base64');
      } else if (props.layout === 'fixed') {
        wrapperStyle = {
          overflow: 'hidden',
          boxSizing: 'border-box',
          display: 'inline-block',
          position: 'relative',
          width,
          height,
        };
      }
    } else if (
      width === undefined &&
      height === undefined &&
      props.layout === 'fill'
    ) {
      wrapperStyle = {
        display: 'block',
        overflow: 'hidden',
        position: 'absolute',
        top: 0,
        left: 0,
        bottom: 0,
        right: 0,
        boxSizing: 'border-box',
        margin: 0,
      };
    } else {
      throw new Error(
        `Image with src "${props.src}" must use "width" and "height" properties or "layout='fill'" property.`
      );
    }

    return (
      <div style={wrapperStyle}>
        {sizerStyle ? (
          <div style={sizerStyle}>
            {sizerSvg ? (
              <img
                style={{ maxWidth: '100%', display: 'block' }}
                alt={props.alt}
                aria-hidden={true}
                role='presentation'
                src={`data:image/svg+xml;base64,${toBase64}`}
              />
            ) : null}
          </div>
        ) : null}
        <img {...props} decoding='async' style={imgStyle} />
      </div>
    );
  },
});

我不知道在这里做什么。任何帮助将不胜感激!

GitHub (Webpack Building Constantly) describing a similar behavior. They solved it by changing the glob of the stories (1) (2) (3) 上有问题。

Before (constantly re-building): stories: ['../**/*.mdx', '../**/*.stories.tsx']
After (no issues): stories: ['../src/**/*.mdx', '../src/**/*.stories.tsx']

--

This works: stories: ['../../src/components/SomeComponent/index.story.js']
This loops: stories: ['../../**/*.story.js']