Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support css-modules #4405

Merged
merged 27 commits into from
Oct 22, 2018
Merged

Support css-modules #4405

merged 27 commits into from
Oct 22, 2018

Conversation

chadfawcett
Copy link
Member

@chadfawcett chadfawcett commented Oct 13, 2018

Issue: Resolve #4306

What I did

Created a react webpack preset that pulls style rules from create-react-app's congis.

  • Support for css modules (*.module.css)
  • Support for sass (scss and sass)
  • Support for sass modules (*.module.scss and *.module.sass)

> The default Webpack config of Storybook is balanced for a medium-size project (specially created with Create React App) or a library.

I know @ndelangen mentioned that this statement is a little outdated, but I feel this change should be okay as it shouldn't cause any harm adding css modules as is.

@igor-dv I chose to propose the simpler option first, as presets aren't well defined yet, and I saw @ndelangen was working on separating the manager and preview webpack processes. Either of these may result in a better implementation of the CRA webpack config in the future.

I can work on implementing the presets approach if this method is not sufficient. What are your thoughts?

How to test

You can update examples/cra-kitchen-sink to the following in order to test (example code taken from initial reporting issue):

//  App.module.css (New file)
.text {
  color: red;
  font-size: 2rem;
}
//  App.js (Existing file)
...
import styles from './App.module.css'

const App = () => (
  ...
    <h2 className={styles.text}>Welcome to React</h2>
  ...
)

Then run yarn storybook from the examples/cra-kitchen-sink repo and the "Welcome to React" will be red.

screen shot 2018-10-12 at 9 31 25 pm

Does this need a new example in the kitchen sink apps?
Maybe? I can add something similar to the code above if we want it committed into the repo.

@codecov
Copy link

codecov bot commented Oct 13, 2018

Codecov Report

Merging #4405 into master will decrease coverage by 0.21%.
The diff coverage is 0%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master   #4405      +/-   ##
=========================================
- Coverage   35.91%   35.7%   -0.22%     
=========================================
  Files         555     557       +2     
  Lines        6668    6708      +40     
  Branches      871     879       +8     
=========================================
  Hits         2395    2395              
- Misses       3826    3858      +32     
- Partials      447     455       +8
Impacted Files Coverage Δ
app/react/src/server/options.js 0% <ø> (ø) ⬆️
lib/core/src/server/core-preset-webpack-custom.js 0% <0%> (ø) ⬆️
app/react/src/server/cra_config.js 0% <0%> (ø)
lib/core/src/server/config.js 0% <0%> (ø) ⬆️
...pp/react/src/server/framework-preset-cra-styles.js 0% <0%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update db8be4b...6297d48. Read the comment docs.

@ndelangen
Copy link
Member

Awesome work @chadfawcett 🎈

@gabrielcsapo
Copy link
Contributor

@chadfawcett love the change! Would this be better isolated in the examples rather than supported at the core level?

@chadfawcett
Copy link
Member Author

@gabrielcsapo if it was isolated at the example level, wouldn’t that mean it was an end user override similar to the workaround proposed in the original issue?

@ndelangen
Copy link
Member

@chadfawcett is correct here.

I think the best approach would be to narrow this into a preset specific for either React of CRA.

I'm sure @igor-dv will be able to help with this.

@chadfawcett
Copy link
Member Author

I’ve got a fairly good idea of how to implement the preset, but it’s a lot more work to merge the default and cra configs. So I thought I’d propose this solution first 😜

@ndelangen
Copy link
Member

I fear this will not work well with other frameworks.

pksunkara
pksunkara previously approved these changes Oct 13, 2018
@pksunkara pksunkara dismissed their stale review October 13, 2018 16:28

Nevermind

@chadfawcett
Copy link
Member Author

@ndelangen Would it cause anymore issues with other frameworks than the existing css rules?

Copy link
Member

