webpack-contrib / extract-text-webpack-plugin

[DEPRECATED] Please use https://github.com/webpack-contrib/mini-css-extract-plugin Extracts text from a bundle into a separate file
MIT License
4.01k stars 513 forks source link

Webpack 2: How to extract Sass and convert to css file #263

Closed Mandersen21 closed 7 years ago

Mandersen21 commented 8 years ago

Hi

Trying to use your module to extract the text from my sass files and generate a css file with the code. I'm using Webpack version "webpack": "2.1.0-beta.22",

Code

{ test: /\.scss$/, loader: ExtractTextPlugin.extract({ fallbackLoader: "style-loader", loader: "css-loader" }) },

The code I got from your guide in github. I can build and run with "npm start", but getting an Uncaught Error: Expected 'styles' to be an array of strings. error.

MwumLi commented 8 years ago

You can try this below:

  { 
      test: /\.scss$/, 
      loader: ExtractTextPlugin.extract("style", "css!sass") 
  }

transform sass to css firstly

Mandersen21 commented 8 years ago

Still gives me the error: Expected 'styles' to be an array of strings.

MwumLi commented 8 years ago

I think this is not a webpack configuration problem, you should consider the framework you are using Are your using angular 2

trivigy commented 8 years ago

Running into the same problem on angular 2. Seems as though the plugin actually strips the code out. Is there a way to force it to save content into file and return a relative url to the file generated based on the output.publicPath?

p.s. here is a solution that worked. @MwumLi you might use it. Unfortunately this is not enough for me since I am trying to use this plugin together with HtmlWebpackPlugin. Any help is much appreciated.

{
    test: /\.scss$/,
    loader: ['file?name=styles.css', 'css', 'sass']
}
szimek commented 8 years ago

This works for me (using extract-text-webpack-plugin v2.0.0-beta.4):

{ 
  test: /\.sass$/, 
  loader: ExtractTextPlugin.extract({
    fallbackLoader: "style-loader",
    loader: "css-loader!sass-loader",
  }),
}
tleunen commented 8 years ago

I'm having this error window is not defined when using the extract loader. Here's my config:

{
  test: /\.s?css$/,
  loader: ExtractTextPlugin.extract({
      fallbackLoader: 'style',
      loader: [
          {
              loader: 'css',
              options: {
                  sourceMap: true,
                  modules: true,
                  localIdentName: NODE_ENV === 'development'
                      ? '[name]__[local]___[hash:base64:5]'
                      : '[hash:base64:5]'
              }
          },
          {
              loader: 'sass',
              options: {
                  includePaths: customPaths
              }
          },
          {
              loader: 'postcss',
              options: {
                  plugins: () => [
                      autoprefixer
                  ]
              }
          }
      ]
  })
}
dmackerman commented 8 years ago

Same error @tleunen.

@szimek how are you importing your SCSS entry point? Like import 'scss/xx.scss'; ? With your exact config, I still get window is not defined

clayne11 commented 8 years ago

I was getting window is not defined because I had style-loader in my list of loaders. When I moved it to the fallbackLoader I stopped having that issue.

dmackerman commented 8 years ago

@clayne11 Very strange. My config is:

"webpack": "^2.1.0-beta.25",
"extract-text-webpack-plugin": "^2.0.0-beta.4",

const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractCSS = new ExtractTextPlugin('./dist/css/[name].css');

...

      {
        test: /\.scss$/,
        use: extractCSS.extract({
          fallbackLoader: 'style-loader',
          loader: 'css-loader!sass-loader',
        }),
      }

plugins: [ extractCSS, ...other stuff ]

I changed my entry .scss file to just have a body { background: red; } to eliminate any weird issue with imports.

In my index.js entry point, import 'scss/entry.scss';

clayne11 commented 8 years ago

This is my config and I have it working:

import ExtractTextPlugin from 'extract-text-webpack-plugin'

