How to properly use multiple loaders with this plugin? #330

Closed
xeho91 opened this Issue Jan 10, 2017 · 30 comments

Projects

None yet
@xeho91
xeho91 commented Jan 10, 2017

Hello!

I am using webpack@2.2.0-rc3
and extract-text-webpack-plugin@2.0.0-beta.4

I am trying to configure this plugin to extract multiple loaders (start with sass, end with css).
The piece of configuration code in my webpack.config.js looks like this:

...
    module: {
        rules: [
            ...
            // Stylesheets
            {
                test: /\.scss$/,
                include: path.resolve(__dirname, 'src'),
                use: ExtractTextPlugin.extract([{
                    fallbackLoader: 'style-loader',
                    loader: 'css-loader!postcss-loader!sass-loader',
                }]),
            },
            ...
        ],
    },
    ...
    plugins: [
        ...
        new ExtractTextPlugin('./stylesheets/[name].css'),
        ...
    ],
...

I am getting errors output in terminal. The first one looks like this and the rest are similar to this one:

ERROR in ./styles.scss
Module parse failed: C:\Users...\node_modules\extract-text-webpack-plugin\loader.js?{"omit":0,"remove":true}!css-loader!postcss-loader!sass-loader!C:\Users...\src\styles.scss Unexcepted character @ (1:0)
You may need an appropriate loader to handle this file type.
| @import './fonts/fonts';
|
| body,
@ ./index.js 7:0-24

I tried several possible ways to make it work. Everytime the same error output.

After some hours of struggling with it I found the only setup which worked so far:

...
            {
                test: /\.scss$/,
                include: path.resolve(__dirname, 'src'),
                use: [
                    ExtractTextPlugin.extract([{ fallbackLoader: 'style-loader', loader: 'css-loader' }]),
                    'css-loader',
                    'postcss-loader',
                    'sass-loader',
                ],
            },
...

However as you could probably have guessed, the result isn't what I excepted to have.
It bundles every stylesheets in folder to one file, which makes a heavy css file from it.

I've followed documentation, tried setup from webpack version 1 and nothing worked so far.
I believe I could have missed something, since I am still a beginner that's why I seek for a help here and I hope to get an advise in case I missed something important.
Also I found out that on gitter webpack chat someone was struggling with same problem as me and got same error output, so naturally I started to think that it could be a bug and I decided to report it.

@frederikprijck
Contributor
frederikprijck commented Jan 10, 2017 edited