@igor-dv igor-dv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to be very careful with the changes like this since you are adding it to the default webpack config and it may really break other usages/frameworks without the real benefit to them. Also, the convention of file.module.css - I am not sure how much this is common (for example I don't use it =). So, who is really winning from this change?

I’ve got a fairly good idea of how to implement the preset, but it’s a lot more work to merge the default and cra configs

Integration with CRA is acutaly the thing we need to do 🤷‍♂️ . Also, I assume we do not need to merge the full config, but only styling, at least for now IMO (I didn't see any other issues that are not related to styling).

P.S sorry for being unresponsive, I am returning from the vacation tomorrow =)

@chadfawcett
Copy link
Member Author

@igor-dv Okay so it seems the consensus is that updating the default config directly is bad, and we should use a preset. There are two approaches we could take:

  1. Detect cra and automatically merge in parts of the cra webpack config (I could use your feedback on my proposed method of detection)
  2. Apply css module support to all @storybook/react. This would just be a css modules preset that we add to storybook/app/react/src/server/options.js

For reference, I figured #4298 could also be fixed if we merged more than just the style rules.

Thoughts?

@igor-dv
Copy link
Member

igor-dv commented Oct 13, 2018

I vote for 1 =) Anything neede to be merged should be merged

@ndelangen
Copy link
Member

Would be cool if we'd load the actual webpack config CRA uses, and merge the relevant bits

@gabrielcsapo
Copy link
Contributor

I agree 1 is the way to go

@Hypnosphi
Copy link
Member

Hypnosphi commented Oct 14, 2018

I fear this will not work well with other frameworks.

Why? CSS Modules are completely framework agnostic and has no direct connection with React

@ndelangen
Copy link
Member

@Hypnosphi you're 100% correct as always.
I guess if we test for /.module.(css|scss|sass)$/ this should be fine.
That's very unlikely to clash with rules already set by the user?

But long term we want to move all these things into presets right?

@chadfawcett
Copy link
Member Author

@Hypnosphi @ndelangen That's what I thought. It may not be the best implementation, but it's and easy win that shouldn't cause issue with anything else.

Should I update the regex to support sass/scss and then call this PR done? Leave the CRA preset to a later PR?

Copy link
Member

@kylemh kylemh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

"react-dev-utils": "^6.0.5"
"react-dev-utils": "^6.0.5",
"semver": "^5.6.0",
"webpack": "^4.21.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I wonder if we could make Webpack 4+ a peer dep instead?

Copy link
Member

@igor-dv igor-dv Oct 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

webpack is a direct dep in a bunch of the SB packages (including core). It will be complicated to make it a peer (like for supporting different major version of webpack)

CC #4371, #4430

Edit: this is not a CR comment, just a reply to @kylemh =)

