Starting new (React+Webpack) project from scratch = beginning =(to)=> advanced

Original Post on Medium

I recently had a new challenge, my team and I were facing some issues using Webpack 2, it was taking a lot of time compiling the project in development and building the production bundle, we have to migrate Webpack 2 to 4 for two years old app.

Thanks to that upgrade, we had to learn more and deep concepts about the new Webpack 4, and then I realized that this is actually a good opportunity to write step by step to help others to understand better how Webpack works, performs, and how to start a project from scratch for a scalable application.

What do you need?

You need a least to have some experience with npm and React works if you are starting to learn about Webpack and React you might get overwhelmed.

To follow this tutorial you just need.

  • VSCode (Editor)
  • node.js
  • Git (I’m using GitHub to track every single step) you can also get a copy of the source code.

Creating a new project – fresh start:_

Open your terminal and create a new folder project whenever you like.

$ mkdir webpack-demo
$ cd webpack-demo
$ npm init

The first thing that we need to do is install Webpack and Webpack-cli

npm install -D webpack webpack-cli --save

Open package.json and add the start command

"start": "webpack --config webpack.config.js"

Create the Webpack config file $ touch webpack.config.js

After that, we need to add at least one javascript file to make Webpack work

mkdir src
touch src/index.js
echo "console.log('Hello world')" >> src/index.js

Run npm run start and if everything is fine so far you will see the following screen.

As you can see above, in Webpack ≥ 4.x, we need to either set the mode to production or development. It sets to production by default.

Webpack team is working hard to make Webpack easy to use and they introduced in version 4 zero-configuration, which means we don’t need to add webpack.config.js but at the difference of Parcel we will need to add our Loaders to be able to handle and load all assets likejpg, png, svg, fonts, html and more, this is the main reason why some people prefer Parcel but sometimes we don’t need to add all loaders to our project.

Notice after running npm run start, a new file was added in the dist directory

Advance Webpack configuration

To make Webpack run better, we’re going to add more custom configurations to our webpack.config.js.

First of all, we need to know the structure of our webpack.config.js this is how it’s looks like:

module.exports = env => {
 const config = {
   mode: "",           //String
   entry: {},          //Object
   output: {},
   module: {
     rules: []
   },
   resolve: {},
   plugins: [],        //Array
   optimization: {
     splitChunks:{}     
     minimizer: []
   },
   devServer: {},
   devTools: ""
}; 
  return config;
};

Be aware Webpack is not only limited to the above structure. Our goal is to go through each property, understand it, and set it up.

Mode

This is a new option included in Webpack 4, now is very straightforward to tell Webpack in what mode it needs to perform, by default this option has been settled up production mode, and when production is enabled the optimization plugins will be enabled as well it means we don’t need to enable or disable manually like we used to do in previous versions. The mode expects a string parameter and it can be settled as production, development, and others.

We can set the operational mode through the CLI in our package.json using the flag, --mode="(development) or (production)" or using the envvariable. In this demo, we are going to use environmental variables to handle it through the CLI.

module.exports = env => {
 const config = {
   mode: env.NODE_ENV === "development" ? "development" : "production"
};
return config;
};
//note if env.NODE_ENV.development is defined on the CLI set mode as Development if not set it as Production

Now we can pass variables through the CLI like this.

"scripts": {
    "start": "webpack --config webpack.config.js --env.NODE_ENV=development",
    "build": "webpack --config webpack.config.js --env.NODE_ENV=production"
},
     npm run start // will be used for development mode
     npm run bluid  // will be used for production mode

Using this approach you will be able to use the production or development modes to enable or disabled options in the webpack.config.jsconfigurations depending on your environment without duplicating the code and using several files one for production and another for development.

Make npm run start and you will see the warning is gone.

npm run start for development mode

Entry Point

We need to tell Webpack which file or files to use to start building the project, also we can separate our bundle into App and Vendors.

App: it’s going to take all the custom code and then make a bundle.

Vendors: it’s going to take all dependencies and will make vendors bundle.

First of all, we need to include path to our webpack.config.js to locate files in our project, also you don’t need to install path because it included in Node.js

https://gist.github.com/victors1681/9f2f6ef2374bed0014c3b8964c40867d

Please notice that I used the key app inside the entry object, we can use any name like main mainApp , after run npm run start Webpack will generate a new bundle called app.js and we will use it later in our HTML file.