...
  {
    test: /\.css$/,
    loader: ExtractTextPlugin.extract({
      fallbackLoader: 'style',
      loader: [
        {
          loader: 'css',
          query: {
            modules: true,
            sourceMap: false,
            localIdentName: '[hash:base64:5]',
          },
        },
        'postcss',
      ],
    }),
    exclude: cssModuleExcludes,
  },
  {
    test: /\.scss$/,
    loader: ExtractTextPlugin.extract({
      fallbackLoader: 'style',
      loader: [
        {
          loader: 'css',
          query: {
            modules: true,
            sourceMap: false,
            localIdentName: '[hash:base64:5]',
          },
        },
        'postcss',
        {
          loader: 'sass',
          query: {
            sourceMap: false,
          }
        }
      ],
    }),
    exclude: cssModuleExcludes,
  },
  {
    test: /\.scss$/,
    loader: ExtractTextPlugin.extract({
      fallbackLoader: 'style',
      loader: [
        'css',
        'postcss',
        {
          loader: 'sass',
          query: {
            sourceMap: false,
          }
        }
      ],
    }),
    include: cssModuleExcludes,
  },
  {
    test: /\.css$/,
    loader: ExtractTextPlugin.extract({
      fallbackLoader: 'style',
      loader: [
        'css',
      ],
    }),
    include: cssModuleExcludes,
  }

plugins: [new ExtractTextPlugin('[name]-[contenthash].css', ...]
dmackerman commented 8 years ago

What are you excluding in cssModuleExcludes? Wondering if it's trying to do something crazy in the node_modules.

clayne11 commented 8 years ago
// cssModuleExclues

export default [
  /src\/client\/ui\/styles\/normalize\.css/,
  /src\/client\/ui\/styles\/index\.scss/,
  /src\/client\/ui\/styles\/stylesheets\/fontcustom\.scss/,
  /src\/client\/ui\/styles\/stylesheets\/icons-material-design\.scss/,
  /src\/client\/ui\/styles\/stylesheets\/scaffolding\.scss/,
  /src\/client\/ui\/styles\/stylesheets\/tooltips\.scss/,
  /src\/client\/ui\/styles\/stylesheets\/typography\.scss/,
  /node_modules/,
]

It's just a few files I have that I don't want being process with CSS modules as well as node_modules.

dmackerman commented 8 years ago

So it turns out, I had a rule that was using the imports loader, but it was looking in lib, which what should have been a more specific rule. Gah!!!!

xtianus79 commented 8 years ago

this issue needs to be explained better and documentation is at fault.

http://stackoverflow.com/questions/28223040/window-not-defined-error-when-using-extract-text-webpack-plugin-react

this explains that because style-loader is outputting js the plugin will never work. It shouldn't even be a fallback. it wouldn't work -- it is literally not the purpose of what the extract plugin is used for:

Checkout extracts explanation below.

It moves every require("style.css") in entry chunks into a separate css output file. So your styles are no longer inlined into the javascript, but separate in a css bundle file (styles.css). If your total stylesheet volume is big, it will be faster because the stylesheet bundle is loaded in parallel to the javascript bundle.

I hope this helps someone after hours of confusion...

clayne11 commented 8 years ago

You actually typically do want style-loader as your fallback. When you have multiple chunks, this plugin only operates on the main chunk, not the subsequent chunks. The fallback loader will be used on non-entry chunks to process the styles.

The styles from the entry chunks will be pulled into their own CSS files but the subsequent lazy-loaded chunks will use the fallback loader and will still be inlined inside your Javascript.

xtianus79 commented 8 years ago

@clayne11 well I would say perhaps from just css but surely not from sass or less to css. Wouldn't you agree?

Furthermore, if what fails... i.e. since the syntax is "fallback" I'll wait for your replay before I update mine and others documentations. perhaps the documentation from style-loader needs to be updated as well.

clayne11 commented 8 years ago

I use extract-text-webpack-plugin with SCSS. I actually don't use style-loader as my fallback though - I use isomorphic-style-loader.

This is necessary for server-side rendering and code-splitting. I use isomorphic-style-loader to gather the styles present in non-entry chunks and append it to the head of my document a style tag. If you use style-loader you'll get an FOUC.

xtianus79 commented 8 years ago

Thanks @clayne11 ... do you use the isomor as a fallback or directly into the loader?

clayne11 commented 8 years ago

I use it as a fallback. The entry chunk is processed using extract-text-webpack-plugin, so all the SCSS / CSS from the entry chunk is pulled into a single CSS file.

For some of my routes I use code-splitting (System.import('XXX')), and for those routes it falls back to isomorphic-style-loader. For SSR, I collect all the inline CSS from the code-split components and load the string in a style tag. On the client it simply appends the styles for each component to head as an independent style tag, exactly the way style-loader does.

The biggest downside with this approach is that it requires wrapping every single component in an HOC with a hook to load the styles for that component on mount.

This is only necessary if you do SSR and code-splitting. If you don't do both of these then you won't need to follow my approach.

Let me know if you need more info, I can share some of my SSR code and an example of how I wrap my components.

xtianus79 commented 8 years ago

@clayne11 this is super helpful and yes it would definitely help people. I created an issue on stack and someone had a similar issue and this basically answers everything very accurately which is appreciated...

stackoverflow issue webpack window not defined

stackoverflow issue webpack symantics

squadwuschel commented 7 years ago

Hi, I am using webpack 2 with Angular 2 with the following loader for sass, same should also work for less.

{
    test: /\.scss$/,
    loader: [ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: ['css-loader'] }),
        'to-string-loader',
        'css-loader',
        'sass-loader'
    ],
    exclude: [helpers.root('Content')]
},

