Search code examples
webpackless-loader

webpack - "Module parse failed: Unexpected token" when using less-loader without chaining css-loader


I need some help understanding the behavior of webpack less-loader.

I'm using webpack 4.5.0.

Here is my webpack config:

const fs = require('fs');
const path = require('path');

const config = {
    entry: './src/index.js',
    mode: 'development',
    output: {
        filename: 'webpack-bundle.js',
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                include: path.resolve(__dirname, 'src'),
                use: {
                    loader: 'babel-loader',
                    options: {
                        cacheDirectory: true,
                        presets: ['react'],
                    }
                }
            },
            {
                test: /\.css$/,
                use: 'css-loader',
            },
            {
                test: /\.less$/,
                use: 'less-loader',
            },
        ],
    },
};

module.exports = config;

Here is my source file structure:

src
 |- index.html
 |- index.js
 |- index.css
 |- index.less
webpack.config.js
package.json

Here is the content of package.json:

{
    "name": "xxx",
    "version": "2.1.0",
    "private": true,
    "scripts": {
        "build": "$(yarn bin)/webpack"
    },
    "devDependencies": {
        "babel-core": "^6.26.0",
        "babel-loader": "^7.1.4",
        "babel-preset-react": "^6.24.1",
        "css-loader": "^0.28.11",
        "less": "^3.0.1",
        "less-loader": "^4.1.0",
        "react": "^16.3.2",
        "react-dom": "^16.3.2",
        "webpack": "^4.5.0",
        "webpack-cli": "^2.0.14"
    }
}

Here is the content of index.less:

body {
    background: #000;
}

When I run yarn build, I get these output:

$ yarn build
yarn run v1.5.1
$ $(yarn bin)/webpack
Hash: deadade0357a0f63a681
Version: webpack 4.5.0
Time: 690ms
Built at: 2018-4-18 17:18:37
            Asset     Size  Chunks             Chunk Names
webpack-bundle.js  695 KiB    main  [emitted]  main
Entrypoint main = webpack-bundle.js
[./src/index.css] 192 bytes {main} [built]
[./src/index.js] 339 bytes {main} [built]
[./src/index.less] 163 bytes {main} [built] [failed] [1 error]
    + 22 hidden modules

ERROR in ./src/index.less
Module parse failed: Unexpected token (1:5)
You may need an appropriate loader to handle this file type.
| body {
|   background: #000;
| }
 @ ./src/index.js 5:0-22

As you can see, js file and css file are compiled, but not less file.

What am I missing here? Why is it telling me I'm missing appropriate loader?


UPDATE:

To verify what @felixmosh said, I update index.less a little bit:

body {
    a {
        text-decoration: none;
    }
}

When I compile again, the error message changes to:

...
[./src/index.less] 170 bytes {main} [built] [failed] [1 error]
    + 22 hidden modules

ERROR in ./src/index.less
Module parse failed: Unexpected token (1:5)
You may need an appropriate loader to handle this file type.
| body a {
|   text-decoration: none;
| }
 @ ./src/index.js 5:0-22

As you can see less code is actually compiled to css (the body a part).

After I chain less-loader with css-loader in webpack.config.js:

const path = require('path');

const config = {
    ...
    module: {
        rules: [
            ...
            {
                test: /\.less$/,
                use: [
                    {
                        loader: 'css-loader',
                    },
                    {
                        loader: 'less-loader',
                    },
                ],
            },
        ],
    },
};

module.exports = config;

The build output changes to:

Built at: 2018-4-19 10:59:58
            Asset     Size  Chunks             Chunk Names
webpack-bundle.js  695 KiB    main  [emitted]  main
Entrypoint main = webpack-bundle.js
[./src/index.css] 192 bytes {main} [built]
[./src/index.js] 339 bytes {main} [built]
[./src/index.less] 198 bytes {main} [built]
    + 22 hidden modules
Done in 1.38s.

All code are compiled and bundled successfully.

In a word, less-loader is only responsible for converting less code to css, but NOT converting css to js which webpack is able to understand and include in the final bundle js.

This is why we need to chain less-loader with css-loader to properly bundle less.


Solution

  • less-loader is a loader that transforms less into css, webpack not understands css without the proper loader.

    You need to concat all of the css related loaders in that case.

    {
      test: /\.less$/,
      use: ['css-loader', 'less-loader'],
    },