result of npm run start

At this point, we have our first bundle, now is the time to add ReactJS to our project, and will generate a bundle separated one for our App and another for our Vendors.

npm install react react-dom --save

Then add it to our entry point those dependencies

entry: {
app: [path.resolve(__dirname, "./src/index.js")],
vendors: ["react", "react-dom"]
}

$ npm run build

Showing the result of the entry configuration
const path = require("path");

module.exports = env => {
 const config = {
    mode: env.NODE_ENV === "development" ? "development" : "production",
    entry: {
       app: [path.resolve(__dirname, "./src/index.js")]
    }
  };
return config;
};

Output

Using output we can tell Webpack where we want to put our bundle instead to use the default path and directory /dist, and also we are able to use the hash sum to our bundle file to improve the cache.

For instance, we have app.js we are planning to include this bundle in our index.html and the browser will cache that file app.js depending on the browser we might have some issues if we redeploy our application with new changes, perhaps the browser will load an old version of our file, to avoid that is a good idea to use hash sum likeapp.23434c.js even if we make small changes to our app the browser will handle the new version properly.

We are going to add some configuration to our output object.

output: {
  path: path.join(__dirname, ".", "myDistribution", "ui"),
  filename: "js/[name]".concat(".[chunkhash:8].js")
}

$ npm run build

Custom output generated

Because we changed the default directory you will be able to see those files in myDistribution/ui/js and now the hash sum is appended to app and vendors.

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Resolve

Tell Webpack how to locate our files, for a small project it doesn’t matter but for the biggest one will be very helpful, also this feature allows us to add an alias to use it as a shortcut for our modules.

resolve: {
alias: {
view: path.resolve(__dirname, "./src/View"),
container: path.resolve(__dirname, "./src/Container")
  },
  extensions: [".js"],
  modules: [
    path.resolve(__dirname, "./src"),
    path.resolve(__dirname, "./node_modules")
  ]
}

At this moment we don’t have any View andContainer the directory inside of our src folder, but using the alias we will be able to reach those folders from anywhere.

Please take a look at the following folder structure.

|-src
|--View
|--|--Header
|--|--|--TestingComponent
|--|--|--|--testing.js 
|--Container
|--|--Links
|--|--|--LinksContainer
|--|--|--|--index.js

Imagine that we’re working in the testing.js located in src/View/Header/TestingComponent/testing.js and we need to import LinksContainer you might accomplish it by doing something like

import LinksContainer from '../../../Container/Links/LinksContainer'

Because we already set an alias and, we will be able to use it like this:

import LinksContainer from 'Container/Links/LinksContainer'

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

DevServer

Now we are going to config our development server, and first of all, we need to install it as a development dependency.

$ npm install webpack-dev-server -D --save

Let’s create a public folder in our root project and then create a new file called index.html this file will hold our JSs bundles.

$ mkdir public && touch public/index.html && echo "<html><head><title>My App</title></head><body>Hello</body></html>" >> public/index.html

VSCode files structure and the new index.html

Open the package.json and change webpack to webpack-dev-server

"start": "webpack --config webpack.config.js --env.NODE_ENV=development",
to
"start": "webpack-dev-server --config webpack.config.js --env.NODE_ENV=development",

Add the devServer configuration

devServer: {
  contentBase: path.join(__dirname, "public"),
  compress: true,
  port: 9000
}

Now we are able to run npm run start and then open http://localhost:9000/

After run devServer running at http://localhost:9000
open http://localhost:9000

Next step is to include our bundle js app.js and vendor.js into our HTML file index.html but we can not add the bundle manually into the HTML header because our bundle has a hash remember is app.[chunkhash].js to inject our bundle we will need to use a plugin called HtmlWebpackPlugin.

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Plugins

In order to customize the Webpack building process, we can use plugins. To inject our bundle.js into our HTML let’s install and config HtmlWebpackPlugin

$ npm install --save-dev html-webpack-plugin

After installing the plugin then import it into webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
...
plugins: [
  new HtmlWebpackPlugin()
], ...

Now we need to add a couple of configurations to HtmlWebpackPlugin

plugins: [
  new HtmlWebpackPlugin({
     filename: "index.html",
     template: path.join(__dirname, "public/index.html"),
     inject: true,
chunks: ["app", "vendors"], })
],

