Muhammad Umer
Muhammad Umer

Reputation: 18097

What configuration is needed for Webpack to do these things?

I've been reading and following tutorials, but I am stuck. I was able to accomplish things listed in tutorials but not combine them together.

I want to write a configuration file that would do these things:

  1. Compile Sass/Scss files into CSS file into public folder, not inline them anywhere.
  2. Transpile JSX/Es6 files into one file (I got this).
  3. Auto Refresh/Load page when Css OR Html changes.
  4. Auto Refresh/Load page when Javascript changes or React component etc.
  5. Plus Run via my own Nodejs Express server, not use webpack dev server, I want to avoid running multiple simultaneous processes. They make things hard.
  6. Whenever Css or js files changes, put hash into the file name for cache busting.
  7. ...Related to previous step, go through all templates and update urls everywhere they were generated.
  8. ...remove old hashed files.
  9. Minify, and generate Source Maps.

So Compile/Transpile Files, Live/Hot Reload, Hash Filenames, minify, and sourcemaps.

Right now I am thinking of abandoning Webpack, and only use it for transpiling and do everything in Gulpjs.

The stuff in bold is where I had a lot of trouble as well. Existing Solutions include generating JSON file, and reading them per request (inefficent!), generating full html file and injecting them (breaks many flows plus cant modify script tag with special attributes if needed, they get written over).

Upvotes: 0

Views: 283

Answers (2)

Allen
Allen

Reputation: 4749

THERE IS NO ONE FITS FOR ALL!

Many configs aren't necessarily bound to webpack, they can be found in scripts entry of package.json, or just reside in presets of .babelrc. Of course, you can configure everything about ES6 just in webpack, but you can also ONLY put them in package.json, so it's more a flavour/ team convention thing. To make it simple, i would just share one workable solution:

You need loaders for Sass/Scss, JSX/ES6 Transformation

exports.module = {
    rules: [
        {
            test: /\.scss$/,
            use: isProd
                ? ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'sass-loader']
                })
                : ['style-loader', 'css-loader', 'sass-loader']
        },
        {
            test: /\.js|jsx$/,
            use:  'babel-loader',
            exclude: /node_modules/
        }
    ]
}

if (isProd) {
    module.exports.plugins = (module.exports.plugins || []).concat([
        new ExtractTextPlugin({
            filename: '[name].[contenthash].css'
        })
    })  
}

ExtractTextPlugin are used here as you may don't want inline css in production, but i will still use them in development.

You need HotModuleReplacement and react-hot-loader plugins for Code Hot Reloading

Even better than Auto Refresh/Reload a page, it will only reload the changed parts of your app (think about Ajax), this helps to iterate even faster and more importantly, it helps you to retain the state of your app, which is fairly important for large scale SPA where state management, time travel debugging etc can be useful.

if (isDev) {
    module.exports.entry.app = [
        'webpack-hot-middleware/client',
        'react-hot-loader/patch'
    ].concat(module.exports.entry.app)
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.HotModuleReplacementPlugin()
    ])
}

As you don't want webpack-dev-server, give webpack-dev-middleware and webpack-hot-middleware a shot, and they will be used in your server.js in development:

// compile your bundle.js on the fly (in memory serve)
app.use(require("webpack-dev-middleware")(compiler, {
  noInfo: true,
  publicPath: "/"
}))

// notify the browser client updated bundle is ready
app.use(require("webpack-hot-middleware")(compiler))

You need Code Splitting and HtmlWebpackPlugin to hash filenames and auto update the referred urls.

if (isProd) {
    module.exports.output = Object.assign({}, module.exports.output, {
        filename: '[name].[chunkhash].js',
        chunkFilename: '[id].[chunkhash].js'
    })
    module.exports.plugins = (module.exports.plugins || []).concat([
        new HtmlWebpackPlugin({
            template: 'index.ejs',
            inject: true,
            chunksSortMode: 'dependency'
        })
    })
}

To remove old hashed files, just simple script in package.json

"scripts": {
  "build": "rm -rf public/** && NODE_ENV=production webpack --progress --hide-modules"
}

You need UglifyJsPlugin, LoaderOptionsPlugin plugins for Minifying and generating source Maps

if (isProd) {
    module.exports.devtool = '#source-map'
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            },
            sourceMap: true
        }),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true,
            debug: false
        })
    ])
} else {
    module.exports.devtool = '#eval-source-map'
}

A "sum up" webpack.config.js may look like this:

const path = require('path')
const resolve = p => path.resolve(__dirname, p)
const webpack = require('webpack')

const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const isProd = process.env.NODE_ENV === 'production'

module.exports = {
    entry: {
        app: [
            resolve('src/js/main.js')
        ]
    },
    output: {
        path: resolve('public'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: isProd
                    ? ExtractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: ['css-loader', 'sass-loader']
                    })
                    : ['style-loader', 'css-loader', 'sass-loader']
            },
            {
                test: /\.js|jsx$/,
                use:  'babel-loader',
                exclude: /node_modules/
            }
        ]
    }
}

if (isProd) {
    module.exports.devtool = '#source-map'
    module.exports.output = Object.assign({}, module.exports.output, {
        filename: '[name].[chunkhash].js',
        chunkFilename: '[id].[chunkhash].js'
    })
    module.exports.plugins = (module.exports.plugins || []).concat([
        new ExtractTextPlugin({
            filename: '[name].[contenthash].css'
        }),
        new HtmlWebpackPlugin({
            template: 'index.ejs',
            inject: true,
            chunksSortMode: 'dependency'
        }),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            },
            sourceMap: true
        }),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true,
            debug: false
        })
    ])
} else {
    module.exports.entry.app = [
        'webpack-hot-middleware/client',
        'react-hot-loader/patch'
    ].concat(module.exports.entry.app)
    module.exports.devtool = '#eval-source-map'
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.HotModuleReplacementPlugin()
    ])
}

It's only for reference, happy tweaking!

In case you want a quick demo: https://github.com/lxynox/so-example-webpack

Upvotes: 2

riyaz-ali
riyaz-ali

Reputation: 9034

  1. Compile styles into a single file - use Webpack ExtractText Plugin
  2. Transpile ES6/JSX to ES5 - you need a transpiler like Babel
  3. Check point 4
  4. Hot Reloading - Since you mentioned that you are using React you can use react-hot-loader
  5. Run via own node.js server - use webpack-dev-middleware
  6. File Hashes - check this blog
  7. same as 6
  8. Do it manually or via some cron-job
  9. Minify, generate Source-maps - check the documentation for production builds

Upvotes: 0

Related Questions