1

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)
Robert Leggett
  • 972
  • 1
  • 8
  • 23

2 Answers2

1

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;
Robert Leggett
  • 972
  • 1
  • 8
  • 23
0
  1. As for the empty file, here is how I use this plugin in one of my projects: ( Webpack@2.2, ExtractTextPlugin v2 )

plugin instantiation

const extractSCSS = new ExtractTextPlugin({
    filename: "[name].scss",
    allChunks: true
});

loader

{
    test: /\.(css|scss)$/,
    loader: extractSCSS.extract({
        loader: [
            {
                loader: "css-loader",
                options: { modules: true }
            },
            {
                loader: "sass"
            }
        ],
        defaultLoader: "style-loader"
    })
}

plugins

plugins: [extractSCSS],

you can checkout the full Webpack configuration I sampled this from in this project

kfitzi
  • 46
  • 3
  • Thanks I will give that configuration a go and see if that makes a difference. – Robert Leggett Jan 24 '17 at 02:03
  • I hope it works. It looks like you were using Webpack v1 and the extract-text-webpack-plugin v1, this config is for v2 so make sure you update your project dependencies if you try it out. – kfitzi Jan 24 '17 at 02:21