$ npm run start

Bundles dynamically appended into index.html

Now you will be able to see app.[chunkhash].js and vendors.[chunkhash].js injected in our index.html

Our app is working!

Javascript executed console.log(“Hello world”)

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Modules

The module can be from the application code or a third-party library. The resolver helps Webpack find the module code that needs to be included in the bundle for every such require / import statement.

At this point, we need a plugin to transpile our Javascript code to make it run and compatible with any browsers, Babel can help us to accomplish that.

Babel converts ES6-ES7 syntaxes into a plaint ES5 Javascript.

ECMAScript 5 is also known as ES5 and ECMAScript 2009

ECMAScript 6 is also known as ES6 and ECMAScript 2015.

ECMAScript 2017

ES6 code

const sum = (a, b) => a + b;

Babel converts to ES5:

var sum = function sum(a, b) {
  return a + b;
};

Using the result we will be able to execute our code in any browser without worry about compatibility.

Installation and configuration

npm install --save-dev @babel/core @babel/cli @babel/preset-env

In order to make Webpack use Babel, we need a loader

npm install --save-dev babel-loader

Instead, to use Babel in our package.json it’s a better practice to create a Babel config file move to root directory and run the command below.

$ touch babel.config.js

Note: for a previous version, you might see different name like .babelrc for new Babel ≥ 7 we are able to use babel.config.js babel.config.json .babelrc.js or .babelrc It all depends on you, if you want to use the static or dynamic file, babel always will look for one of those names.

After opening our new file babel.config.js we can declare our new module like this:

module.exports = api => {
api.cache(true); //new babel 7 feature
return {
   presets: ["@babel/preset-env"],
   plugins: []
 };
};

Since you want to use React you need to add more configuration to transform the React’s JSX syntax to vanilla Javascript.

npm install --save-dev @babel/preset-react

and then add the new preset installed in webpack.config.js

module.exports = api => {
  api.cache(true);
 return {
   presets: ["@babel/preset-env", "@babel/preset-react"],
   plugins: []
 };
};

Later we are going to expand our configuration by adding plugins to babel.config.js

Now we have our configuration for Babel completed, and now we need to tell Webpack to load those configurations and transpile the all ES6 code.

Go back to webpack.config.js and add new rule modules in the webpack JSON structure.

module: {
  rules: [{
    test: /.(js)$/,
    exclude: /node_modules/,
    use: ["babel-loader"]
  }]
},

Adding this rule we are telling to Webpack, find all JS files in our project and transpile it but exclude our vendors, vendors are already transpired.

At this point, our app is ready to use Reactjs let’s open src/index.js remove console.log(‘Hello world’) and add the following code into it.

import React from 'react';
import ReactDOM from 'react-dom';

const content = 'This is my react app';

ReactDOM.render(
  {content},
  document.getElementById('mainApp')
);

$ npm run start

React App is working!

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Babel Plugins

Our app is transpiring the ES6 basic syntax now we are going to make our app slightly complicated. Create a new directory  src called View and add a new file to it called Counter.js

$ mkdir src/View

$ touch src/View/Counter.js

https://gist.github.com/victors1681/184ee7f986e32e8d70707dbc0bc807f5

Then open src/index.js and add the new counter.js component

https://gist.github.com/victors1681/f7c9fc020b9201e36a60c5d6c487045b

After do$ npm run start you definitely will have an error like this:

Babel webpack transpile error

It’s because Babel does not recognize classes, to fix that we need to install and add @babel/plugin-proposal-class-properties to babel.config.js

npm install --save-dev @babel/plugin-proposal-class-properties

https://gist.github.com/victors1681/4e3a313d78472fc8f77aa8a83c1728d0

Then make $ npm run start

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Optimization

It’s really important to optimize our application for production, Webpack ≥ 4 is making our life easy bringing tools to accomplish that.

Note: Webpack ≥ 4 introduced optimization for chunk splitting this new feature madeCommonsChunkPlugindeprecated.

First of all, we’re going to install terser-webpack-plugin this plugin is similar to UglifyJS-webpack-plugin I started using terse for Webpack 4 because has better performance at runtime, this plugin allows us to compress and minimize our bundles and optimize them for production.

$ npm install terser-webpack-plugin --save-dev

