7

I'm a total newcomer to Webpack (2) so please forgive me for my simple understanding so far.

Following from a few tutorials around the web, I've cobbled together a working package.json and webpack.babel.config.js file.

Essentially, I am trying to turn SCSS into CSS, Pug into HTML and JS into Babel'd JS.

When I run my dist command, it produces the files, but also along with it, are .js files for each .scss and .html etc. Hopefully the image below can illustrate this:

enter image description here

Ideally what I'm after is simply, app.css, index.html and app.js.

webpack.babel.config.js

import webpack from 'webpack';
import path from 'path';
import autoprefixer from 'autoprefixer';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';

const extractHtml = new ExtractTextPlugin({
  filename: '[name].html'
});

const extractPug = new ExtractTextPlugin({
  filename: '[name].[contenthash].pug'
});

const extractScss = new ExtractTextPlugin({
  filename: '[name].[contenthash].css',
  allChunks: true
});

let config = {
  stats: {
    assets: false,
    colors: true,
    version: false,
    hash: true,
    timings: true,
    chunks: false,
    chunkModules: false
  },
  entry: {
    'dist/html/app': [
      path.resolve(__dirname, 'src/pug/app.pug')
    ],
    'dist/js/app': [
      path.resolve(__dirname, 'src/js/app.js')
    ],
    'dist/css/app': [
      path.resolve(__dirname, 'src/scss/app.scss')
    ]
  },
  output: {
    path: path.resolve(__dirname, './'),
    filename: '[name].js'
  },
  module: {
    rules: [
      {
        test: /\.pug$/,
        use: extractHtml.extract({
          use: ['html-loader','pug-html-loader?pretty=false&exports=false']
        })
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: ['babel-loader']
      },
      {
        test: /\.scss$/,
        use: extractScss.extract({
          use: ['css-loader', 'postcss-loader', 'sass-loader']
        })
      }
    ]
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: false,
    stats: 'errors-only',
    open: false
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Portfolio',
      // minify: {
      //   collapseWhitespace: true
      // },
      hash: true,
      template: './dist/html/app.html',
      filename: 'index.html'
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: false,
      debug: true,
      options: {
        babelLoader: {
          presets: [
            ['es2015']
          ]
        },
        postcss: [
          autoprefixer({
            browsers: ['last 2 versions', 'Explorer >= 9', 'Android >= 4']
          })
        ],
        sassLoader: {
          includePaths: [
            path.resolve(__dirname, 'node_modules/sanitize.css/')
          ]
        }
      }
    }),
    extractScss,
    extractHtml,
    extractPug
  ]
}

export default config;

package.json

{
  "name": "portfolio",
  "version": "1.0.0",
  "description": "Portfolio of Michael Pumo",
  "main": "index.js",
  "scripts": {
    "dev": "webpack-dev-server",
    "prod": "webpack -p",
    "dist": "webpack --config webpack.config.babel.js"
  },
  "author": "Michael Pumo",
  "license": "ISC",
  "devDependencies": {
    "autoprefixer": "^6.7.7",
    "babel-core": "^6.24.0",
    "babel-loader": "^6.4.1",
    "babel-preset-es2015": "^6.24.0",
    "css-loader": "^0.27.3",
    "extract-text-webpack-plugin": "^2.1.0",
    "html-loader": "^0.4.5",
    "html-webpack-plugin": "^2.28.0",
    "html-webpack-pug-plugin": "^0.0.3",
    "node-sass": "^4.5.0",
    "postcss-loader": "^1.3.3",
    "pug": "^2.0.0-beta11",
    "pug-html-loader": "1.1.1",
    "sass-loader": "^6.0.3",
    "style-loader": "^0.14.1",
    "webpack": "^2.2.1",
    "webpack-dev-server": "^2.4.2"
  }
}

I have many other issues I'd like to resolve too but I'm taking it one issue at a time. Thanks for your help.

2 Answers 2