and in my component I am using

styleUrls: [ './logmonitor.component.scss' ],

my current devdependencies are:

 "devDependencies": {
   "@types/node": "6.0.51",
   "angular2-template-loader": "0.6.0",
   "awesome-typescript-loader": "2.2.4",
   "css-loader": "0.26.1",
   "css-to-string-loader": "0.1.2",
   "file-loader": "0.9.0",
   "url-loader": "0.5.7",
   "html-loader": "0.4.4",
   "svg-url-loader": "1.1.0",
   "less": "2.7.1",
   "less-loader": "2.2.3",
   "node-sass": "3.13.0",
   "sass-loader": "4.0.2",
   "style-loader": "0.13.1",
   "raw-loader": "0.5.1",
   "to-string-loader": "1.1.5",
   "clean-webpack-plugin": "0.1.4",
   "extract-text-webpack-plugin": "2.0.0-beta.4",
   "html-webpack-plugin": "2.21.0",
   "webpack-notifier": "1.4.1",
   "webpack": "2.1.0-beta.27",
   "webpack-dev-middleware": "1.8.4",
   "webpack-dev-server": "2.1.0-beta.12",
   "webpack-md5-hash": "0.0.5",
   "webpack-merge": "0.17.0",
   "typescript": "2.0.10",
   "typings": "2.0.0"
 }
cascornelissen commented 7 years ago

@sokra, any estimation on how long it will take to fully support Webpack 2 now that the first stable release (2.2.0) is out?

xtianus79 commented 7 years ago

@cascornelissen i am using webpack 2+ and everything works the same.

cascornelissen commented 7 years ago

@xtianus79, I'm migrating to 2.2.0 at the moment and running into the issue that the names of keys in the rules object doesn't match the new setup.

As stated in the migration documentation, loaders.loader should now be rules.use but extract-text-webpack-plugin still requires you to use the loader key. It's not working with use for me, at least.

Can't really remember why I added the comment to this issue though, might've been a mistake.

xtianus79 commented 7 years ago

@cascornelissen Why have you not upgraded to 2.4.1? This is the version I am running currently

cascornelissen commented 7 years ago

@xtianus79, 2.4.1 of what? 2.2.0 was released today.

EDIT: Ah, you were talking about Angular? I was referring the the start (and essential part) of this issue. I'm not working on anything Angular 2-specific.