Open webpack.config.js import the terser-webpack-plugin and add the plugin under the optimization/minimizer object.

const TerserPlugin = require("terser-webpack-plugin");

optimization: {
minimizer: [
new TerserPlugin({
        cache: true,
        parallel: true,
        terserOptions: {
               compress: {
               dead_code: true,
               conditionals: true,
               booleans: true
        }, 
        module: false,
        output: {
           comments: false,
           beautify: false,
        }
      }
     })
   ]
}

Please check the terser-webpack-plugin repository to add more configuration if you need it.

After the configuration has been added you can go ahead and make

$ npm run build

Your production bundle will look like this.

Production Bundle minimized

The next screenshot shows our production bundle before minimized, notice there are comments and also functions name will be largest.

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

SplitChunksPlugin

Once again we’re going to talk about performance, Split Chunks allow us to split our code into multiples files, you might ask why splitting our code improves the performance of the application, well the fact is browser cache will be better, imagine we make a small change for our application and that change only affect a particular module, instead to load a big bundle will be better only to load a small piece of the app.

Concept of SplitChunksPlugins

SplitChunks also allows for avoiding duplicate code we don’t want to have a new copy of our dependencies for each chunk, to visualize that we are going to introduce webpack-bundle-analyzer

npm install --save-dev webpack-bundle-analyzer

Open package.json and add a new script to activate the analyze plugin also a flag env.analyze to only run the plugin whenever we want.

"analyze": "npm run start -- --env.analyze"

Add this plugin to webpack.config.js bellow to the HtmlWebpackPlugin

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;


plugins: [
    new HtmlWebpackPlugin({
       filename: "index.html",
       template: path.join(__dirname, "public/index.html"),
       inject: true,
       chunks: ["app", "vendors"]
    }),
 env.analyze && new BundleAnalyzerPlugin()
]

Fixes: The previous code won’t work if we execute $ npm run start needs to be fixed. Plugins key is expecting an Array of plugins, if env.analyze is not defined we will setting an invalid array will looks like this :

plugins: [{HtmlWebpackPlugin Object}, false]; //Fail

In order to fix this issue, we can create a new function to return only activated plugins

const getPlugins = env => [
new HtmlWebpackPlugin({
 filename: "index.html",
   template: path.join(__dirname, "public/index.html"), 
   inject: true,
   chunks: ["app", "vendors"]
}),
env.analyze && new BundleAnalyzerPlugin()
].filter(plugin => plugin); //if env.analyze is false will return only [HtmlWebpackPlugin]

Then call the get plugins function from the plugin object

