storybook: Preset config for TailwindCSS breaks build

Describe the bug I was trying to contribute a preset as advised by @shilman for setting up TailwindCSS, the first step towards that was to setup an advanced preset configuration and copy the working config from webpack.config.js to my-preset.js and import it to presets.js, everything else that was copied works except tailwind.

To Reproduce Steps to reproduce the behavior:

  1. Add the below config in webpack.config.js for tailwind:
const path = require('path');
module.exports = ({ config, mode }) => {
    const svelteLoader = config.module.rules.find(
        r => r.loader && r.loader.includes('svelte-loader'),
    );

    
    svelteLoader.options = {
        ...svelteLoader.options,
        emitCss: true,
        hotReload: false,
    };
    config.module.rules = [
        ...config.module.rules,
        {
            test: /\.css$/,
            include: path.resolve(__dirname, '../src'),
            use: [
                {
                    loader: 'postcss-loader',
                    options: {
                        sourceMap: true,
                        config: {
                            path: './.storybook/',
                        },
                    },
                },
            ],
        },
    ];

    return config;


};
  1. Add a postcss.config.js config in your storybook folder:
var tailwindcss = require('tailwindcss');

module.exports = {
    plugins: [require('postcss-import')(), tailwindcss('./tailwind.config.js'), require('autoprefixer')],
};
  1. Compile and it works including @apply tags in components
  2. delete the webpack.config.js and add two files presets.js and my-presets.js
  3. Add this under presets.js
const path = require('path');

module.exports = [path.resolve('./.storybook/my-preset')];
  1. Add this under my-preset.js and compile - it breaks!
const path = require('path');
const tailwindcss = require('tailwindcss');

async function webpack(webpackConfig, options) {
    //Svelteloader
    const svelteLoader = webpackConfig.module.rules.find(
        r => r.loader && r.loader.includes('svelte-loader'),
    );

    svelteLoader.options = {
        ...svelteLoader.options,
        emitCss: true,
        hotReload: false,
    };

    const { module = {} } = webpackConfig;
    const { loaderOptions, rule = {} } = storysourceOptions;

    return {
        ...webpackConfig,
        module: {
            ...module,
            rules: [
                ...(module.rules || []),
                // TailwindCSS config
                {
                    test: /\.css$/,
                    include: path.resolve(__dirname, '../src'),
                    use: [
                {
                    loader: 'postcss-loader',
                    options: {
                        sourceMap: true,
                        config: {
                            path: './.storybook/',
                        },
                    },
                },
            ],
                },
            ],
        },
    };
}

module.exports = { webpack };

  1. Some advised this config instead and still breaks
const path = require('path');
const tailwindcss = require('tailwindcss');

async function webpack(webpackConfig, options) {
    //Svelteloader
    const svelteLoader = webpackConfig.module.rules.find(
        r => r.loader && r.loader.includes('svelte-loader'),
    );

    svelteLoader.options = {
        ...svelteLoader.options,
        emitCss: true,
        hotReload: false,
    };

    const { module = {} } = webpackConfig;
    const { loaderOptions, rule = {} } = storysourceOptions;

    return {
        ...webpackConfig,
        module: {
            ...module,
            rules: [
                ...(module.rules || []),
                // TailwindCSS config
                {
                    test: /\.css$/,
                    include: path.resolve(__dirname, '../src'),
                    use: [
                        {
                            loader: 'css-loader',
                        },
                        {
                            loader: 'postcss-loader',
                            options: {
                                plugins: () => [
                                    tailwindcss('./tailwind.config.js'),
                                    require('autoprefixer'),
                                ],
                            },
                        },
                    ],
                },
            ],
        },
    };
}

module.exports = { webpack };
  1. Some advised on ordering of plugins and tried different combination and still doesn’t work and every time its the same error as shown below:
ERROR in ./src/styles/tailwind.css
Module build failed (from ./node_modules/postcss-loader/src/index.js):
SyntaxError

(2:1) Unknown word

  1 |
> 2 | var content = require("!!../../node_modules/css-loader/dist/cjs.js??ref--9-1!../../node_modules/postcss-loader/src/index.js??post
css!./tailwind.css");
    | ^
  3 |
  4 | if(typeof content === 'string') content = [[module.id, content, '']];

 @ ./.storybook/config.js 7:0-36
 @ multi ./node_modules/@storybook/core/dist/server/common/polyfills.js ./node_modules/@storybook/core/dist/server/preview/globals.js .
/.storybook/config.js (webpack)-hot-middleware/client.js?reload=true&quiet=true
Child HtmlWebpackCompiler:
                          Asset      Size               Chunks  Chunk Names
    __child-HtmlWebpackPlugin_0  6.35 KiB  HtmlWebpackPlugin_0  HtmlWebpackPlugin_0
    Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
    [./node_modules/html-webpack-plugin/lib/loader.js!./node_modules/@storybook/core/dist/server/templates/index.ejs] 2.15 KiB {HtmlWeb
packPlugin_0} [built]

WARN Broken build, fix the error above.
WARN You may need to refresh the browser.

I import the tailwind.css in a central place at ./.storybook/config.js

Expected behavior I’m expecting the webpack config both inside preset and outside to behave in the same way with the same configuration.

System: Environment Info:

System: OS: Windows 7 Binaries: Node: 10.16.0 - C:\Program Files\nodejs\node.EXE Yarn: 1.19.0 - C:\Program Files (x86)\Yarn\bin\yarn.CMD npm: 6.9.0 - C:\Program Files\nodejs\npm.CMD

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 21 (15 by maintainers)

Most upvoted comments

@jerriclynsjohn Was just having an unrelated conversation about Ember framework preset configuration with @dbendaou and we should have some patterns to work off of in the next few days. We’ll follow up here.

Would it help if the preset itself was opt-in? So by default your main.js (for react) would look like:

module.exports = {
  stories: ...,
  presets: ['@storybook/react/preset'],
}

Then you could just remove that react preset if you didn’t want any of the base rules.

Alternatively, how about this:

module.exports = {
  stories: ...
  disabledPresets: ['@storybook/react/preset']
}

I am trying a similar setup for a custom PostCSS config. It works inside a custom webpack.config.js file but not when loaded via a preset.

When I console.log the config from within the preset file, like this:

async function webpack(config, options) {
  console.log(config.module.rules);

  return config;
}

I only see these JS-related rules:

{ test: /\.(mjs|jsx?)$/,
  use: [ { loader: 'babel-loader', options: [Object] } ],
  include: [ './demo' ],
  exclude: [ './demo/node_modules' ] }
{ test: /\.md$/,
  use:
   [ { loader:
        './demo/node_modules/raw-loader/dist/cjs.js' } ] }
{ test: /\.(stories|story).mdx$/,
  use:
   [ { loader: 'babel-loader', options: [Object] },
     { loader: '@mdx-js/loader', options: [Object] } ] }
{ test: /\.mdx$/,
  exclude: /\.(stories|story).mdx$/,
  use:
   [ { loader: 'babel-loader', options: [Object] },
     { loader: '@mdx-js/loader' } ] }
{ test: /\.(stories|story)\.[tj]sx?$/,
  loader:
   './demo/node_modules/@storybook/source-loader/dist/server/index.js',
  options: {},
  enforce: 'pre' }

but when I log the same thing from within the webpack file I see the other rules for css, svg, mp4, etc.

It looks like the presets API doesn’t have access to the css rule.

@shilman this is possibly related to the issues we have had. But It would need more testing…