I'm just interested in when the first stable 2.x release of this plugin will land as it currently doesn't support the correct syntax AFAIK.

And for anyone running into the same issues/questions, there appears to be a milestone for Wepback 2 and this is the exact problem I'm running into.

rohmanhm commented 7 years ago

Tried all config I've found and still no one be work. This is really disturbing bugs.

jebuenga commented 7 years ago

I'm using "extract-text-webpack-plugin": "2.0.0-beta.5" which seems to work with Webpack 2.

scott-thrillist commented 7 years ago

Since Google brought me here and @rohmanhm found the issue in this thread, I'll post it here in hopes of helping save someone from the hours of work of missing a single line :-p

In your app's entry JS file you must require the SASS entry point. So in my app.js I have this near the top:

require("mainCss");

then in my webpack.config I alias the path:

resolve: { alias: { 'mainCss': path.resolve( __dirname, 'sass/mainFileName.scss') } }

Still trying to work out other issues such as retaining the name of the original [filename].scss in the output file.

ankitc1010 commented 7 years ago

The loader config. {loader: ExtractTextPlugin.extract({loader: ['css-loader', 'sass-loader']}), test:/.s?css$/}

I am using Webpack 2 and extract-text-webpack-plugin@2.0.0-rc.1

kazagkazag commented 7 years ago

My package.json:

"webpack": "2.2.1", "extract-text-webpack-plugin": "2.0.0-rc.3",

Config:

export default {
    //some rules here...
    modules: {
        rules: [
            ...baseRules,
    {
        test: /(\.css|\.scss)$/,
        use: ExtractTextPlugin.extract({
            fallback: "style-loader",
            use: [
                {
                    loader: "css-loader",
                    options: {
                        sourceMap: true,
                        modules: true,
                        importLoaders: true,
                        localIdentName: "[name]__[local]___[hash:base64:5]"
                    }
                },
                {
                    loader: "postcss-loader",
                    options: {
                        plugins: function () {
                            return [
                                require("autoprefixer")
                            ];
                        }
                    }
                },
                {
                    loader: "sass-loader",
                    options: {
                        sourceMap: true
                    }
                }
            ]
        })
    }
]
    }
}

And it works finally...

whisher commented 7 years ago

Hi there, I'm using angular2 this work for me:

{
        test: /\.scss$/,
        exclude: [helpers.root('src', 'app')],
        use: ExtractTextPlugin
          .extract({
            fallbackLoader: 'style-loader',
            loader: [
              { loader: 'css-loader', query: { modules: true, sourceMaps: true } },
              { loader: 'sass-loader'},
              { loader: 'postcss-loader'},
              ]
          })
      },
      {
        test: /\.scss$/,
        include: [helpers.root('src', 'app')],
        use: [
          { loader: 'raw-loader'},
          { loader: 'sass-loader'},
          { loader: 'postcss-loader'}
        ]
      }
dawiwt commented 7 years ago

update extract-text-webpack-plugin to v2.0.0-beta.4, it's work.

rohmanhm commented 7 years ago

@DaWiii the new version is v2.0.0-rc.3 dude, https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/e831507573feff824ef2c3c3c4a8fc971f278bc7

leongaban commented 7 years ago

@whisher Hi! Curious how you got sass-loader working with webpack2/angular 2. I've been trying for a couple days now :(

http://stackoverflow.com/questions/42058357/webpack-not-understanding-import-statement-in-my-sass-files

Seems like it shouldn't be this hard? Just attached sass-loader and point to a directory...

whisher commented 7 years ago

@Leon Hi, you've got just the accepted answer :)

On 7 February 2017 at 22:19, Leon Gaban notifications@github.com wrote:

@whisher https://github.com/whisher Hi! Curious how you got sass-loader working with webpack2/angular 2. I've been trying for a couple days now :(

http://stackoverflow.com/questions/42058357/webpack- not-understanding-import-statement-in-my-sass-files

