Search code examples
csswebpackcss-loadersass-loaderextract-text-plugin

Why does ExtractTextPlugin create an empty css file via the plugin? Why is the sass loader providing empty objects when imported with Webpack?


I have configured webpack to use ExtractTextPlugin for generating a single css file from sass as well as allowing it to be available within a reactjs app via import through a webpack loader.

The usage of the sass files are detected through the import statement in the react component and an output file is being generated however the main.css file that is generated is empty and the import is returning an empty style object, does anyone know what I am doing incorrectly?

Structure

client
  - app
    - components
      - navbar
        Navbar.js
        navbar.scss
  - public
    - styles
      main.scss
    bundle.js
    index.html

Webpack Config

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

const BUILD_DIR = path.resolve(__dirname, 'public');
const APP_DIR = path.resolve(__dirname, 'app');

const config = {
   entry: APP_DIR + '/index.js',
   output: {
     path: BUILD_DIR,
     filename: 'bundle.js'
  },
  externals: {
    'cheerio': 'window',
    'react/lib/ExecutionEnvironment': true,
    'react/lib/ReactContext': true,
  },
  module : {
    loaders : [
      {
        test : /\.jsx?/,
        include : APP_DIR,
        loader : 'babel'
      },
      {
        test: /\.scss$/,
        include : APP_DIR,
        loader: ExtractTextPlugin.extract('css!sass')
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'My App'
    }),
    new ExtractTextPlugin('styles/main.css', {
      allChunks: true
    })
  ]
};

module.exports = config;

navbar.scss

.navbar {
  display: flex;

  .navbarBrand {

  }
}

Navbar.js

import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
import Login from '../login/Login'
import Logout from '../logout/Logout'
import styles from './navbar.scss';


export default class Navbar extends Component {

  render() {
    const { isAuthenticated, errors, onLogin, onLogout } = this.props;

    console.log(styles);
    const navBarStyles = classnames(styles.navbar);
    const navBarBrandStyles = classnames(styles.navbarBrand);

    return (
        <nav className={navBarStyles}>
          <div className='container-fluid'>
            <a className={navBarBrandStyles} href="#">User Management App</a>
            <div className='navbar-form'>

              {!isAuthenticated &&
                <Login
                    errors={errors}
                    onLoginClick={ (creds) => onLogin(creds) }
                />
              }

              {isAuthenticated &&
                <Logout onLogoutClick={() => onLogout()} />
              }

            </div>
          </div>
        </nav>
    )
  }
}

Navbar.propTypes = {
  isAuthenticated: PropTypes.bool.isRequired,
  errors: PropTypes.arrayOf(PropTypes.string),
  onLogin: PropTypes.func.isRequired,
  onLogout: PropTypes.func.isRequired
};

Symptoms

Loaders and Imports (NO longer an issue, please refer to answer below)

  1. console.log(styles) = Object {}

  2. styles.navbar = undefined

Plugins

  1. public/styles/main.css is empty (NO longer an issue, please refer to answer below)

Questions

  1. Why is style an empty object when importing it in the Narbar.js? What did I configure wrong? (I have found the solution and provided the answer below in the answers section)
  2. Why is public/style/main.css empty? (I have found the solution and provided the answer below in the answers section)

Solution

  • sass-loader fixed.

    The key is the add modules=true for the css-loader, you can then import you for styles from the scss files.

    {
                test: /\.scss$/,
                loader: extractCSS.extract('style', 'css?modules=true!sass?sourceMap=true')
              }
    

    public/style/main.css fixed.

    To be honest I am not sure what the difference is but the public/styles/main.css file is now populated.

    The only change I made is to the webpack config.

    const webpack = require('webpack');
    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    const BUILD_DIR = path.resolve(__dirname, 'public');
    const APP_DIR = path.resolve(__dirname, 'app');
    
    let generateHtml = new HtmlWebpackPlugin({ title: 'My App' });
    let extractCSS = new ExtractTextPlugin('styles/[name].css', { allChunks: true });
    
    const config = {
      entry: APP_DIR + '/index.js',
      output: {
        path: BUILD_DIR,
        filename: 'bundle.js'
      },
      externals: {
        'cheerio': 'window',
        'react/lib/ExecutionEnvironment': true,
        'react/lib/ReactContext': true,
      },
      module : {
        loaders : [
          {
            test : /\.jsx?/,
            include : APP_DIR,
            loader : 'babel'
          },
          {
            test: /\.scss$/,
            loader: extractCSS.extract('style', 'css?modules=true!sass?sourceMap=true')
          }
        ]
      },
      plugins: [
        generateHtml,
        extractCSS
      ]
    };
    
    module.exports = config;