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:
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;
};
postcss.config.js config in your storybook folder:var tailwindcss = require('tailwindcss');
module.exports = {
plugins: [require('postcss-import')(), tailwindcss('./tailwind.config.js'), require('autoprefixer')],
};
webpack.config.js and add two files presets.js and my-presets.jspresets.jsconst path = require('path');
module.exports = [path.resolve('./.storybook/my-preset')];
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 };
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 };
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
Thanks for working on this @jerriclynsjohn!
@igor-dv do you mind taking a look at this to help out?
@igor-dv I'm not sure what's causing the problem, but since this works in the plain webpack config I'm assuming that this has nothing to do with Tailwind or PostCSS. Do let me know
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.
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
@shilman @igor-dv Hey, it would be a great if someone could look into this
TBH I don't know what's going on here. I believe @mrmckeb has also seen some weirdness with the presets & when things get added to the webpack config. I don't know if it's a bug or desired behavior @igor-dv
@shilman this is possibly related to the issues we have had. But It would need more testing...
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
Can we keep this one alive? Or maybe rebrand it as a more generic issue such as "Preset API does not allow modification of non-JS rules in Webpack config"?
The problem is Storybook adds some assets related rules after presets' webpack called. If you want to add a rule for the assets, you need to filter them in webpackFinal. (See https://github.com/storybookjs/vue-cli-plugin-storybook/blob/master/preset.js#L43, https://github.com/pocka/storybook-preset-nuxt/blob/master/storybook-preset-nuxt/src/index.ts#L106)
Something like this (I'm not sure this works):
const path = require('path');
const tailwindcss = require('tailwindcss');
async function webpackFinal(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.filter(rule => !rule instanceof RegExp || !rule.test('.css')),
// 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 = { webpackFinal };
I think it's a good time to allow preset to prevent Storybook core from adding base rules, instead of hard-coding package name.
Yeah @pocka we're starting SB6.0 and it's a good time to make this kind of breaking change. What do you propose?
cc @mrmckeb
@shilman
I think having an option like noBaseRules would be straightforward.
// some-preset.js
const webpack = (config, opts) => {/* ... */}
module.exports = { webpack, noBaseRules: true }
// Allowing turning on/off each rule might better
// but we need to update these flags when we add/remove base loaders
module.exports = {
webpack,
baseRules: {
css: false,
assetFiles: true
}
}
// or completly disable base config
module.exports = { webpack, noBaseConfig: true }
// or putting it on an options object?
module.exports = { webpack, options: { noBaseRules: true }}
The downside of this method is conflicting between plugins: one plugin sets noBaseRules: true and another sets noBaseRules: false then what will happen...? etc
I like the idea of turning off selected rules 馃憤馃徎
Currently we have to do this:
config.module.rules = config.module.rules.filter(f => {
return f.test.toString() !== '/\\.css$/'
});
to clear the way for our CSS rules.
Maybe the naming of the flags could be inverted?
{
baseRules: false,
baseConfig: false
}
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']
}
@shilman
Yes, if we move baseWebpackConfig into each frameworks' presets. So simple and modular approach!
One thing I concern about is how users feel: we always need to set a preset for the framework... (e.g. users need to write presets: "@storybook/react/preset" even though they don't use any other presets)
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
I would really love to revisit this, what can we do to make this happen?
@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.
@shilman do let me know if there's any progress or maybe you could link the related issue here so that I'm updated about it.
@jerriclynsjohn Two relevant PRs:
reactOptions: https://github.com/storybookjs/storybook/pull/12470emberOptions: https://github.com/storybookjs/storybook/pull/12440
Most helpful comment
@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.