2

You don't want to use multiple entries, but instead add it to a single entry. For it to work nicely with the extract-text-webpack-plugin you should also change the output a little. It makes it much easier when you set output.path to the dist/ directory, which also makes more sense conceptually. You'll have one entry point for app and then you set the output for the different file types to the corresponding directories. For JavaScript that's the output.filename option, which you want in js/. Your entry and output should look likes this:

entry: {
  app: [
    path.resolve(__dirname, 'src/pug/app.pug'),
    path.resolve(__dirname, 'src/js/app.js'),
    path.resolve(__dirname, 'src/scss/app.scss')
  ]
},
output: {
  path: path.resolve(__dirname, 'dist'),
  filename: 'js/[name].js'
},

And you do the same thing as with output.filename for the filename in the extract text plugins:

const extractHtml = new ExtractTextPlugin({
  filename: 'html/[name].html'
});

const extractScss = new ExtractTextPlugin({
  filename: 'css/[name].[contenthash].css',
  allChunks: true
});

You didn't use extractPug at all, so I left it off and you probably want to remove it.

The output will look likes this:

dist
├─ css
│  └─ app.50352154102b272d39e16c28ef00c1ac.css
├─ html
│  └─ app.html
├─ js
│  └─ app.js
└─ index.html

Now you also have index.html in the dist directory and you can simply deploy the dist directory, as it's self-contained.

On a side note: You should not use ./dist/html/app.html as a template to html-webpack-plugin but instead use the .pug file directly with the pug-loader, which means that you don't even need to add it to the entry or extract the HTML.

6
  • Thank you. I've now done what you suggested. The files are generated, but there's an error saying: "ERROR in Error: Child compilation failed: Module build failed: Error: "extract-text-webpack-plugin" loader is used without the corresponding plugin". Opening my HTML file in the /app folder, I see it full of errors too. Any ideas? Thanks. Commented Mar 17, 2017 at 19:45
  • The extract-text-webpack-plugin error means that you did not put it into plugins, make sure you have all of them in there, which you had in the config you posted, but maybe you removed one that you're still using. Commented Mar 17, 2017 at 19:49
  • I think it's due to the 'new HtmlWebpackPlugin' in the plugins array. If I comment that part, the build doesn't error. But I need that part to insert the script and link tags, right? The compiled app.html does not contain these parts. I'm still importing the same modules. Commented Mar 17, 2017 at 19:54
  • If you use a .pug file as the template for the html-webpack-plugin, you can't use the extract plugin for /\.pug$/. So you would need to remove that and use pug-loader instead for that rule. And then you can also remove the app.pug from your entry. Commented Mar 17, 2017 at 19:59
  • Yes that was it. I should have read what you said earlier: "...but instead use the .pug file directly with the pug-loader, which means that you don't even need to add it to the entry or extract the HTML". It works now. Thank you very much! Commented Mar 17, 2017 at 20:14
0

From what I understand, there is one output for each of your entry points (this seems to suggest that way). I believe the most common use case is that there is only one entry point (usually app.js or index.js), and all the required files (like css and other js files) are "required" in the app.js (or import if you are using ES6). That way, there is just one output js file. Also, you can define where to store the individual output files for each loader in the loader's query.

For example, this is a part of my webpack config file:

  module: {
    loaders: [
      {test: /\.css$/, loader: 'style-loader!css-loader' },
      {test: /\.js$/, exclude: /node_modules/, loaders: ['babel-loader']},
      {test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/, loader: 'file-loader', query: {name: 'fonts/[name].[ext]'}},
      {test: /\.(jpg|png)$/, loader: 'file-loader', query: {name: 'img/[name].[ext]'}},
      {test: /\.ico$/, loader: 'file-loader?name=[name].[ext]'}
    ]
  }

The query parameter on each loader is specifying the output directory and filename for each of the loaders.

Not the answer you're looking for? Browse other questions tagged or ask your own question.