birdofpreyru / babel-plugin-react-css-modules

Transforms styleName to className using compile time CSS module resolution.
https://dr.pogodin.studio/docs/babel-plugin-react-css-modules
Other
30 stars 12 forks source link

Implement a "SCSS preset" supporting all standard SCSS features out of the box (those for which just postcss-scss is not enough) #34

Open vlastimil-dolejs opened 2 years ago

vlastimil-dolejs commented 2 years ago

Hi, I have trouble making the plugin work with Webpack 5 + SCSS. I had a working setup with the original gajus/babel-plugin-react-css-modules, Webpack 4, and SCSS. During the migration to Webpack 5 I had to swap the original plugin for yours (thank you for maintaining the fork!). The configuration remained mostly the same.

I have created a minimalistic example here: https://github.com/vlastimil-dolejs/css-modules-scss-bug

The error I get is:

ERROR in ./src/Test.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
CssSyntaxError: C:\code\css-modules-scss-bug\src\Test.scss:6:4: Expected a pseudo-class or pseudo-element.
    at C:\code\css-modules-scss-bug\src\Test.scss:6:3
    at Input.error (C:\code\css-modules-scss-bug\node_modules\postcss\lib\input.js:148:16)
    at Rule.error (C:\code\css-modules-scss-bug\node_modules\postcss\lib\node.js:60:32)
    at Root._error (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:177:25)
    at Root.error (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\selectors\root.js:43:19)
    at Parser.error (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:740:21)
    at Parser.expected (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:1133:19)
    at Parser.pseudo (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:875:19)
    at Parser.parse (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:1084:14)
    at Parser.loop (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:1043:12)
    at new Parser (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:164:10)
 @ ./src/index.js 3:0-26 4:50-54

The error is thrown, when there is some special SASS construct in the .scss file. It looks like the class name lookup is done in .scss file before it is processed by postcss-scss.

Relevant part of babel.config.js

const plugins = [
    ["@dr.pogodin/react-css-modules", {
        "exclude": "node_modules",
        "generateScopedName": "[path][name]__[local]",
        "filetypes": {
            ".scss": {
                "syntax": "postcss-scss"
            }
        }
    }]
];

Relevant part of webpack.config.js

{
    test: /\.scss$/,
    exclude: /node_modules/,
    use: [
        MiniCssExtractPlugin.loader,
        {
            loader: "css-loader",
            options: {
                modules: {
                    localIdentName: "[path][name]__[local]",
                },
                importLoaders: 1, // to include sass-loader
                sourceMap: true,
            }
        },
        "sass-loader"
    ]
}
birdofpreyru commented 2 years ago

Ok. I'll check within next few days.

birdofpreyru commented 2 years ago

Hey @vlastimil-dolejs ,

It looks to me the problem in your example is that you are using SASS interpolation syntax in selector name. You have postcss-scss syntax parser configured for SCSS files, but that parser on its own does not compile SCSS instructions, so babel-plugin-react-css-modules still sees .#{$selector-var} selector rather than .test you expect it to be, hence the failure.

To make it work with this syntax you should add a PostCSS plugin to transform that syntax, and that plugin must be synchronous. The closest I found is https://github.com/postcss/postcss-simple-vars. I think if you try in the configuration this:

            "filetypes": {
                ".scss": {
                    "syntax": "postcss-scss"
                },
                "plugins": ["postcss-simple-vars"],
            }

and if you write the selector as .$selector-var (the interpolation syntax understood by postcss-simple-vars), it will work.

The caveat is sure that to work with the original SASS interpolation syntax you need to find (or write) a slightly different plugin which, at least, transforms selectors like .#{$var} into just .$var (if you do this before postcss-simple-vars, then that plugin will take care of the rest).


With this said, I believe it is not a bug in babel-plugin-react-css-modules, and we can close this ticket, as the support of specific SASS syntax pre-compilation is not a part of this project. Sure, if you find / write a PostCSS plugin which solves this problem, I'll be glad to mention it in README of this plugin.

vlastimil-dolejs commented 2 years ago

Hi @birdofpreyru, Thank you for the analysis. I have misunderstood the role of the postcss-scss syntax. You are correct that without the plugins the syntax reads the content of the scss file but doesn't resolve any dynamic features of the SASS. It's not only the variable interpolation but also some other features. I will probably stick with plain CSS modules without styleNames. Thank you!

birdofpreyru commented 2 years ago

Just curious, what other features you found causing problems? I'm myself using this plugin with SCSS, and had no issues, as postcss-scss still covers most of stuff, as long as you don't do something exotic with classnames (e.g. I even didn't know/need before that it is possible to interpolate variable inside classnames, while interpolation inside rule values is fine for this plugin).

vlastimil-dolejs commented 2 years ago

I had a problem with:

Property nesting (https://sass-lang.com/documentation/style-rules/declarations#nesting) - this can be solved with postcss-nested-props but it causes problems with CSS modules :global() keyword (.my-button:global(.btn) {...})

Parent selector used for class composition:

.foo {
  &-bar {
  }
}

Class .foo-bar is available only after compilation.

And the already mentioned variable interpolation.

birdofpreyru commented 2 years ago

Thanks. It makes me think to pre-assemble a sort of "preset", which is easy to enable and have all standard SCSS features supported out of the box, rather than having to hunt for individual PostCSS plugins for individual features. Not sure I have time to do it in the nearest future.