Seems like it shouldn't be this hard? Just attached sass-loader and point to a directory...

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/263#issuecomment-278143517, or mute the thread https://github.com/notifications/unsubscribe-auth/AAn0015GvIya2NLO2N6lfZ1mS7hPlNsNks5raN_-gaJpZM4KOx23 .

cjke commented 7 years ago

Note this variation also works (if you needed to include any options to style-loader), it's following the newer pattern:

    rules: [{
        test: /\.css$/,
        exclude: '/node_modules/',
        use: ExtractTextPlugin.extract({
            fallbackLoader: [{
                loader: 'style-loader',
            }],
            use: [{
                loader: 'css-loader',
                options: {
                    modules: true,
                    localIdentName: '[name]__[local]--[hash:base64:5]',
                },
            }, {
                loader: 'postcss-loader',
            }],
        }),
    }]

Be nice if this was documented though..

webjay commented 7 years ago

Here's my way which works with 2.0.0-rc.3:

  {
    test: /\.scss$/,
    loader: ExtractTextPlugin.extract({
      fallback: 'style-loader',
      loader: [
        {
          loader: 'css-loader',
          options: {
            sourceMap: true,
            minimize: true,
            discardComments: {
              removeAll: true
            }
          }
        },
        {
          loader: 'postcss-loader',
          options: {
            plugins: () => [autoprefixer]
          }
        },
        {
          loader: 'sass-loader',
          options: {
            sourceMap: true
          }
        }
      ]
    })
  }
squadwuschel commented 7 years ago

@webjay where do you have the autoprefixer defined

whisher commented 7 years ago

@webjay good point to have sass-loader before postcss-loader. I found clearer to use a postcss.config.js like

module.exports = {
  plugins: [
    require('postcss-import')(),
    require('autoprefixer')()
  ]
}

This works for me (Angular2)

{
        test: /\.css$/,
        exclude: [helpers.root('src', 'app')],
        use: ExtractTextPlugin
          .extract({
            fallback: 'style-loader',
            use: [
              { loader: 'css-loader', query: { modules: false, sourceMaps: true } },
              { loader: 'postcss-loader'  }
            ]
          })
      },
      {
        test: /\.css$/,
        include: [helpers.root('src', 'app')],
        use: [
          { loader: 'raw-loader' },
          { loader: 'postcss-loader' }
        ]
      },
      {
        test: /\.scss$/,
        exclude: [helpers.root('src', 'app')],
        use: ExtractTextPlugin
          .extract({
            fallback: 'style-loader',
            use: [
              { loader: 'css-loader', query: { modules: false, sourceMaps: true } },
              { loader: 'postcss-loader' },
              { loader: 'sass-loader', query: { sourceMaps: true } }
            ]
          })
      },
      {
        test: /\.scss$/,
        include: [helpers.root('src', 'app')],
        use: [
          { loader: 'raw-loader' },
          { loader: 'sass-loader', query: { sourceMaps: true } },
          { loader: 'postcss-loader' }
        ]
      }
webjay commented 7 years ago

@squadwuschel Like this: import autoprefixer from 'autoprefixer';

squadwuschel commented 7 years ago

@whisher you are not using extractText plugin in your component styles only for custom styles outside your app and could you post your package.json for devdependencies?

your solution is working for me thanks.

whisher commented 7 years ago

@squadwuschel
I use extractText plugin in my component styles only for custom styles because I'm using Angular2 fw

Here it is

