0

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?

Brandon Tom
  • 889
  • 7
  • 16
  • webpack 1 or 2? – Andy Ray Jan 04 '17 at 20:26
  • It's Webpack 1.14.0 – Brandon Tom Jan 04 '17 at 20:32
  • Can you try `new ExtractTextPlugin(path.resolve(__dirname, 'dist') + '/style.css', { allChunks: true })` – Andy Ray Jan 04 '17 at 21:02
  • Unfortunately, that did not solve the issue. I was able to fix this issue, but my solution still doesn't feel right. I'll post what I came up with. – Brandon Tom Jan 04 '17 at 22:00
  • Do you require the css file from any of your javascript files? that's required for webpack to find it – Andy Ray Jan 04 '17 at 22:13
  • I suppose that makes sense, but since the idea was to simply reference an external stylesheet, I didn't. It turns out if you don't reference the stylesheet within the stylesheet you can still generate a stylesheet, but in order to do so you have to specify the stylesheet as an entry point. – Brandon Tom Jan 04 '17 at 22:34
  • Right. Generally we require the stylesheet from javascript to put it in the main dependency graph (http://blog.andrewray.me/webpack-when-to-use-and-why/) but not required for small projects – Andy Ray Jan 04 '17 at 22:39

2 Answers2

1

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.

Brandon Tom
  • 889
  • 7
  • 16
0

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.

Brandon Tom
  • 889
  • 7
  • 16