const plugins = [...baseConfig.plugins];
if (baseConfig.mode === 'production')
plugins.push(
new MiniCssExtractPlugin({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this exclude interop for CRAv1? I don't really care about that, but it could be something to consider.

}
}

export function getStyleRules(rules) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to get some comments throughout this function. Not certain what's occurring.

It looks like a way to concatenate across the subset of style loading possibilities given the array of file suffixes we're sifting?


export function applyCRAWebpackConfig(baseConfig) {
// Remove style rules from baseConfig
const baseRulesExcludingStyles = baseConfig.module.rules.filter(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice 👏

const craWebpackConfig = getCraWebpackConfig(baseConfig.mode);

// Concat will ensure rules is an array
const craStyleRules = getStyleRules([].concat(craWebpackConfig.module.rules));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we not simply do:

[ ...craWebpackConfig.module.rules ]

Interested to hear more on "ensur[ing] rules is an array"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops. I was thinking module.rules could be something other than an array. This is the case for rule.test (array, regex, function, string, etc) but not the case for module.rules. I've removed the concat statement.

const plugins = [...baseConfig.plugins];
if (baseConfig.mode === 'production')
plugins.push(
new MiniCssExtractPlugin({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW we dont really need autoprefixer in the default ruleset either 🤷‍♂️

I'm a fan of good defaults 👍

@kylemh
Copy link
Member

kylemh commented Oct 20, 2018

(This comment isn't about this PR, but a general point of discussion)
This preset approach is very interesting.

I wonder if we could set ground rules and documentation about adding new ones.

Most importantly, I wonder if we could give the user control in using presets thus removing the need for us to handle edge-cases. Presets can only be selected under X assumptions - otherwise, you're on your own.

Copy link
Member

@igor-dv igor-dv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I am happy 👍

"react-dev-utils": "^6.0.5"
"react-dev-utils": "^6.0.5",
"semver": "^5.6.0",
"webpack": "^4.21.0"
Copy link
Member

@igor-dv igor-dv Oct 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

webpack is a direct dep in a bunch of the SB packages (including core). It will be complicated to make it a peer (like for supporting different major version of webpack)

CC #4371, #4430

Edit: this is not a CR comment, just a reply to @kylemh =)

// eslint-disable-next-line global-require, import/no-extraneous-dependencies, prefer-destructuring
normalizeCondition = require('webpack/lib/RuleSet').normalizeCondition;
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
MiniCssExtractPlugin = require('mini-css-extract-plugin');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you can require it already in the applyCRAWebpackConfig func and remove the state from the module (IMO Node caches imported modules, so you don't need to care about this issue).

@igor-dv
Copy link
Member

igor-dv commented Oct 21, 2018

@kylemh

I wonder if we could set ground rules and documentation about adding new ones.

We should, it's on my todo, at least for the maintainers,

Most importantly, I wonder if we could give the user control in using presets thus removing the need for us to handle edge-cases. Presets can only be selected under X assumptions - otherwise, you're on your own.

Custom presets could be used by the users, but we didn't want to open this feature until #4169 (and because we kinda need to tests this 😅 )

Regarding the "selective" presets thingy, how can the API for this look like?

@igor-dv
Copy link
Member

igor-dv commented Oct 21, 2018

Almost forgot. Do we have any CLI tests testing this setup?

@shilman
Copy link
Member

shilman commented Oct 22, 2018

@chadfawcett @igor-dv @ndelangen would love to get this into an RC -- any chance we can merge this today?

@igor-dv igor-dv merged commit d704510 into storybookjs:master Oct 22, 2018
@igor-dv
Copy link
Member

igor-dv commented Oct 22, 2018

The CLI tests could be added after

@chadfawcett chadfawcett deleted the css-modules branch October 22, 2018 16:49
@chadfawcett
Copy link
Member Author

Sorry I was in the middle of responding to comments when GitHub started having issues yesterday. Glad my latest push synced in time.

@igor-dv As for the CLI tests, all of the fixtures have React 15 as dependencies. This brought to light that I needed to test that react-scripts was at least v2, but that meant the CLI tests were no longer testing this new preset.

Should the fixtures be updated to React 16? Or should additional fixtures be created?

@igor-dv
Copy link
Member

igor-dv commented Oct 23, 2018

I think React 15 is not supported now, I am not sure how it works. But yeah, you can just create a new one.

const plugins = [...baseConfig.plugins];
if (baseConfig.mode === 'production') {
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a second thought, I think this can make problems if 'mini-css-extract-plugin' is not installed as the root package in node_modules 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we need to do something like this instead ?

const miniCssExtractPlugin = craWebpackConfig.plugins.find(plugin => plugin.constructor.name === 'MiniCssExtractPlugin');

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you were right initially. Since we check to make sure react-scripts is installed before calling applyCRAWebpackConfig (I Just noticed this has all caps for CRA), then we know mini-css-extract-plugin is present.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to use it, we have to depend on it

Copy link
Member

@Hypnosphi Hypnosphi Oct 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we know mini-css-extract-plugin is present.

There's no guarantee that it's available in root node_modules not in node_modules/react-scripts/node_modules. You can never rely on npm hoisting

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like #4524 is broken now because of this

Copy link
Member Author

@chadfawcett chadfawcett Oct 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #4534 to hopefully resolve this

@@ -33,12 +33,15 @@
"@babel/runtime": "^7.1.2",
"@emotion/styled": "^0.10.6",
"@storybook/core": "4.0.0-rc.1",
"@storybook/node-logger": "^3.4.11",
Copy link
Member

@Hypnosphi Hypnosphi Oct 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be "4.0.0-rc.3"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants