Storybook: Full-control webpack config with default results in duplicate style loader rules

Created on 5 Oct 2018  路  9Comments  路  Source: storybookjs/storybook

We use storybook with angular in full-control webpack.config mode (to configure ts aliases). In v4.0.0-alpha.20 this worked fine, but sincd v4.0.0-alpha.21, we get the error "Module build failed: Unknown word" while loading CSS file. This is because module.rules includes css rules twice - once from angular-cli and once from the storybook's default webpack. I guess this should be migitgated by applyAngularCliWebpackConfig which extracts rulesExcludingStyles for this reason. However, this function is no longer called to generate the default config passed to our custom webpack function.

I guess this problem was introduced with presets (#4027), but I have not looked further into it.

Steps to reproduce

If the description is not clear enough, I can try to make an MCVE.

Please specify which version of Storybook and optionally any affected addons that you're running

  • @storybook/angular 4.0.0-alpha.21 through alpha.24
angular babel / webpack bug presets

All 9 comments

These symptoms sound just like what I'm seeing in a React project, although we are pinned at alpha.16.

We use the full control + default mode and our setup looks roughly like

import ourConfig from 'path/to/config'

module.exports = (baseConfig, env, defaultConfig) => {
  const merged = {
    // keep storybook's defaults
    ...defaultConfig,

    // add our rules (required for SASS loaders, js/happypack, etc)
    module: {
      rules: [
        ...baseConfig.module.rules,
        ...ourConfig.module.rules
      ]
    },

    // add our plugins (babel transforms, etc)
    plugins: [...defaultConfig.plugins, ...ourPluginsFiltered ],

    // our changes to `resolve`
    resolve: { ...defaultConfig.resolve, ...ourConfig.resolve }
  }

  return merged
}

In our case the error is coming from postcss-loader

storybook unknown word css error

which is added by defaultConfig.module.rules. I switched the rules section to use baseConfig.module.rules, leaving defaultConfig for the other parts and things work as expected.

Definitely a workaround but posting in case it unblocks someone else.

@Yogu, I don't know what is MCVE is, but if it will help me to reproduce that'd be great. Also, please share your custom webpack.config for Storybook.

@jfsiii, It looks not related to the Angular issue. BTW, defaultConfig contains some rules for css, so merging it as you did could mess things up. Can you please open another issue for this with a bit more details - for example, what is the content of ourConfig.

@igor-dv Here's more info about our setup


Failing webpack.config.js (extends default config)

worked until a story imported a component which had an import from 'some/package/dist/style.css

const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ourConfig = require('../webpack')

const ourPluginsFiltered = ourConfig.plugins
  // filter our HMR plugin since Storybook has its own
  // otherwise get a `SyntaxError: Maximum call stack exceeded` error
  .filter(p => !(p instanceof webpack.HotModuleReplacementPlugin))
  // exclude any of our `HtmlWebpackPlugin` entries
  // they're for creating our own entry points (index.html, unsupported.html, etc)
  // This also prevents _our_ `index.html` from clobbering storybook's file
  .filter(p => !(p instanceof HtmlWebpackPlugin))

const storySourceRule = {
  test: /(\b|\.)stories\.js$/,
  exclude: /node_modules/,
  loaders: [
    {
      loader: require.resolve('@storybook/addon-storysource/loader'),
      options: {
        uglyCommentsRegex: [
          /^eslint-.*/,
          /^global.*/,
        ]
      }
    }
  ],
  enforce: 'pre',
}

module.exports = (baseConfig, env, defaultConfig) => {
  const merged = {
    // keep storybook's defaults
    ...defaultConfig,

    // add our rules (required for SASS loaders, js/happypack, etc)
    module: {
      rules: [
        ...defaultConfig.module.rules,
        ...ourConfig.module.rules,
        storySourceRule
      ],
    },

    // add our plugins (babel transforms, etc)
    plugins: [...defaultConfig.plugins, ...ourPluginsFiltered ],

    // our changes to `resolve` which allow `import`s like
    // import Button from 'components/interactive/LinkButton'
    // vs
    // import Button from './client/components/interactive/LinkButton'
    resolve: { ...defaultConfig.resolve, ...ourConfig.resolve }
  }

  return merged
}


Working webpack.config.js (extends base config)

worked after changing rules section to use baseConfig instead of defaultConfig

const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ourConfig = require('../webpack')

const ourPluginsFiltered = ourConfig.plugins
  // filter our HMR plugin since Storybook has its own
  // otherwise get a `SyntaxError: Maximum call stack exceeded` error
  .filter(p => !(p instanceof webpack.HotModuleReplacementPlugin))
  // exclude any of our `HtmlWebpackPlugin` entries
  // they're for creating our own entry points (index.html, unsupported.html, etc)
  // This also prevents _our_ `index.html` from clobbering storybook's file
  .filter(p => !(p instanceof HtmlWebpackPlugin))

const storySourceRule = {
  test: /(\b|\.)stories\.js$/,
  exclude: /node_modules/,
  loaders: [
    {
      loader: require.resolve('@storybook/addon-storysource/loader'),
      options: {
        uglyCommentsRegex: [
          /^eslint-.*/,
          /^global.*/,
        ]
      }
    }
  ],
  enforce: 'pre',
}

module.exports = (baseConfig, env, defaultConfig) => {
  const merged = {
    // keep storybook's defaults
    ...defaultConfig,

    // add our rules (required for SASS loaders, js/happypack, etc)
    module: {
      rules: [
        // using `baseConfig` instead of `defaultConfig` because 
        // default included additional CSS rules which caused errors
        // when importing a component which imported a CSS file, like
        // `import from 'read/dist/style.css'`
        ...defaultConfig.module.rules,
        ...ourConfig.module.rules,
        storySourceRule

      ],
    },

    // add our plugins (babel transforms, etc)
    plugins: [...defaultConfig.plugins, ...ourPluginsFiltered ],

    // our changes to `resolve` which allow `import`s like
    // import Button from 'components/interactive/LinkButton'
    // vs
    // import Button from './client/components/interactive/LinkButton'
    resolve: { ...defaultConfig.resolve, ...ourConfig.resolve }
  }

  return merged
}

ourConfig is a webpack config object which has different values depending on the environment


CSS rules from ourConfig

[ { loader: 'style-loader', options: { sourceMap: true } },
  { loader: 'css-loader',
    options: { sourceMap: true, minimize: false } },
  { loader: 'resolve-url-loader', options: { sourceMap: true } } ]


CSS rules from defaultConfig

[ '/Users/jfsiii/work/stae-product/node_modules/@storybook/core/node_modules/style-loader/index.js',
  { loader:
     '/Users/jfsiii/work/stae-product/node_modules/css-loader/index.js',
    options: { importLoaders: 1 } },
  { loader:
     '/Users/jfsiii/work/stae-product/node_modules/@storybook/core/node_modules/postcss-loader/lib/index.js',
    options:
     { ident: 'postcss', postcss: {}, plugins: [Function: plugins] } } ]

When I use baseConfig, which has no CSS rules, the error disappears


baseConfig rules

[ { test: /\.js$/,
    use: [ [Object] ],
    include: [ '/Users/jfsiii/work/stae-product' ],
    exclude: [ '/Users/jfsiii/work/stae-product/node_modules' ] }

For documentation
This happens in alpha.25 with an fresh ng new my-project, sb init, build-storybook

Stack trace

info @storybook/angular v4.0.0-alpha.25
info 
info => Loading presets
info => Loading custom addons config.
info => Found custom tsconfig.json
info => Loading angular-cli config.
info => Get angular-cli webpack config.
info => Using default webpack setup based on "angular-cli".
info Building storybook ...
Starting type checking service...
Using 1 worker with 2048MB memory limit
ERR! Failed to build the storybook
ERR! ./src/styles.css (./node_modules/raw-loader!./node_modules/postcss-loader/lib??embedded!./node_modules/@storybook/core/node_modules/style-loader!./node_modules/css-loader??ref--17-1!./node_modules/@storybook/core/node_modules/postcss-loader/src??postcss!./src/styles.css)
ERR! Module build failed (from ./node_modules/postcss-loader/lib/index.js):
ERR! Syntax Error 
ERR! 
ERR! (2:1) Unknown word
ERR! 
ERR!   1 | 
ERR! > 2 | var content = require("!!../node_modules/css-loader/index.js??ref--17-1!../node_modules/@storybook/core/node_modules/postcss-loader/src/index.js??postcss!./styles.css");
ERR!     | ^
ERR!   3 | 
ERR!   4 | if(typeof content === 'string') content = [[module.id, content, '']];
ERR! 
ERR!  @ ./src/styles.css 2:14-325
ERR!  @ multi ./node_modules/@storybook/core/dist/server/config/polyfills.js ./node_modules/@storybook/core/dist/server/config/globals.js ./.storybook/config.js ./src/styles.css
info Building storybook completed.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] build-storybook: `build-storybook`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] build-storybook script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\kro\AppData\Roaming\npm-cache\_logs\2018-10-15T11_58_51_257Z-debug.log

Process finished with exit code 1

Maybe we are not filtering out our default css rules well for angular-cli?

Maybe we are not filtering out our default css rules well for angular-cli?

That's what I think, too. The code for this is still there (applyAngularCliWebpackConfig), but apparently it's not executed in the right spot anymore since alpha 21.

Fixed by https://github.com/storybooks/storybook/pull/4431
Published in 4.0.0-rc.1

@kroeder with this update there is no way to adjust angular-cli plugins/loaders cause they are merged after custom config.
We need to adjust includePaths option for scss loader and remove CopyWebpackPlugin that copies assets from angular.json that are not related to working components (monorepo with different angular projects with one angular.json at the repo root)

@artaommahe, it will be fixed with #4405

Was this page helpful?
0 / 5 - 0 ratings