Extract Text Webpack Plugin Does Not Generate Artifacts

664 views Asked by At

I have a very simple set up that uses Webpack (1.14.0) with Extract Text Webpack Plugin (1.0.1) to generate a CSS file. The problem is that upon running webpack, no CSS artifact is produced.

Here is my webpack.config.js:

var ExtractTextPlugin = require('extract-text-webpack-plugin')
var path = require('path')

module.exports = {
  entry: [
    path.resolve(__dirname, 'src') + '/index.js'
  ],
  module: {
    loaders: [
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
      }
    ]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  },
  plugins: [
    new ExtractTextPlugin(path.resolve(__dirname, 'dist') + '/style.css')
  ]
}

As you can see this is a very simple setup. I have a folder called src which contains a file called index.js (that is currently blank) and style.css (which only contains a single body style). The expectation is that the relative dist folder contains an artifact called style.css (which should basically be just a carbon copy of the original). The actual result is that only dist/bundle.js is ever produced. As far as I can tell the version of Webpack and Extract Text Webpack Plugin should be compatible (the peerDependency of Extract Text Webpack Plugin is ^1.9.11).

What am I missing here?

2

There are 2 answers

0
Brandon Tom On

I was able to get it to generate an artifact, but I'm not really comfortable with this solutions.

Here's my updated webpack.config.js:

var ExtractTextPlugin = require('extract-text-webpack-plugin')
var path = require('path')

module.exports = {
  entry: [
    path.resolve(__dirname, 'src') + '/index.js',
    path.resolve(__dirname, 'src') + '/style.scss'
  ],
  module: {
    loaders: [
      {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract('style-loader', 'css-loader!sass-loader')
      }
    ]
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  },
  plugins: [
    new ExtractTextPlugin('[name].css')
  ]
}

The solution was to add the stylesheet (in this case path.resolve(__dirname, 'src') + '/style.scss') as an entry point. You have to do this if your JavaScript files do not reference the stylesheet in anyway. I changed the stylesheet to Sass in order to prove that the loader was handling the file and not simply copying an unprocessed file.

This solution doesn't seem to jive with existing documentation on the topic, but I think the entry point property is implied with most tutorials and examples. Therefore, I probably just didn't realize the way this had to be specified.

Adding to the problem was that I was passing in the full path to the new ExtractTextPlugin() constructor. That wasn't the reason a file wasn't being generated, but I was doing that wrong as well. It only needs to be the file name. The output property path is referenced automatically.

0
Brandon Tom On

So after a lot of reworking, I think I finally came up with a solution that is much better than the one I originally used. To clarify what the goal was, I wanted to use Webpack to generate a separate stylesheet file without having the reference the CSS file within the JavaScript entry points.

As mentioned previously, you can accomplish this by adding the stylesheet as one of the entry points. The problem with this method is that Webpack generates an additional JavaScript file that would otherwise store the CSS had Extract Text Webpack Plugin not removed it (leaving you with a mostly empty JavaScript). To get around this, you can call the loaders within the entry point definitions themselves. Subsequently, I don't use Extract Text Webpack Plugin, instead I'm just using file-loader.

Here's a pretty elaborate example of how to accomplish this. Keep in mind, this example is in ES2015.

Here's my webpack.config.babel.js:

import HtmlWebpackPlugin from 'html-webpack-plugin'
import neat from 'node-neat'
import path from 'path'
import webpack from 'webpack'

const sourcePath = path.resolve(__dirname, 'src')
const jsPath = `${sourcePath}/js`
const pugPath = `${sourcePath}/pug`
const sassPath = `${sourcePath}/scss`
const outputPath = path.resolve(__dirname, 'dist')
const neatPaths = neat.includePaths.map((path) => {
  return `includePaths[]=${path}`
}).join('&')

export default {
  devServer: {
    inline: true
  },
  entry: [
    `${jsPath}/index.js`,
    `file-loader?name=styles.css!sass-loader?${neatPaths}!${sassPath}/styles.scss`
  ],
  module: {
    loaders: [
      {
        exclude: /node_modules/,
        loader: 'babel-loader',
        test: /\.js$/
      },
      {
        loader: 'pug-html-loader',
        test: /\.pug$/
      }
    ]
  },
  output: {
    filename: '[name].js',
    path: outputPath
  },

  plugins: [
    // Webpack Plugins
    new webpack.optimize.UglifyJsPlugin({
      mangle: false
    }),

    // Main Template
    new HtmlWebpackPlugin({inject: 'body', template: `${pugPath}/index.pug`})
  ],
  resolve: {
    extensions: ['.css', '.js', '.pug', '.scss']
  }
}

As you can see in the entry point definitions, I'm calling the loaders directly from there. This generates a new .css file without having to reference it in code. Also I ditched all the uses of Extract Text Webpack Plugin and used file-loader to generate the new file.