...
},
plugins: getPlugins(env),
 module: {
   ...

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Now we can run the analysis script

npm run analyze

After that, the devServer will run and the analyze plugin will launch a new browser window http://127.0.0.1:8888/.

webpack bundle analyze

After the launch, bundle-analyze you might notice that both bundle app.js and vendors.js both have a copy of the dependencies like react-dom and also have almost the same size ~429 kb and the size or our app 858.97 kb this is really bad, now is time to add split chunk.

Go to webpack.config.js and reach the optimization object.

optimization: {
splitChunks: {
  cacheGroups: {
vendors: {
     test: /[\/]node_modules[\/]/,
     name: "vendors",
     chunks: "all"
  }
 }
},
 minimizer: [
...

run again the bundle-analyze

npm run analyze

As you can see the size of our app is 431.61 KB because we don’t have a copy of our dependency on each bundle.

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Speeding up the Webpack building process

As our project grows the building process takes longer for production bundle and development, there’re some techniques we can implement to speed up our building time for Webpack ≥ 4, also Webpack team are working to brings more tools to help to face that issues like persistent cache perhaps will be available for futures version of Webpack, some of the time consumed during the building process is taken by Babel converting our code, Cache Loader Thread Loader can help us to better performance.

npm install --save-dev cache-loader thread-loader

After installing it we can use those plugins in our bable-loader, open webpack.config.js and modify the loader.

module: {
  rules: [{
      test: /.(js)$/,
      exclude: /node_modules/,
      use: [
         "cache-loader",
         "babel-loader?cacheDirectory",
         "thread-loader"
        ]
      }]
},

The following screenshot was taken before implementing Cache-loader and Thread-loader

Production Bundle without Thread and Cache 825ms

After

npm run dist

Production Bundle after implement Thread and Cache 470ms

Because our application is too small we can not see a real improvement but our bundle was built 50% faster than before and it using the base configuration if you take a look at the documentation of thread-loader we also can increase the number of worker parallel jobs up to 50.

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

CSS-loader & Sass-loader & SplitChunk

CSS plays a very important role in our front-end application, it means we might have tons of CSS code that also needs to be performed. In this section, we are going to install css-loader sass-loaderand also we are going to extract all CSS and bundle it into a single file to cached.

npm install sass-loader node-sass style-loader css-loader  -D

After installation, we can add a new rule to find all css and sass in modules in webpack.config.js

        {
            test: /.(sc|c)ss$/,
            use: [
                "style-loader", 
                "css-loader", 
                "sass-loader"
            ]
        }

To make sure everything is working properly let’s add son CSS styles to our project.

Go to src/View/ and add a new file, Counter.scss

$ touch src/View/Counter.scss , and add some classes;

https://gist.github.com/victors1681/1016c0c7245a014b0ec6eb044d91059a

Import the Counter.scss into the Counter.js

import './Counter.scss'; and add those classes to the JSX code.

https://gist.github.com/victors1681/aa02e93bbac88618dadc9fee2c800cc9

$ npm run start

Component with CSS

Everything is working as expected, the scss was loaded and compiled and applied correctly in our component.

If you notice all CSS properties are loaded inline inside the HTML file, and we can not cache it, the browser will load every time all CSS of our app, to perform the production bundle we can make some adjustments to extract all CSS and compile a new style.css bundle, that way all browsers will able to cache.

mini-css-extract-plugin will help us to extract all CSS.

npm install --save-dev mini-css-extract-plugin

Go to webpack.config.js import the plugin

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

Go to getPlugins and add the new one, notice this plugin only can be used in production mode.

env.analyze && new BundleAnalyzerPlugin(),
env.production && new MiniCssExtractPlugin({
   filename: "css/[name].[chunkhash:8].css"
})

and then modify the loader to only extract all CSS in production.

{
  test: /.(sc|c)ss$/,
  use: [
env.NODE_ENV === "development" ? "style-loader" : MiniCssExtractPlugin.loader,
    "css-loader",
    "sass-loader"
   ]
}
//if development use 'style-loader' if not use MiniCssExtractPlugin

Then locate optimization the object and add new cacheGroup

splitChunks: {
  cacheGroups: {
    vendors: {
      test: /[\/]node_modules[\/]/,
      name: "vendors",
      chunks: "all"
    },
styles: {
       test: /.css$/,
       name: 'styles',
       chunks: 'all',
       enforce: true
   }
  },
},
Extracting CSS to the Browser cache

To illustrate how splitChunk and mini-css-extract-plugin extracted all CSS in one I added another component called Login.js Login.scssand ViewSelector.js

https://gist.github.com/victors1681/6e3f816e743beff314018cf7f772ef8fhttps://gist.github.com/victors1681/17b4d37da8404e8c979af366e35cffc4https://gist.github.com/victors1681/6aec1cae3f5f73c950e07cad8869ccebhttps://gist.github.com/victors1681/1446e53b6c1454d27aa9ebff14514908

One file was generated app.b96....css and all CSS combined.

Show a single CSS file

The last thing we need to do is to generate a minimized version of our CSS file.

$ npm install --save-dev optimize-css-assets-webpack-plugin

In webpack.config.js import, the plugin

const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");

optimization.minimizer: [] add the new plugin to the minimizer array

new OptimizeCssAssetsPlugin({
   assetNameRegExp: /.css$/g,
   cssProcessor: require("cssnano"),
   cssProcessorPluginOptions: {
       preset: ["default", { discardComments: { removeAll: true } }]
   },
   canPrint: true
})

$ npm run build

CSS minimized

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

HMR (Hot Module Replacement) & React-Hot-Loader

HMR exchanges, adds or removes modules while an application is running, it means every time you make changes in your CSS or JS the browser will update without a full reload.

Let’s try this out!

First of all, we need to add the property hot to our devServer in webpack.config.js

devServer: {
  contentBase: path.join(__dirname, "public"),
  compress: true,
  port: 9000,
  hot: true
},

Add the HotModuleReplacementPlugin to our getPlugins in Webpack configuration.

https://gist.github.com/victors1681/94c418cae460885ae04da7ccb6d3cc4c

//Only on development
env.NODE_ENV.development && webpack.HotModuleReplacementPlugin()

Then npm run start and you will see HMR will be enabled in the WDS (webpack dev server).

HMR Hot Module Replacement enabled

To test this out go to src/View/Login/login.scss to change the button color to

background: green;

Save it and HMR will update our app and the browser will update without full reload.

It’s working, but what about javascript changes.

HMR losing the current state

HMR is not working we have a full load, to fix that issue we need to add some code to our main tileindex.js

if (module.hot) {
   module.hot.accept("./View/ViewSelector", function() {
ReactDOM.render(<ViewSelector />,
   document.getElementById("mainApp"));
   });
}

Now change back Sign In without full load to Sign in and HRM work’s fine.

HMR (hot module replacement)

Now let’s toggle our view click Toggle View and then make some changes to Counter.js

<h1 className="counter-header">This is my counter Hot Reload</h1>

Notice we don’t have a full reload but we are losing the React state

HMR Losing React State

To fix this issue we need help from React-Hot-Loader

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Configuring React-Hot-Loader:

npm install react-hot-loader -D

Then move to babel.config.js

// babel.config.js
{
  "plugins": ["react-hot-loader/babel"]
}

Please notice there we’re going to add more changes that we don’t have in the getting started React-Hot-Loader documentation

module.exports = api => {
   api.cache(true);
 return {
    presets: [
["@babel/preset-env", {
          loose: true,
          modules: false
        }
], "@babel/preset-react"],
    plugins: [
["@babel/plugin-proposal-class-properties",{ "loose": true }], 
"react-hot-loader/babel"
     ]
};
};

Then open ViewSelector.js our parent file included in index.js

import { hot } from 'react-hot-loader'
.....
export default hot(module)(ViewSelector);

Then remove the rerender code added in index.js

// if (module.hot) {
//     module.hot.accept("./View/ViewSelector", function() {
//         ReactDOM.render(<ViewSelector />, document.getElementById("mainApp"));
//  });
// }
HMR & React-hot-loader

Every time we update our component we don’t have a full reload and we not lose the state.

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Cleaning Distribution folder.

You might notice that every time we run npm run dist new files are generated in the directory, to fix that issue we’re going to use clean-webpack-plugin this plugin allows us to clean up removing old files.

npm install --save-dev clean-webpack-plugin

Add it into webpack.config.js and append the plugin into the plugin object, only should be executed for distribution and specify the directories to clean it up, add myDistribution/ui/js and myDistribution/ui/css.

const CleanWebpackPlugin = require('clean-webpack-plugin');
...
env.NODE_ENV === "production"  && new CleanWebpackPlugin(['myDistribution/ui/js', 'myDistribution/ui/css'])

https://gist.github.com/victors1681/b9d610d08b7525bd0a7cc674bfb0b6ff

Then npm run dist and you will see only the latest

clean-webpack-plugin

Webpack Configuration Refactorization

So far this is how your Webpack configuration looks like, I prefer to split webpack.config.js into multiples files to make it easy to read and maintain.

Repository of this tutorial

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

Webpack Tutorial configuration.

https://gist.github.com/victors1681/20e51bd4d43666ab5be7445d46d66b16

Webpack Refactorized

I created a new repository to make life easy every time I start a new project.

https://github.com/victors1681/webpack-demo/commit/1c0cc7a99d5f0f18215b3448a5b96b57c7c121e5

+ Babel plugins added.

@babel/plugin-proposal-object-rest-spread

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; 
console.log(x); // 1 
console.log(y); // 2 
console.log(z); // { a: 3, b: 4 }

@babel/plugin-transform-async-to-generator

async function foo() {   await bar(); }

to

var _asyncToGenerator = function (fn) {   ... }; var foo = _asyncToGenerator(function* () {   yield bar(); });

@babel/plugin-syntax-dynamic-import

@babel/plugin-transform-modules-commonjs

@babel/polyfill Babel plugin that includes a polyfill that includes a custom regenerator runtime and core-js.

@babel/plugin-proposal-export-namespace-from

export * as ns from 'mod';

@babel/plugin-proposal-throw-expressions

function test(param = throw new Error('required!')) {   
    const test = param === true || throw new Error('Falsey!'); 
}

Leave a Comment