Linking my related SO (it's using LESS tho): http://stackoverflow.com/questions/41554983/using-extracttextplugin-with-webpack-2-2-0-rc3-throws-errors

My SO question includes a repro sample to be able to investigate this:

https://dl.dropboxusercontent.com/u/87239305/webpack-less.zip

Steps to reproduce:

npm install
node_modules\ .bin\webpack.cmd --config conf\webpack.conf.js

I've also added a second config file which is working, without ExtractTextPlugin:

node_modules\ .bin\webpack.cmd --config conf\webpack.conf2.js

@toddbluhm

Running into this same issue as well. Same setup as @frederikprijck just without the post-css loader.

@miljan-aleksic

I also am having those issues trying to load less files.

@415DomSmith

I was running in to a similar issue when moving to webpack 2 from 1, and I found:

http://stackoverflow.com/questions/41021614/webpack-2-css-modules-support

Keeping my css-loader line the same as what I had in webpack 1 worked. My rule now looks like:

...
    {
        test: /\.(scss)$/,
        include: [path.join(__dirname, 'src')],
        loader: ExtractTextPlugin.extract({
          fallbackLoader: 'style-loader',
          loader: [
            'css-loader?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
            'sass-loader'
          ]
        })
     }
...
@AlexanderTserkovniy

@415DomSmith Thanks a lot! You saved me!

@tlrobinson

@415DomSmith's solution worked for me. It appears ExtractTextPlugin doesn't yet support the Webpack 2 { loader: "loader-name", options: { ... } } syntax like:

    {
        test: /\.(scss)$/,
        include: [path.join(__dirname, 'src')],
        loader: ExtractTextPlugin.extract({
          fallbackLoader: 'style-loader',
          loader: [
            { loader: 'css-loader', options: { modules: true, importLoaders: 1, localIdentName: "[path]___[name]__[local]___[hash:base64:5]" },
            { loader: 'sass-loader' }
          ]
        })
     }

It might also make sense to change/alias the loader option to use like in Webpack 2.

@dylanjha
dylanjha commented Jan 17, 2017 edited

passing in options into the sass-loader through query params solved this for me:

modules: {
  rules: [
     {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract([
          'css-loader', 'sass-loader?includePaths=./node_modules/compass-mixins/lib'
        ])
      }
  ]
}

webpack 2.2.0-rc.6
extract-text-webpack-plugin 2.1.0-beta.25

@abierbaum

I have no idea why, but @dylanjha 's format above with no fallback loader was the only way I could get it to work. Definitely some rough edges here that need fixed up. Spent half a day of trial and error on this one. :(

@jaydenseric

This plugin doesn't support Webpack v2 yet, here is the milestone.

@frederikprijck
Contributor
frederikprijck commented Jan 19, 2017 edited

@jaydenseric That's not correct. The readme clearly says:

extract text plugin for webpack 2

The API has changed since version 1. For the webpack 1 version, see the README in the webpack-1 branch.

@TheLarkInn confirmed it was a problem with ExtractTextPlugin and should have been fixed with the latest release: https://github.com/webpack/extract-text-webpack-plugin/releases/tag/v2.0.0-beta.5 (I still have to verify this myself tho.)

Link to the twitter conversation with him regarding this issue: https://twitter.com/TheLarkInn/status/818868861000962048

@frederikprijck
Contributor
frederikprijck commented Jan 19, 2017 edited

I verified my reproduce app mentioned above (#330 (comment)) is working when upgrading ETP to beta5 using the following syntax:

{ test: /\.(css|less)$/, loader: ExtractTextPlugin.extract(["css-loader", "postcss-loader", "less-loader"]) }

Aswell as using the following syntax

{ 
    test: /\.(css|less)$/, 
    loader: ExtractTextPlugin.extract({
        fallbackLoader: "style-loader",
        loader: ["css-loader", "postcss-loader", "less-loader"]
    }) 
},

This is not 100% according to the readme, but that should be fixed once #341 get's merged.

@xeho91 can you verify this works for u ?

@cascornelissen

@frederikprijck, do you have an example for when we want to use plugins as well? Say I want to use autoprefixer with postcss-loader, any idea on how to set that up?

@frederikprijck
Contributor
frederikprijck commented Jan 19, 2017 edited

@cascornelissen the postcss-loader is used in my syntax above.

you also have to add this to the plugins section IIRC:

new LoaderOptionsPlugin({
    debug: true,
    options: {
        postcss: {
            plugins: () =>[autoprefixer]
        }
    }
})

It's also in my reproduction example #330 (comment)

@cascornelissen
cascornelissen commented Jan 19, 2017 edited

Ah, didn't know about the LoaderOptionsPlugin. I'll see if that works. Is passing a configuration object to autoprefixer also supported this way?

Edit: seems like you can just

postcss: {
    plugins: function() {
        return [
            autoprefixer({
                browsers: [
                    'last 2 version',
                    'ie >= 9',
                    'ios 8'
                ]
            })
        ];
    }
}
@trainiac

With regards to postcss plugins specifically. You can also provide a postcss.config.js file at your project root that get's picked up by postcss-loader.

// postcss.config.js
module.exports = () => {
  const pluginConfigs = {
    'import': {
      path: ['somepath']
    }
  }

  const plugins = [{
    name: 'import',
    mod: require('postcss-import')
  }, {
    name: 'mixins',
    mod: require('postcss-mixins')

  }, {
    name: 'simple-vars',
    mod: require('postcss-simple-vars')

  }, {
    name: 'color-function',
    mod: require('postcss-color-function')

  }, {
    name: 'autoprefixer',
    mod: require('autoprefixer')
  }]

  return {
    syntax: 'postcss-scss',
    plugins: plugins.map(plugin => {
      if (plugin.name in pluginConfigs) {
        return plugin.mod(pluginConfigs[plugin.name])
      }

      return plugin.mod()
    })
  }
}

https://github.com/michael-ciniawsky/postcss-load-config

@kb3eua
kb3eua commented Jan 20, 2017

I got it to work in webpack 2.0 with the following syntax:

{
  test: /\.s[ac]ss$/,
  use: [
    'to-string-loader',
    ExtractTextPlugin.extract({
      fallbackLoader: 'style-loader',
      loader: [
          'css-loader?-url&sourceMap',
          'postcss-loader',
          'sass-loader?sourceMap'
      ]
    })
  ],
  exclude: [
    root('node_modules')
  ]
}

notice I had to add the "to-string-loader" to get it to work.

@jaydenseric

I got it to work with Webpack 2, PostCSS (with postcss-import, postcss-cssnext and postcss-reporter plugins) and sourcemaps:

@Ephem
Ephem commented Jan 24, 2017 edited

I wish I would have read @415DomSmith comment more carefully earlier on, would have saved me a few hours. I had the syntax inside of the ExtractTextPlugin.extract({ ... }) correct, but do note that you have to use loader: and not use: in the outer webpack-config as well.

Just to clarify, this does NOT work:

{
  test: /\.s[ac]ss$/,
  use: ExtractTextPlugin.extract({
    fallbackLoader: 'style-loader',
    loader: ['css-loader', 'sass-loader']
  })
}

This DOES work:

{
  test: /\.s[ac]ss$/,
  loader: ExtractTextPlugin.extract({
    fallbackLoader: 'style-loader',
    loader: ['css-loader', 'sass-loader']
  })
}

@kb3eua If you change use to loader in your example you should be able to remove the to-string-loader.

As I understand it ExtractTextPlugin.extract takes the options and returns a string based on the old loader!loader-2-syntax. I guess this needs to return an array with the loaders instead to work with use:? This however would break the current way of using it with loader:. Not sure what the best way forward would be with this, but the readme could probably be clarified in the meantime. :)

@frederikprijck
Contributor
frederikprijck commented Jan 24, 2017 edited

Nice catch @Ephem .

I've modified my reproduction sample (webpack-less.zip) and It idd fails when using use. Replacing use with loaders works fine.

Monkeypatching PR #343 (as mentioned by @Ephem below) solved the problem. So I'm excited to see #343 getting merged in!

@Ephem
Ephem commented Jan 24, 2017

Returning an array instead of a string so that use: works is actually what PR #343 does and luckily this does NOT break loader: in Webpack 2. In Webpack 1 loader: only works with a string, but I tried it out in Webpack 2 and loader: works fine with arrays there.

@bebraw bebraw added this to the Webpack 2 release milestone Jan 28, 2017
@bebraw
Contributor
bebraw commented Jan 28, 2017

@xeho91 Can you update to rc0? Please note that you have to extract with use, not loader. Let me know how it goes.

@bebraw
Contributor
bebraw commented Jan 31, 2017

@xeho91 Any news on this? This is a blocking final release. A standalone project to study would be ideal. Note that loader: format should work too. There's rc2 available now.

@frederikprijck
Contributor

@bebraw To me it's working. But well, I didn't file the issue so I guess that doesn't matter. But I did solve my reproduction sample mentioned previously (#330 (comment)).

To summarize, I'm succesfully using the following versions:

  • webpack 2.2.0 (final)
  • extract-text-webpack-plugin: 2.0.0-rc.1

Together with the following configuration:

{
    test: /\.(css|less)$/,
    use: ExtractTextPlugin.extract({
        fallbackLoader: 'style-loader',
        loader: ['css-loader', 'postcss-loader', 'less-loader']
    })
}

and

plugins: [new ExtractTextPlugin('index-[contenthash].css')]
@bebraw
Contributor
bebraw commented Jan 31, 2017

@frederikprijck Ok, excellent to hear. Often it's hard to work with issues like this as the information tends to be partial (not much to work with), but still cool. ๐Ÿ‘

@nryoung
nryoung commented Feb 10, 2017

I have tried multiple variations to get chained loaders to get to work with this plugin. I have tried all the examples in this thread and nothing seems to work.

  • webpack 2.2.0
  • extract-text-webpack-plugin: 2.0.0-rc.3

here is my simple configuration:

               test: /\.scss$/,
                use: ExtractTextPlugin.extract({
                    loader: [
                        'css-loader',
                        'resolve-url-loader',
                        'sass-loader?sourceMap=true'
                    ]
                })
            },

with plugins looking like:

 new ExtractTextPlugin('[name].[contenthash].css')

I execute webpack and it seems to compile successfully, but the css file is never emitted. There is no error message or anything that points me in the right direction.

@bebraw
Contributor
bebraw commented Feb 10, 2017

@nryoung I would need a project to study to say anything concrete. Based on your description alone I can't see how you refer to CSS in your code.

@dylanjha

@nryoung with this bit of code it's difficult to tell what the issue is. Are you using import in your entry file or any of the files that get loaded? It would probably be most beneficial to you to post a question on stack overflow with a complete example.

@nryoung
nryoung commented Feb 10, 2017

@bebraw @dylanjha Thanks for the replies. I went back and looked at my entry point file and realized it was basically a global include for all of my other sass modules. I added this to plugins and it worked:

new ExtractTextPlugin({
  filename: '[name].[contenthash].css',
  allChunks: true
)

Notice the allChunks: true, this allowed ExtractTextPlugin to bundle all of the includes in the entry point .scss file.

@xeho91
xeho91 commented Feb 12, 2017 edited

@bebraw

Sorry, I was away for a few weeks.

I'm currently using following versions:
"extract-text-webpack-plugin": "^2.0.0-beta.5",
"webpack": "^2.2.0-rc.4","webpack": "^2.2.0-rc.4".
(I know I need to update, but I'm checking the older versions just in case).

and the code for this plugin I've got right now (sorry, I don't have the older - the one which was used to report this issue):

const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = function extractCSS(paths, env) {
    return {
        module: {
            rules: [
                // Extract CSS during build
                {
                    test: /\.scss$/,
                    include: paths,
                    exclude: /node_modules/,
                    loader: ExtractTextPlugin.extract({
                        fallbackLoader: 'style-loader',
                        loader: [
                            {
                                loader: 'css-loader',
                                query: {
                                    sourceMap: (env === 'development'),
                                },
                            },
                            'postcss-loader',
                            {
                                loader: 'sass-loader',
                                query: {
                                    sourceMap: (env === 'development'),
                                    sourceMapContents: (env === 'development'),
                                },
                            },
                        ],
                    }),
                },
            ],
        },

        plugins: [
            new ExtractTextPlugin('stylesheets/[name].css'),
        ],
    };
};

And it works perfectly.

However using 'use' as mentioned before like this:

...
use: ExtractTextPlugin.extract({
...

didn't work.

@bebraw
Contributor
bebraw commented Feb 12, 2017

loader and use should work the same. Thanks for confirming it works now. Time to close.

@bebraw bebraw closed this Feb 12, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment