webpack-contrib / css-loader

CSS Loader
MIT License
4.31k stars 601 forks source link

Exporting variables from SCSS file to JavaScript no longer works in css-loader 4.x.x #1179

Closed felipecarrillo100 closed 4 years ago

felipecarrillo100 commented 4 years ago

Other: sass-loader: "8.0.2", style-loader: "1.2.1",

Expected Behavior

In version CSS-loader 3.x.x I was able to import SCSS variables but since I migrated to css-loader 4.x.x this no longer works, now I get an empty object.

Example: webpack.config.js

                {
                    test: /\.(css|scss|sass)$/,
                    loader: [
                         'style-loader',
                         'css-loader',
                        'sass-loader'
                    ],
                }

_export.scss

// variables.scss
$white-color: #fcf5ed;
$dark-color: #402f2b;
$light-color: #e6d5c3;

// the :export directive is the magic sauce for webpack
:export {
  whitecolor: $white-color;
  darkcolor: $dark-color;
  lightcolor: $light-color;
}

somefile.js

const variables = require("../../../../../theme/_export.scss");
console.log(variables)

With css-loader 3.x.x and older everything works fine as expected. I get variables as an object containing the values { whitecolor: "#fcf5ed"; darkcolor: "#402f2b"; lightcolor: "#e6d5c3" }

Actual Behavior

Since migrated to css-loader 4.x.x instead of the expected values I get an empty object {} If I role back to an older version of css-loader everything works again as expected.

Code

This is a full copy of my webpack.config.js

'use strict';

const path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebPackPlugin = require("html-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = (env, argv) => {
    const mode = argv.mode || process.env.NODE_ENV;
    console.log("Mode: " + mode);
    return {
        entry: ['./src/index.tsx'],
        output: {
            path: path.resolve(__dirname, "dist"),
            filename: 'index.js'
        },
        devtool: mode === 'production' ? undefined : "inline-source-map",
        module: {
            rules: [
                {
                    test: /\.(ts|tsx)$/,
                    enforce: 'pre',
                    exclude: /node_modules/,
                    include: /src/,         
                    use: [
                        {
                            loader: "eslint-loader",
                            options: {
                                fix: true
                            }
                        }
                    ]
                },
                {
                    test: /\.tsx?$/,
                    loader: 'ts-loader',
                    options: {
                        transpileOnly: true // IMPORTANT! use transpileOnly mode to speed-up compilation
                    }
                },
                {
                    test: /\.html$/,
                    use: [
                        {
                            loader: "html-loader",
                            options: {
                                minimize: true
                            }
                        }
                    ]
                }, {
                    test: /\.(css|scss|sass)$/,
                    loader: [
                         'style-loader',
                         'css-loader',
                        'sass-loader'
                    ],
                }, {
                    test: /\.(jpe?g|png|gif|xml|svg)$/i,
                    use: [{
                        //data-url if file size is below 5kb
                        loader: 'url-loader',
                        options: {
                            hash: 'sha512',
                            digest: 'hex',
                            name: '[hash].[ext]',
                            limit: 5000
                        }
                    }]
                }, {
                    test: /\.(woff(2)?|ttf|eot)$/,
                    use: {
                        loader: 'file-loader',
                        options: {
                            outputPath: 'fonts/'
                        }
                    }
                }]
        },
        resolve: {
            extensions: [
                '.ts',
                '.tsx',
                '.js',
                '.jsx',
                '.scss',
                '.css',
                '.svg',
                '.eof',
                '.woff',
                '.woff2',
                '.ttf',
                '.txt',
                '.xml'
            ]
        },
        plugins: [
            ...(mode === "production" ? [new MiniCssExtractPlugin({filename: "index.css"})] : []),
            new ForkTsCheckerWebpackPlugin({
                async: false
            }),
            new HtmlWebPackPlugin({
                template: "./src/index.html",
                filename: "./index.html"
            }),
            new CopyWebpackPlugin({
                patterns: [
                    {
                        from: './static',
                        globOptions: {
                            ignore: ['**/static/readme.txt']
                        }
                    }
                ]
            }),
        ],
        devServer: {
            port: 4000,
            overlay: {
                warnings: false,
                errors: true
            }
        },
        optimization: {
            minimizer: [
                new TerserPlugin({
                    terserOptions: {
                        compress: {
                            unused: false
                        }
                    }
                })
            ]
        }
    };
};

How Do We Reproduce?

Just try to export some variables in a scss files and import in a js file. It all works fine with css-loader 3.x.x or older. With css-loader 4.x.x the imported variables are object is and empty object.

alexander-akait commented 4 years ago

https://github.com/webpack-contrib/css-loader#compiletype, we even have example in docs for this case https://github.com/webpack-contrib/css-loader#separating-interoperable-css-only-and-css-module-features

florianrubel commented 3 years ago

Why is this issue closed? Where is the solution? I have the same problem and I can't find any solution.

gknapp commented 3 years ago

Why is this issue closed? Where is the solution? I have the same problem and I can't find any solution.

See the linked CRA 4 issue. Basically you change your css-loader options in webpack for SCSS files so the compileType is icss. Here's the change in my own config:

     test: /\.scss$/,
     use: [
-      { loader: "style-loader" },
-      { loader: "css-loader" },
+      "style-loader",
+      {
+        loader: "css-loader",
+        options: {
+          importLoaders: 1,
+          modules: {
+            compileType: "icss"
+          }
+        }
+      },
mtolek commented 3 years ago

I had similar issue with packages: "css-loader": "6.2.0", "sass-loader": "12.1.0", "style-loader": "3.2.1", "webpack": "5.21.0"

and configuration: { test: /\.scss$/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, { loader: 'resolve-url-loader', }, { loader: 'sass-loader', options: { sourceMap: true, }, }, ], },

After upgrading style-loader from 2.0.0 to 3.2.1 SCSS variables imported in JS stops working - empty object:

` import variables from 'shared/ReactComponents/scss/variables.scss'

export const REPORT_STATUS = { OK: 'OK', ERROR: 'ERROR', NOT_TESTED: 'NOT TESTED', }

const REPORT_STATUS_TO_COLOR_MAP = {

} ` Finally I solved it using SCSS modules - more here:

https://github.com/webpack-contrib/css-loader#separating-interoperable-css-only-and-css-module-features

BigglesZX commented 2 years ago

I ran into (what I think is) this issue today and this thread seems to rank quite high for it so here's what ended up being my solution: make sure your JS import uses the .default property.

I had to change:

const { tabletPx } = require('../../scss/_variables.scss');

To:

const { tabletPx } = require('../../scss/_variables.scss').default;

The config for recent versions of css-loader is now:

// ...
{
    loader: 'css-loader',
    options: {
        modules: true,
    },
},
// ...

Hope this helps someone else in the same situation.

Atozzio commented 1 year ago

Why is this issue closed? Where is the solution? I have the same problem and I can't find any solution.

See the linked CRA 4 issue. Basically you change your css-loader options in webpack for SCSS files so the compileType is icss. Here's the change in my own config:

     test: /\.scss$/,
     use: [
-      { loader: "style-loader" },
-      { loader: "css-loader" },
+      "style-loader",
+      {
+        loader: "css-loader",
+        options: {
+          importLoaders: 1,
+          modules: {
+            compileType: "icss"
+          }
+        }
+      },

Instead of using "compileType", the options.modules config should be mode: "icss" now according to https://github.com/webpack-contrib/css-loader#separating-interoperable-css-only-and-css-module-features