"devDependencies": {
    "@angular/compiler-cli": "2.4.3",
    "@types/jasmine": "^2.5.42",
    "@types/node": "^7.0.0",
    "@types/webpack": "^2.0.0",
    "angular2-template-loader": "0.6.0",
    "assets-webpack-plugin": "^3.5.1",
    "autoprefixer": "^6.7.2",
    "awesome-typescript-loader": "3.0.0-beta.18",
    "codelyzer": "~2.0.0-beta.4",
    "copy-webpack-plugin": "^4.0.1",
    "css-loader": "0.26.1",
    "extract-loader": "^0.1.0",
    "extract-text-webpack-plugin": "2.0.0-rc.3",
    "favicons-webpack-plugin": "^0.0.7",
    "file-loader": "0.9.0",
    "html-webpack-plugin": "2.26.0",
    "jasmine-core": "^2.5.2",
    "json-loader": "^0.5.4",
    "karma": "^1.4.1",
    "karma-jasmine": "^1.1.0",
    "karma-phantomjs-launcher": "^1.0.2",
    "karma-sourcemap-loader": "^0.3.7",
    "karma-webpack": "^2.0.2",
    "loader-utils": "^0.2.16",
    "ng-router-loader": "^1.0.2",
    "node-sass": "^4.5.0",
    "null-loader": "^0.1.1",
    "phantomjs-prebuilt": "^2.1.14",
    "postcss-import": "^9.1.0",
    "postcss-loader": "^1.2.2",
    "raw-loader": "^0.5.1",
    "rimraf": "2.5.4",
    "sass-loader": "^4.1.1",
    "script-ext-html-webpack-plugin": "^1.5.0",
    "style-loader": "0.13.1",
    "ts-helpers": "1.1.2",
    "ts-node": "^2.0.0",
    "tslint": "~4.3.1",
    "tslint-loader": "^3.3.0",
    "typescript": "2.1.5",
    "url-loader": "^0.5.7",
    "webpack": "2.2.0",
    "webpack-dev-middleware": "^1.9.0",
    "webpack-dev-server": "2.2.0",
    "webpack-dll-bundles-plugin": "^1.0.0-beta.5",
    "webpack-md5-hash": "^0.0.5",
    "webpack-merge": "~2.4.0",
    "webpack-notifier": "^1.5.0"
  }
squadwuschel commented 7 years ago

@whisher I am also trying to get it working for my angular 2 app. And when I try to use your ExtractTextPlugin for my "App" folder where I have all my directives and compontens for my app I got the error

 Expected 'styles' to be an array of strings.  ....

Its only working when I use pure loaders like in your example on my "App" directory and the extractTextPlugin is working on my custom styles outside my "App" directory.

here so I try to load my styles all files are in the same directory where the "sxpNotifications.component.ts" is located.

@Component({
    selector: '[sxp-notifications]',
    templateUrl: './sxpNotifications.component.html',
    styleUrls: ['./sxpNotifications.component.scss']
})
scottdj92 commented 7 years ago

Documentation has been updated #413

scottdj92 commented 7 years ago

Is this still an issue? Please make sure that you are running the latest version of this plugin.

michael-ciniawsky commented 7 years ago

ETWP has some regressions when used with Angular 2 Components, during the fact that Angular 2 Components need the CSS at runtime => Uncaught Error: Expected 'styles' to be an array of strings.(Angular Compiler Error), ETWP on the other hand removes the CSS Chunks from the bundle and emits a separate CSS file, which is the correct behaviour. Use ETWP only for CSS not required directly by Angular Components { styles: require('./index.css') } and address this issue @angular/angular to allow an empty {String} || {Array} in styles

jsdevtom commented 7 years ago

This worked for me with: "webpack": "^2.6.1" and "@angular/...": "^4.1.3":

module: {
    rules: [
      {
        test: /\.scss$/,
        loaders: ['raw-loader', 'sass-loader']
      },
      {
        test: /\.css$/,
        loader: 'raw-loader'
      }
    ]
  }
denu5 commented 7 years ago

@jsdevtom could you please post your complete webpack config? i'm having having problems with this too

jsdevtom commented 7 years ago

@denu5 Sure. The whole project can be found here

webpack.common.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/app/main.ts',
  resolve: {
    extensions: [ '.js', '.ts' ]
  },
  module: {
    rules: [
      {
        test: /\.html$/,
        loaders: ['html-loader']
      },
      {
        test: /\.scss$/,
        loaders: ['raw-loader', 'sass-loader']
      },
      {
        test: /\.css$/,
        loader: 'raw-loader'
      }
    ],
    exprContextCritical: false
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    })
  ]
}