Laravel-mix: Can't import SASS when node_modules is included in webpack.config.js

Created on 5 Mar 2017  路  7Comments  路  Source: JeffreyWay/laravel-mix

  • Laravel Mix Version: 0.8.8
  • Node Version (node -v): 6.9.4
  • NPM Version (npm -v): 4.2.0
  • OS: MacOS Sierra 10.12.3

Description:

When trying to import a sass file in JavaScript with style-loader (import "./custom.scss";), I get the following error, as if SASS loaders would not be in place:

 error  in ./src/custom.scss

Module parse failed: /{project_path}/mixbug/src/custom.scss Unexpected token (3:0)
You may need an appropriate loader to handle this file type.
| $bg: red;
|
| .container {
|   background-color: $bg;
| }

 @ ./src/app.js 2:0-23
 @ multi ./src/app.js ./src/app.scss

But I know it is working, because my Sass dependencies (mostly bootstrap and font awesome) are properly compiled. I noticed when I comment out this line from webpack.config.js, it properly compiles (that is, if I also comment out this line from webpack.mix.js). But of course I do want both of them compiled, so when I toggle .sass() call in my webpack.mix.js back, i get the following error:

 error  in ./src/app.scss

Module build failed:
@import "~bootstrap-sass/assets/stylesheets/bootstrap";
^
      Invalid CSS after "...load the styles": expected 1 selector or at-rule, was "var content = requi"
      in /{project_path}/mixbug/src/app.scss (line 1, column 1)

 @ ./src/app.scss 4:14-389
 @ multi ./src/app.js ./src/app.scss

Steps To Reproduce:

  1. Initialize NPM npm init -y
  2. Install Mix, Vue, and Bootstrap-sass npm install --save-dev laravel-mix vue bootstrap-sass
  3. Copy settings from Mix: cp node_modules/laravel-mix/setup/** ./
  4. Setup some files: mkdir src && touch src/app.js && touch src/custom.scss && touch src/app.scss && touch index.html
  5. Fill files with content:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <div id="app">
    <custom></custom>
  </div>
  <script src="/dist/app.js"></script>
</body>
</html>

src/app.js

import Vue from "vue";
import "./custom.scss";

Vue.component("custom", {
  template: `
    <div class="container">
      <strong>{{message}}</strong>
    </div>
  `,
  data() {
    return {
      message: "Hello World!",
    }
  }
});

const app = new Vue({ el: "#app" });

src/app.scss

@import "~bootstrap-sass/assets/stylesheets/bootstrap";

src/custom.scss

$bg: red;

.container {
  background-color: $bg;
}

package.json

{
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack --progress --hide-modules",
    "watch": "cross-env NODE_ENV=development webpack --watch --progress --hide-modules",
    "hot": "cross-env NODE_ENV=development webpack-dev-server --inline --hot",
    "production": "cross-env NODE_ENV=production webpack --progress --hide-modules"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "bootstrap-sass": "^3.3.7",
    "laravel-mix": "^0.8.8",
    "vue": "^2.2.1"
  }
}
  1. Run npm run dev

Should this work at all, or am I trying to do something too extreme?

stale

Most helpful comment

I finally solved it!

The problem is that there are two rules added for scss files.
One for the exact .sass() src in preprocessors to extract the file.
Preprocessor.js

   /**
     * Prepare the Webpack rules for the preprocessor.
     */
    rules() {
        return {
            test: this.test(),
            use: this.getExtractPlugin().extract({
                fallback: 'style-loader',
                use: this.defaultLoaders().concat(this.loaders(this.mixOptions.sourceMaps))
            })
        };
    }


    /**
     * Get the regular expression test for the Extract plugin.
     */
    test() {
        return new RegExp(this.src.path.replace(/\\/g, '\\\\') + '$');
    }

The other one is added in the webpack.config.js

{
        test: /\.s[ac]ss$/,
        include: /node_modules/,
        loaders: ['style-loader', 'css-loader', 'sass-loader']
    },

Webpack tries to compile the .sass() src twice now since its a .scss file.

Anyway if you just exclude the .sass() src files generated by the preprocessor from the webpack.config.js sass rule, it works and you still can import sass files from node_modules.
No need for include: /node_modules/

Here is my final solution.

Webpack.config.js

let preprocessorsExcludes = [];
if (Mix.preprocessors) {
    Mix.preprocessors.forEach(preprocessor => {
        preprocessorsExcludes.push(preprocessor.test());
    });
}

let rules = [
   ...
   {
        test: /\.s[ac]ss$/,
        exclude: preprocessorsExcludes,
        loaders: ['style-loader', 'css-loader', 'sass-loader']
    },
   ...
]

Now, this rule won't touch your .sass() src anymore.

It does the trick for me.
But I'm not sure if something else breaks with this solution but I haven't run into any trouble yet.
Maybe @JeffreyWay can look over it?

Hope that helps.

All 7 comments

@sinchang Hey, thanks for the reply! I find that it actually is in the webpack.config.js file, at line 126, but that rule unfortunately isn't related to this problem, for I was trying to make sass to work (which by the way is also present, at line 131). :/

Rather, my question is: Is there any reason for it to fail when I try to compile both SASS (with .sass() method in webpack.mix.js), and import another sass file in the JavaScript itself (via import)? Shouldn't it work out of the box theoretically? Because if I remove the include property at line 132, it actually imports my sass file and the style-loader works, but then my .sass() method in webpack.mix.js fails.

I have the same problem. I haven't found a solution yet.

At the moment I compile my sass with gulp and just load the css with the sass() task of mix. Which seem to work.

Not the most elegant solution I know.

+1

I finally solved it!

The problem is that there are two rules added for scss files.
One for the exact .sass() src in preprocessors to extract the file.
Preprocessor.js

   /**
     * Prepare the Webpack rules for the preprocessor.
     */
    rules() {
        return {
            test: this.test(),
            use: this.getExtractPlugin().extract({
                fallback: 'style-loader',
                use: this.defaultLoaders().concat(this.loaders(this.mixOptions.sourceMaps))
            })
        };
    }


    /**
     * Get the regular expression test for the Extract plugin.
     */
    test() {
        return new RegExp(this.src.path.replace(/\\/g, '\\\\') + '$');
    }

The other one is added in the webpack.config.js

{
        test: /\.s[ac]ss$/,
        include: /node_modules/,
        loaders: ['style-loader', 'css-loader', 'sass-loader']
    },

Webpack tries to compile the .sass() src twice now since its a .scss file.

Anyway if you just exclude the .sass() src files generated by the preprocessor from the webpack.config.js sass rule, it works and you still can import sass files from node_modules.
No need for include: /node_modules/

Here is my final solution.

Webpack.config.js

let preprocessorsExcludes = [];
if (Mix.preprocessors) {
    Mix.preprocessors.forEach(preprocessor => {
        preprocessorsExcludes.push(preprocessor.test());
    });
}

let rules = [
   ...
   {
        test: /\.s[ac]ss$/,
        exclude: preprocessorsExcludes,
        loaders: ['style-loader', 'css-loader', 'sass-loader']
    },
   ...
]

Now, this rule won't touch your .sass() src anymore.

It does the trick for me.
But I'm not sure if something else breaks with this solution but I haven't run into any trouble yet.
Maybe @JeffreyWay can look over it?

Hope that helps.

i still have this issue, when i log _Mix.preprocessors_ it is empty

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kpilard picture kpilard  路  3Comments

terion-name picture terion-name  路  3Comments

hasnatbabur picture hasnatbabur  路  3Comments

stefensuhat picture stefensuhat  路  3Comments

dtheb picture dtheb  路  3Comments