Category Archives: TUTORIALS

Detailed tutorials explaining how to build most common applications, in different languages.

Adding Webpack-dev-server

branch-name:  
Click To Copy

Ading Webpack-dev-server

what is webpackdevserver ? Like it was said  “The webpackdevserver is a little Node.js Express server, which uses the webpackdev-middleware to serve a webpack bundle. It also has a little runtime script, which is connected to the server via Sock.js. The server emits information about the compilation state to the client, which reacts to those events.

Basically webpack-dev-server is handy dev tool that is not only an HTTP server but it could monitor your files for changes and rebuild the bundle and serve it from memory which speeds up the developing process.

Let’s add webpack-dev-server so we could open the project via http. Additionally  Webpack dev server adds a few more benefits like live reloading for the client side code.
Webpack dev server like it’s name suggests should be used only for development and never in production.

yarn add webpack-dev-server --dev

Let’s add a script to execute it through Yarn/NPM. Also, since we have the entry and the output set up in webpack.config.js we can simplify the build-dev and build-prod scripts as well.

  "scripts": {
    "start": "webpack-dev-server",
    "clean": "rm -rf ./dist",
    "lint": "eslint .",
    "build-dev": "webpack --mode development",
    "build-prod": "webpack --mode production"
  }

Let’s give it a try and start the server:

yarn start

and point the browser to http://localhost:8080/

If everything worked fine, you should be able to see the project and the console message.

But bundle file is still served statically from the ./dist` folder instead of bundled dynamically from Webpack-dev-server and serving it from memory,  and you could confirm this by deleting the ./dist folder, and restarting the server. And you will get this error message in the console.

GET http://localhost:8080/dist/main-bundle.js net::ERR_ABORTED 404 (Not Found)

As you see bundle file is not there, instead Webpack-dev-server is serving it from here:http://localhost:8080/main-bundle.js
Point your browser there and you will see the bundle file. Let’s fix this. Open ./webpack.config.js and add the highlighted lines:

./webpack.config.js

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',
    './src/app.js'
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist',
  },  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};

This instructs Webpack-dev-server to serve the bundle from ./dist location but this time the bundle will be dynamically generated by Webpack-dev-server, instead of statically served from the ./dist location.

The advantage is that the bundle will re-generate on change and the location will be the same as the production location but just served from the memory.
Reload the browser and now the error message in the console is gone, and if you navigate to `http://localhost:8080/dist/main-bundle.js` you will see newly generated bundle.

 Webpack-dev-server is serving the bundle from memory. You could go ahed and delete ./dist folder and reload the server and the main bundle should still be accessible.

Set up Webpack to monitor your project’s folder for changes and rebuild the bundle.

Webpack-dev-server comes with very handy option --watch to monitor for changes, rebuild the bundle and restart the browser.
The god news is that by default the --watch` mode it turned on so if you go and do some changes into any file under ./src folder and save it, you will see in the console that Webpack-dev-server will re-generate the bundles and the browser will restart and reflect the changes.

There are two ways of setting up Webpack-dev-server to reload the page:

  • Iframe mode (page is embedded in an iframe and reloaded on change)
  • Inline mode (a small webpack-dev-server client entry is added to the bundle which refreshes the page on change)

But we could go even further.

Adding Hot Module Replacement plug-in.

Having Webpack-dev-server to reload our page and reflect the changes is cool but we still reload the whole page and components state is reset back to the initial state. We could tell Webpack-dev-server to use hot module replacement plugin (HMR) to re-load only the components that did change, but before doing this, let’s make some real component that will show something in the website instead of the console.

Remove ./src/test.js.

Let’s create greeting component that will simply show a greeting in the webpage.
create folder called ‘greeting’ and file in ./src/greeting/index.js with these contents:

./src/greeting/index.js

function doGreeting(name) {
  return "Hello " + name;
}
module.exports = doGreeting;

edit ./src/app.js to load the new component and attach the result to an HTML div component. We have to also add the highlighted if statement that will enable HMR.

./src/app.js

var greeting = require('./greeting');

var result = greeting("John");
document.querySelector('#root').innerHTML = result;


if (module.hot) {
  module.hot.accept();
}

and edit index.html to have a div component with id=”root” that  will be the entry point for our app and will show the result of our greeting component.

./index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Babel Webpack Boilerplate</title>
    </head>
    <body>
        <div id="root"></div>
        <script type="text/javascript" src="dist/main-bundle.js"></script>
    </body>
</html>

Last step is to tell Webpack-dev-server to use HMR.

./package.json

{
  "name": "babel-webpack-react-boilerplate",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.1.2",
    "@babel/preset-env": "^7.1.0",
    "@babel/preset-react": "^7.0.0",
    "babel-eslint": "^10.0.1",
    "babel-loader": "^8.0.4",
    "eslint": "^5.7.0",
    "eslint-loader": "^2.1.1",
    "eslint-plugin-react": "^7.11.1",
    "webpack": "^4.20.2",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.9"
  },
  "scripts": {
    "start": "webpack-dev-server --hot",
    "clean": "rm -rf ./dist",
    "lint": "eslint .",
    "build-dev": "webpack --mode development",
    "build-prod": "webpack --mode production"
  }
}

what we just did:
– we added --hot parameter to tell Webpack-dev-server to use HMR.

Go ahead, restart the server and give it a try and now if you change something in ./src folder the browser will reflect the changes without reloading. (you could observe the reload button and it will never show reload). Also if you look at browser’s console you will see the messages from HMR

[HMR] Waiting for update signal from WDS…
client:71 [WDS] Hot Module Replacement enabled.

Now development process could really speed up!

branch-name:  
Click To Copy

 

Setting up SSL host using LetsEncrypt free certificate and set up auto renewal shell script.

1. Obtain SSL certificates from the letsencrypt.org ACME server.Navigate to the location that you would like to add the script. I personally prefer to keep apps and scripts here:
cd ~/Applications

2. Download getssl (this is the auto renewal shell script)
git clone https://github.com/srvrco/getssl.git

4. Do initial setup
 ./getssl -c yourdomain.com

this command will create the following folders and files:

~/.getssl
~/.getssl/getssl.cfg
~/.getssl/yourdomain.com
~/.getssl/yourdomain.com/getssl.cfg

5. Edit ~/.getssl/getssl.cfg There are a couple of things that we have to set up:
– make sure that you switch to use the staging server while testing, since the production one has rate limits.

# The staging server is best for testing
CA="https://acme-staging.api.letsencrypt.org"
# This server issues full certificates, however has rate limits
#CA="https://acme-v01.api.letsencrypt.org"

6.Add the sub domains in ~/.getssl/yourdomain.com/getssl.cfg

# Additional domains - this could be multiple domains / subdomains in a comma separated list
# Note: this is Additional domains - so should not include the primary domain.
SANS="www.mysite.com, other.mysite.com"

– Very important: add the location where the script should put verification file in order to prove that you have ownership over this domain. For more information of what exactly is the prove of ownership procedure you could read https://letsencrypt.org/how-it-works/ but basically the bash script puts a small file into specific server directory and then the letsencrypt server checks id the file is there and ensures that you have control over this domain.
So Make sure that this folder (acme-challenge) is accessible on the web.
What that means is that if you put a test text file (e.e. test.txt) with any random text inside in this location: /webroot/.well-known/acme-challenge and then you open the browser and point to www.mysite.com/.well-known/acme-challenge/test.txt you should be able to see the contents of the file.
Once you did this you could go ahead and edit .getssl file and add the right location.

# Acme Challenge Location. The first line for the domain, the following ones for each additional domain.
# If these start with ssh: then the next variable is assumed to be the hostname and the rest the location.
# An ssh key will be needed to provide you with access to the remote server.
# Optionally, you can specify a different userid for ssh/scp to use on the remote server before the @ sign.
# If left blank, the username on the local server will be used to authenticate against the remote server.
# If these start with ftp: then the next variables are ftpuserid:ftppassword:servername:ACL_location
# These should be of the form "/path/to/your/website/folder/.well-known/acme-challenge"
# where "/path/to/your/website/folder/" is the path, on your web server, to the web root for your domain.
#ACL=('/var/www/toninichev.com/web/.well-known/acme-challenge'
#     'ssh:server5:/var/www/toninichev.com/web/.well-known/acme-challenge'
#     'ssh:sshuserid@server5:/var/www/toninichev.com/web/.well-known/acme-challenge'
#     'ftp:ftpuserid:ftppassword:toninichev.com:/web/.well-known/acme-challenge')

ACL=('/var/www/html/projects/src/webroot/.well-known/acme-challenge')

 Make sure that you replace /var/www/html/projects/src/ with the actual location on your server.
– one last thing that I did is to use single ACL to make my life easier

#Set USE_SINGLE_ACL="true" to use a single ACL for all checks
USE_SINGLE_ACL="true"

7. Execute the script and create certificate

~/Applications/getssl yourdomain.com

Again make sure that you navigate to the place where you did git clone of the script.

Example of adding certificate to an express server:

var options = {
    key: fs.readFileSync('/Users/toninichev/.getssl/mysite.com.key'),
    cert: fs.readFileSync('/Users/toninichev/.getssl/mysite.com.crt')
    };

/**
 * HTTP Server
 * Gets post requests from app_clients and sends data to the web_clients
 */
var app = https.createServer(options, function (request, response) {
...
});

Example of adding certificate to Apache virtual host:

<VirtualHost *:443>

    ServerName mywebsite.com
    ServerAlias www.mywebsite.com
    ServerAdmin info@mywebsite.com

    SSLEngine on
    SSLCertificateFile /Users/toninichev/.getssl/mywebsite.com/mywebsite.com.crt
    SSLCertificateKeyFile /Users/toninichev/.getssl/mywebsite.com/mywebsite.com.key

    ErrorLog "/private/var/log/apache2/mywebsite.com-error_log"
    CustomLog "/private/var/log/apache2/mywebsite.com-access_log" common

    SetEnv APPLICATION_ENV production
    DocumentRoot "/Users/toninichev/mywebsite/app/webroot"
    <Directory "/Users/toninichev/mywebsite/app/webroot">
        Options Indexes FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

 

 

Median of Two Sorted Arrays

[tabby title=”Task”]

Task:

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

You may assume nums1 and nums2 cannot be both empty.

Example 1:

nums1 = [1, 3]
nums2 = [2]

The median is 2.0
Example 2:

nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

This problem was taken from Leetcode

[tabby title=”Solution”]
Using a brute force we could merge the two arrays and find the median, but this is slow.
To optimize the solution we could use one of the properties of the arrays: the arrays are sorted.
– We could substitute the problem of find the median with find the numbers that are not in the median

Two sum diagram

The solution:

Java Script

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var findMedianSortedArrays = function(nums1, nums2) {

	function findMedianIndex(arr) {
		var median = 0;
		var i = 0;
		if(arr.length % 2) {
			var i = (arr.length / 2 + 0.5) - 1;
			median = arr[i];
		}
		else {
			var i = (arr.length / 2);
			median = ((arr[i - 1] + arr[i]) / 2);
		}
		return median;
	}

	function isFirstArrayInTheMiddleOfSecondEvenArray(x, y) {		
		var minX = x[0];
		var maxX = x [1];
		var medianY = Math.floor(y.length / 2);
		var leftMiddleY = y[medianY - 1];
		var rightMiddleY = y[medianY];
		return minX > leftMiddleY && maxX < rightMiddleY; } function findMedianOfArrayAndValue(arr, val) { if(arr.length == 0) { return val; } else if (typeof(val) == 'undefined') { return findMedianIndex(arr); } var median = 0; if(arr.length % 2) { // odd aray // var medianIndex = Math.floor((arr.length / 2)); // median = arr[medianIndex]; var arrMedian = findMedianIndex(arr); if(arrMedian > val) {
				// the median of merged array should lie on the left of arr  <== var i = Math.floor(arr.length / 2); var right = arr[i]; var left = Math.max( arr[i - 1], val ); return findMedianIndex( [left, right] ); } else { // the median of merged array should lie on the right of arr ==>
				var i = Math.floor(arr.length / 2);
				var left = arr[i];				
				var right = Math.min( arr[i + 1], val );
				return 	findMedianIndex( [left, right] );					
			}
		}
		else {
			// even aray
			var arrMedian = findMedianIndex(arr);
			if(arrMedian > val) {
				// the median of merged array should lie on the left of arr median  <== var i = Math.floor((arr.length / 2) - 1); var left = arr[i]; return Math.max(left, val); } else { // the median lies on the right side ==>
				var i = (arr.length / 2);
				var right = arr[i];
				return Math.min(right, val);
			}

			median = findMedianIndex( [ arr[medianIndex], arr[medianIndex + 1] ]);

		}
		return ( Math.min(median, val) + Math.max(median, val) ) / 2;
	}


	function findMedian(X, Y) {

		// check all odd cases	
		if (X.length === 1 && Y.length === 1) {
			return (X[0] + Y[0]) / 2;
		}

		if(X.length <= 1) {
			return findMedianOfArrayAndValue(Y, X[0]);
		}
		else if(Y.length <= 1) {
			return findMedianOfArrayAndValue(X, Y[0]);
		}		
		else if(X.length == 1 && Y.length == 1) {
			var testArray = [ X[0], Y[0] ];
			return findMedianIndex(testArray);
		}
		else if(X.length < 1) {
			var testArray = Y.concat(X);
			return findMedianIndex(testArray);	
		}
		else if( Y.length < 1) { var testArray = X.concat(Y); return findMedianIndex(testArray); } if( X.length === 2 && Y.length >= 2 && Y.length % 2 === 0) {
			if(isFirstArrayInTheMiddleOfSecondEvenArray(X, Y)) {
				/*
					in example:
					var X = [1, 2];
					var Y = [-1, 3];
				*/
				return (X[0] + X[1]) / 2;
			}
		}
		if( Y.length === 2 && X.length >= 2 && X.length % 2 === 0) {
			if(isFirstArrayInTheMiddleOfSecondEvenArray(Y, X)) {
				/* and the other way */
				return (Y[0] + Y[1]) / 2;
			}
		}	

		var mX = findMedianIndex(X);
		var mY = findMedianIndex(Y);
		
		if(mX == mY) {
			return mX;
		}
			

		var splicePart = Math.floor(Math.min(X.length / 2 - 1, Y.length / 2 - 1));
		splicePart = splicePart < 1 ? 1 : splicePart;

		if (mX < mY) {
			X.splice(0, splicePart);
			Y.splice(Y.length - splicePart);
		} else {
			X.splice(X.length - splicePart);
			Y.splice(0, splicePart);
		}
	

		return findMedian(X, Y);
	}

	return findMedian(nums1, nums2);
}

[tabbyending]

Linked Lists

Linked list

Singly-linked list example implementation in Java Script

Let’s store these values from the diagram above in linked list.

Using Object:

const list = {
    head: {
        value: 20,
        next: {
            value: 18,
            next: {
                value: 15,
                next: {
                    value: 67,
                    next: null
                }
            }
        }
    }
};


/* example of retrieving the value at second position */

console.log(list.head.next); // the result should be 18

 

Using class

function ListNode(val) {
    this.val = val;
    this.next = null;
}

var linkList = new ListNode(20);
linkList.next = new ListNode(18);
linkList.next.next = new ListNode(15);
linkList.next.next.next = new ListNode(67);
linkList.next.next.next.next = null;

/* example of retrieving the last value */
console.log(linkList.next.next.next); // the result should be 67

 

Traversing double linked list example

var list  = {
  one: {value: 1, prev: null, next: null},
  two: {value: 2, prev: null, next: null},
  three: {value: 3, prev: null, next: null}
}

var root = list.one;

list.one.next = list.two;
list.two.prev = list.one;
list.two.next = list.three;
list.three.prev = list.two;

var i = 0;

var element = root;
var loop = true;

while(loop == true) {
  console.log(">>", element.value);
  if(element.next == null)
      loop = false;
  element = element.next;
}

 

Two Sum

[tabby title=”Task”]

Task:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Examples :

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

This problem was taken from Leetcode

[tabby title=”Solution”]

One pass hash table.

Let’s take this example: nums = [2, 7, 11, 15], target = 9
The brute force solution might be to loop through each value in the nums array, and to add with each other value in the array and to compare with the target result.

Optimize the algorithm using hash table

The idea is to store the array into a hash table where keys will be the values of the array (i.e: 2, 7, 11, 15) and the values: the element index (i.e: 0,1,2,3)
key:value
2 : 0,
7 : 1,
11:2,
15:3

Two sum diagram

This way all we need to do is:
– Iterate through the array once.
– For each value calculate what number could add to the target value ( x = target – nums[i])
– Dynamically add the next key-value pair into the hashmap

The solution:

Java Script

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    var hashMap = {};


    for(var i in nums) {                
            
        var test = target - nums[i];
        if(hashMap[test]) {
            return [parseInt(hashMap[test]), parseInt(i)];
        }     
        var key = nums[i];
        hashMap[key] = i;   
    }
};

C++

class Solution {
    
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        
        vector<int> result;

        std::map<int, int> buffer;
        for(int i = 0;i < nums.size(); i++) {
            int key = nums[i];
            int test = target - nums[i];

            if(buffer[test] != 0) {
                result = {buffer[test] -1, i};
                break;
            }
            buffer[key] = i + 1;
        }
        return result;
    }
};

 

[tabbyending]

Running GraphQL with Express sever

Create the project with all dependencies.

Create new node project -y will answer yes to all questions during setting up the project to save us some time.

yarn init -y

add the libraries:

yarn add graphql express express-graphql

what we just did:
– created new npm project (-y answering yes on all questions)
– installed graphQL (–save locally in project’s node_modules folder)
– installed Expless server with Express-graphQL module locally

Create server.js

Create ./server.js in the root folder with the following contents:

var express = require('express');
var { graphqlHTTP } = require('express-graphql');
var { buildSchema } = require('graphql');

// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
  type Query {
    hello: String
  }
`);
// The root provides a resolver function for each API endpoint

var root = {
  hello: () => {
    return 'Hello world!';
  },
};

var app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));

app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

what we just did:
– we created a schema (lines 6-10)
– created a resolver function for our schema (lines 13-17)
– created Express http server (line 19)
– tell Express http server to use graphQL library (lines 20-24)

Now everything is almost ready. Let’s create a start script and run our server.

Create the start script.

Open ./package.json and add the highlighted lines:

{
  "name": "graphql-test",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "node server.js"
  },  
  "dependencies": {
    "express": "^4.16.4",
    "express-graphql": "^0.7.1",
    "graphql": "^14.0.2"
  }
}

Run GraphQL server.

Open terminal, navigate to the root folder and execute the start script:

yarn start

Since we configured graphqlHTTP with graphiql: true (line 23) we can access GraphQL UI tool and execute queries.
Navigate the browser to http://localhost:4000/graphql and run { hello } and the result should look like the one in the image below:

Congratulations! You just ran your first GraphQL server!

Server Side Rendering

branch-name:  
Click To Copy

The motivation: why doing it?

Now we have a production build which works great, but when search engines look at your site they will see this:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Babel Webpack Boilerplate</title>
        <link rel="stylesheet" type="text/css" href="dist/main.css">
    </head>
    <body>
        <div id="root"></div>
        <script type="text/javascript" src="dist/main-bundle.js"></script>
    </body>
</html>

and will have no idea what this site is about and how to index it.

Good search engines like Google will render the JS and will get the right idea but there are also other benefits of having SSR.

Benefits:
Quicker page render. Once the source code is there, with all assets (CSS, JS, Images, fonts, etc) is sent to the browser, it could start render it and it could show the site like it was already ready for use (although the events won’t be attached at this point and the site won’t be really ‘clickable’ the user will at least see a fully rendered site which gives him the perception of a fully ready site)
SCO benefits
Pages could be pre-rendered on the server and cached.

So. let’s dive into this:

SSR Checklist

This might be more complicated task that we would expect so let’s outline a checklist of what have to be done to achieve SSR:

  • Fetch all GraphQL queries on the backend before start generating the source code.
    Some components rely on having their data fetched from GraphQL before render.
    For example, If you remember we dynamically fetch the page components layout from GraphQL so in order to serve the page source, we have to make the page query and return the list of all components for the current route. For example the ‘/home’ page should hare 2 components: header and Home components.
  • Make sure that bundle splitting will continue work.
  • Fetch only assets that are necessary to be served for each particular route.
    We have to pre-render the page, and figure out which JS and CSS bundles should be added in the source code. For example for the home page ‘/home’ we have to include:
    <script src=’dist/header.js’/></script>
    <link href=”/dist/header.css”/><script src=’dist/home.js’/></script>
    <link href=”/dist/home.css”/>
  • Finally fetch the source code string and send it to the browser.

 

Adding server side rendering  (SSR).

Let’s start with adding the server side rendering script, and the script that will run it.

Adding ssr build script

./package.json

"scripts": {
  "start-cli": "webpack-dev-server --hot --history-api-fallback --config webpack.cli.config.js",
  "start-api": "babel-node server-api.js",
  "start-middleware": "babel-node server-middleware.js",
  "clean": "rm -rf ./dist ./server-build",
  "lint": "eslint .",
  "build-dev": "webpack --mode development",
  "build-prod": "webpack --config webpack.prod.config.js",
  "build-ssr": "webpack --config webpack.server.config.js",
  "run-server": "node ./server-build/server-bundle.js",
  "start": "yarn clean; yarn build-prod; yarn build-ssr; yarn run-server"
},

what we just did:
– (line 9) adding build-ssr script to bundle the express server into a executable JS
– (line 10) adding start script to run the express server which will:
1. send the page source to the browser.
2. serve the static production bundle.
3. serve all other assets (images, fonts, etc)
– remove run-prod-server  and build-and-run-prod-server  since we are replaced them with the new scripts.
– add `server-build` to the cleaning script since this is the location where the server bundle will be dumped.

Creating SSR config

Since most of the configuration will be similar to the production config, let’s start by copying the production config into a new file called ./webpack.ssr.config.js and do some adjustments specific for SSR.

./webpack.ssr.config.js

const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
let config = require('./webpack.base.config.js');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

config.mode = "production";
config.devtool = "";
config.target = "node";
config.externals = [nodeExternals()];

config.entry = {
    server: './ssr-server.js'
  }

config.output = {
    filename: '[name]-bundle.js',
    path: path.resolve(__dirname, 'server-build')    
}

config.module.rules[1].use[0] = MiniCssExtractPlugin.loader;
config.plugins = [ 
    ... config.plugins,
    ... [
        new MiniCssExtractPlugin({
            filename: "[name].css"
        }), 
        new OptimizeCSSAssetsPlugin({}),  
        // on the server we still need one bundle
        new webpack.optimize.LimitChunkCountPlugin({
            maxChunks: 1
        })
    ]
];
module.exports = config;

what we just did:
– (line 10) we told Webpack that this bundle will not run in the browser but in the ‘node’ environment.
– (line 11) we aded Webpack Node Externals which will remove the node_modules which we don’t need when execute in ‘node’ environment.
– (line 14) specifies a new entry point for the SSR, that will run the express server (similar to the one that we built for production)
– (line 31) on the server side we still need one bundle file.

Now let’s add the missing module:

yarn add webpack-node-externals --dev

Adding Express server

This is going to be the same Express server that we added in the previous chapter to serve the  production requests, but settings will be much more complex to fit the SSR needs. Create ssr-server.js with the following content:

./ssr-server.js

import React from 'react';
import express from 'express';
import App from './src/components/App/ssr-index';
import Loadable from 'react-loadable';
import manifest from './dist/loadable-manifest.json';
import { getDataFromTree } from "react-apollo";
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { renderToStringWithData } from "react-apollo"
import { createHttpLink } from 'apollo-link-http';
import { getBundles } from 'react-loadable/webpack';

const PORT = process.env.PROD_SERVER_PORT;
const app = express();

app.use('/server-build', express.static('./server-build'));
app.use('/dist', express.static('dist')); // to serve frontent prod static files
app.use('/favicon.ico', express.static('./src/images/favicon.ico'));

app.get('/*', (req, res) => {
  const GRAPHQL_URL = process.env.GRAPHQL_URL;
  const client = new ApolloClient({
    ssrMode: true,
    link: createHttpLink({
     uri: GRAPHQL_URL,
     fetch: fetch,
     credentials: 'same-origin',
     headers: {
       cookie: req.header('Cookie'),
     },
   }), 
    cache: new InMemoryCache()
  });    

  // Prepare to get list of all modules that have to be loaded for this route
  const modules = [];
  const mainApp = (
    <Loadable.Capture report={moduleName => modules.push(moduleName)}>
      <App req={req} client={client} />
    </Loadable.Capture>    
  );

  // Execute all queries and fetch the results before continue
  getDataFromTree(mainApp).then(() => {        
    // Once we have the data back, this will render the components with the appropriate GraphQL data.
    renderToStringWithData().then( (HTML_content) => {       
      // Extract CSS and JS bundles
      const bundles = getBundles(manifest, modules); 
      const cssBundles = bundles.filter(bundle => bundle && bundle.file.split('.').pop() === 'css');
      const jsBundles = bundles.filter(bundle => bundle && bundle.file.split('.').pop() === 'js');
    
      res.status(200);
      res.send(`<!doctype html>
      <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Server Side Rendering and Bundle Splitting</title>
        <link
        href="/dist/main.css"
        rel="stylesheet"
        as="style"
        media="screen, projection"
        type="text/css"
        charSet="UTF-8"
      />
        <!-- Page specific CSS bundle chunks -->      
        ${
          cssBundles.map( (bundle) => (`
            <link
              href="${bundle.publicPath}"
              rel="stylesheet"
              as="style"
              media="screen, projection"
              type="text/css"
              charSet="UTF-8"
            />`)).join('\n')
        }
        <!-- Page specific JS bundle chunks -->
        ${jsBundles
          .map(({ file }) => `<script src="/dist/${file}"></script>`)
          .join('\n')}
        <!-- =========================== -->
      </head>
      <body cz-shortcut-listen="true">
        <div id="root"/>
          ${HTML_content}
        </div>
        <script>
        window.__APOLLO_STATE__=${JSON.stringify(client.cache.extract())};
        </script>
      
        <script src="/dist/main-bundle.js"></script>
      </body>
    </html>`);
      res.end(); 
    });    

  }).catch( (error) => {
    console.log("ERROR !!!!", error);
  });
});

Loadable.preloadAll().then(() => {
  app.listen(PORT, () => {
    console.log(`? Server is listening on port ${PORT}`);
  });
});

what we just did:
Clearly a lot of stuff so let’s take one step at a time.
– We created express server similar to the one we did for production in the previous chapter.
  – (line 13,14) we created the server, telling it to run on the port that we will specify in the .env file later in this tutorial.
– (line 20) we are telling Express to listen to any request pattern, except for these above described in app.use 
– (line 53) sending the html to the browser.
– We fetch the data for all queries in the app before continue.
  – (lines 21-33) We created the Apollo client.
– (line 44) calling getDataFromTree from react-apollo will walk through the React tree to find any components that make GraphQL requests. It will execute the queries and return a promise.
– (line 46) calling renderToStringWithData  will render the app with the necessary GraphQL data.
– (line 90) since we already fetched all data,  we are going to stringify it, and attach it to the window object so it could be re-used on the client side.  this part is very important. It does so called Store hydration. If we miss it we will end up making twice more queries to GraphQL: one set on the server side, and one set on the client side.
– We get a list of all components that will have to render for the particular route, and create appropriate SCRIPT and LINK tags to load JS and CSS only for these components.
  – (line 38) we use Loadable.Capture from react-loadable to create an array of all components that exist in the particular route and store it in modules = []
– (lines 48 – 50) Will extract the CSS and JS bundle file lists
– ( lines 67 – 83) will traverse CSS and JS arrays and will create the appropriate CSS and JS tags to include the components that are about to render in this particular route.
– (line 104) is going to pre-load all the components before continue so we won’t see just the “loading” component.

One very important step that could be easily forgotten is to add react-loadable/babel plug-in into .babelrc otherwise we are going to wonder why the assets list is never created.

./.babelrc

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-syntax-dynamic-import",
    "react-loadable/babel"
  ]  
}

Also let’s add PROD_SERVER_PORT to our .env file

./env

APP_NAME=Webpack React Tutorial
GRAPHQL_URL=http://localhost:4001/graphql
PROD_SERVER_PORT=3006

and make it available on the front end so Express could pick it up.

./getEnvironmentConstants.js

const fs = require('fs');
// Load environment variables from these files
const dotenvFiles = [
  '.env'
];
// expose environment variables to the frontend
const frontendConstants = [
  'APP_NAME',
  'GRAPHQL_URL',
  'PROD_SERVER_PORT'
];
function getEnvironmentConstants() {
  
  dotenvFiles.forEach(dotenvFile => {
    if (fs.existsSync(dotenvFile)) {
      require('dotenv-expand')(
        require('dotenv').config({
          path: dotenvFile,
        })
      );
    }
  });
  
  const arrayToObject = (array) =>
  array.reduce((obj, item, key) => {
    obj[item] = JSON.stringify(process.env[item]);
    return obj
  }, {})
  return arrayToObject(frontendConstants);      
}
module.exports = getEnvironmentConstants;

 

Adding ./components/App/ssr-index.js

Now if we look again at ./ssr-server.js (line 3) we will see that we are not pointing the App module to the index.js but to ssr-index.js instead, and the reason for this is that on the server things will be slightly different.
First we can’t use BrowswerRouter because clearly there is no browser in a node environment. We have to use StaticRouter instead.
Second, we don’t need to instantiate a new ApolloClient since we already did this in ./ssr-server.js file above. We are just going to pass it as a client param.

With that being said, let’s go ahead and create:

./components/App/ssr-index.js

import React from 'react';
import PageLayout from '../../containers/PageLayout';
import { StaticRouter,  Route, Switch } from 'react-router-dom';
import { ApolloProvider } from 'react-apollo';
import { Provider } from 'react-redux';
import { createStore} from 'redux';
import reducers from '../../reducers';
import fetch from 'isomorphic-fetch';

import styles from './styles.scss';

const store = createStore(reducers, {});

export default ( {req, client} ) => {

  const context = {};
  return (
    <div className={styles.appWrapper}>
      <Provider store={store}>   
        <ApolloProvider client={client}>            
          <StaticRouter location={ req.url } context={context}>
            <Switch>
              <Route exact path="*" component={PageLayout} />  
            </Switch>            
          </StaticRouter>
        </ApolloProvider>
      </Provider>
    </div>
  );
}

DOM Hydration

We did almost all necessary steps for SSR but if we run the app we will see that we are not taking the advantage of the server side rendering and the app will reload on the client side.

We still need to do two important things: DOM hydration and Store rehydration.

DOM hydration is going to attach the events to the existing HTML layout returned from the SSR. Adding it is pretty straight forward: we have to replace ReactDOM.render with ReactDOM.hydrate.

./src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
ReactDOM.hydrate(<App/>, document.getElementById('root'));
if (module.hot) {
  module.hot.accept();
}

Apollo store rehydration

This is also pretty straight forward. If we go back in this tutorial we could haver recall that when we create ./ssr-server.js we fetched the data for all queries, and attached it to the window object (line 90)
Now, let’s use this:
./src/components/App/index.js

import React from 'react';
import PageLayout from '../../containers/PageLayout';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore} from 'redux';
import reducers from '../../reducers';

const styles = require('./styles.scss');

const store = createStore(reducers, {});

export default ( {req} ) => {
  const GRAPHQL_URL = process.env.GRAPHQL_URL;
  const client = new ApolloClient({
    link: new HttpLink({ uri:  GRAPHQL_URL }),
    cache: new InMemoryCache().restore(window.__APOLLO_STATE__),
  }); 
  
  return (
    <div className={styles.appWrapper}>
      <Provider store={store}>
        <ApolloProvider client={client}>
          <Router>
            <Switch>
            <Route exact path="*" component={PageLayout} />  
            </Switch>
          </Router>
        </ApolloProvider>
      </Provider>
    </div>        
  );
}

Ready for a test flight

Finally we are ready to test flight this beast. yarn start and navigate the browser there localhost:3006/home

The response time should be mind blowing with all prod settings and SSR in place.
Open the source code and you should see something like this:

<!doctype html>
      <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Server Side Rendering and Bundle Splitting</title>
        <link
        href="/dist/main.css"
        rel="stylesheet"
        as="style"
        media="screen, projection"
        type="text/css"
        charSet="UTF-8"
      />
        <!-- Page specific CSS bundle chunks -->      
        
            <link
              href="/dist/2.css"
              rel="stylesheet"
              as="style"
              media="screen, projection"
              type="text/css"
              charSet="UTF-8"
            />

            <link
              href="/dist/3.css"
              rel="stylesheet"
              as="style"
              media="screen, projection"
              type="text/css"
              charSet="UTF-8"
            />
        <!-- Page specific JS bundle chunks -->
        <script src="/dist/2-bundle.js"></script>
<script src="/dist/3-bundle.js"></script>
        <!-- =========================== -->
      </head>
      <body cz-shortcut-listen="true">
        <div id="root"/>
          <div class="App-appWrapper" data-reactroot=""><div><div><div class="Header-wrapper"><h2> <!-- -->Webpack React Tutorial<!-- --> </h2><ul><li><a href="/home">HOME</a></li><li><a href="/greetings">GREETINGS</a></li><li><a href="/dogs-catalog">DOGS CATALOG</a></li><li><a href="/about">ABOUT</a></li></ul></div></div><div><div class="Home-wrapper">This is my home section!</div></div></div></div>
        </div>
        <script>
        window.__APOLLO_STATE__={"Page:home":{"id":"home","url":"/home","layout":[{"type":"id","generated":true,"id":"Page:home.layout.0","typename":"PageLayout"},{"type":"id","generated":true,"id":"Page:home.layout.1","typename":"PageLayout"}],"__typename":"Page"},"Page:home.layout.0":{"span":"12","components":[{"type":"id","generated":true,"id":"Page:home.layout.0.components.0","typename":"PageComponents"}],"__typename":"PageLayout"},"Page:home.layout.0.components.0":{"name":"Header","__typename":"PageComponents"},"Page:home.layout.1":{"span":"12","components":[{"type":"id","generated":true,"id":"Page:home.layout.1.components.0","typename":"PageComponents"}],"__typename":"PageLayout"},"Page:home.layout.1.components.0":{"name":"Home","__typename":"PageComponents"},"ROOT_QUERY":{"getPageByUrl({\"url\":\"/home\"})":{"type":"id","generated":false,"id":"Page:home","typename":"Page"}}};
        </script>
      
        <script src="/dist/main-bundle.js"></script>
      </body>
    </html>

As you might notice, (lines 15-37) that only the bundles for the components that we need there are loaded.

Also you will see the Apollo client (The GraphQL queries) serialized and attached to the window object (line 44) which we are using to re-hydrate the client and avoid another GraphQL call.

Well, this could have been as far as a production ready stack, but two important things are missing: tests and caching. But this is good enough as an end of this chapter.

 

branch-name:  
Click To Copy

 

Longest Substring Without Repeating Characters

[tabby title=”Task”]

Task:
Given a string, find the length of the longest substring without repeating characters.

Example 1:

Input: "abcabcbb"
Output: 3
Explanation: The answer is "abc", which the length is 3.

 

Example 2:

Input: "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.

 

Example 3:

Input: "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

 

This problem was taken from Leetcode

[tabby title=”Solution”]

The sliding window solution.

The sliding window represents a range of elements like an array,  defined by the start and end indices. Then we start moving “sliding” the indices to the next checked subset.

Let’s say that we have this string: “ABCDECFGH”.
The answer of the problem is “DECFGH“.

The obvious resolution could be to grab each letter of the string (starting with the first one), compare it with a buffer of the letters that we already checked and if there is match, continue with the next letter and keep track of the length of the longest string sequence without repeated characters.

In the current example “ABCDECFGHlet’s name is S we start with starting index i  = 1, and end index j = 1. so S[i, j)

Then we start “sliding” j adding more elements in the range and comparing next element added in the range with all previous elements till we find match. I our example this will happen on i = 1 and j = 6 since 6th letter is C and C is already in the range at position 3. Let’s call it j1 = 3.

Then the next step will be to “slide” i and start all over. i = i + 1.

But we could improve this algorithm in two ways:

  1. Store the characters in the buffer into an associative array or so called hashmap where the key will be the character, and the value the index. Example:
    { key = A, val = 1 }
    { key = B, val = 2 }

    This way we don’t have to iterate through each element into the buffer when checking if the character is there or not.
  2. When we find match, we don’t have to start from the next character but we can “slide” to the value of the character in the buffer that matched + 1 since we already know, that the sequence before has shorter length.
    In the range [i, j1] which in the current example is [1, 3] we know for sure that there will be no substring longer than the current one [i, j] so we don’t have to “slide” i = i + 1 but we could do i = j1 + 1 which will save checking of [i+1, j1]  (B and C)
    Example:
    start checking ABCDE and then C is already found in the buffer at index = 3. So instead of starting from index 2 – B we could start from index 3 + 1 which is D

Longest Substring Without Repeating Characters solution diagram

The solutions:

Java Script

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {


    var hashMap = {};    

    function findUniqueSequence(left) {
        var buffer = [];
        
        for(var c = left;c < s.length; c ++) {            
            if(buffer.includes(s[c])) {
                var symbol = s[c];
                var nextPos = hashMap[symbol] + 1;
                return [buffer, nextPos ];
            }
            hashMap[s[c]] = c;
            buffer.push(s[c]);
        }
        return [buffer, s.length];
    }


    var left = 0; 
    var longest = [];       
    while(left < s.length) {
        var result = findUniqueSequence(left);
        left = result[1];
        longest = longest.length < result[0].length ? result[0] : longest;
        if(s.length - left < longest)
            break;
    }
    return longest.length;    
};

 

C++

class Solution {
    
public:
    int lengthOfLongestSubstring(string s) {
        unsigned long n = s.length();
        char char_array[n+1];
        strcpy(char_array, s.c_str());
        
        int left = 0;
        unsigned long arr_length = sizeof(char_array) - 1;
        int longestStringSize = 0;
        int sizeCounter = 0;
        
        std::map<char, int> buffer;
        int co = 0;
        while (left < arr_length) {
            char cursor = char_array[left];
            
            if(buffer[cursor] == 0) {
                buffer[cursor] = left + 1;
                sizeCounter ++;
                left ++;
            }
            else {
                left = buffer[cursor];
                buffer.clear();
                longestStringSize = sizeCounter > longestStringSize ? sizeCounter : longestStringSize;
                sizeCounter = 0;
            }
        }
        longestStringSize = (longestStringSize == 0 || sizeCounter > longestStringSize) ? sizeCounter : longestStringSize;
        return longestStringSize;
    }
};

 

[tabbyending]

Adding Config file and Babel loader

branch-name:  
Click To Copy

Why adding Babel ?

Java Script is a language that evolves really fast, but unfortunately many people still use old browsers that doesn’t support the latest Java Script syntax.

Let’s say that we would like to be at the edge of the technology and use the latest ECMA Script 6 or (European Computer Manufacturers Association) or EC for shorter and we want to use the latest one EC6. The solution will be to `transpile` the new EC6 to older version, that is supported even with the old browsers.

But Webpack is just JS bundle creator and it doesn’t know how to translate the new EC6 syntax to the old one and here comes the Webpack loaders.

Webpack loaders simply pre-process the files which Webpack is going to bundle.

There are tons of loaders, you could check them here https://webpack.js.org/loaders/ but the one that we need for this particular task is called Babel

There are two ways of adding loaders: with a config file or from the package.json. Since doing it through a config file is more flexible we will explore this option.

Adding Babel loader and Webpack config file.

In order to use Babel loader, we have to tell Webpack about it.

Adding Webpack config file.

 Just creating webpack.config.js in the root folder of our project is enough to create Webpack config file, which Webpack will pick up automatically from this location.

But for our particular task we need to add Babel loader to our project. So first before we move any further, let’s do this:

Babel got split in a couple of sub-modules that we need to add:

  • babel-core
  • babel-loader
  • babel-preset-env – for compiling Javascript ES6 code down to ES5

yarn add @babel/core babel-loader @babel/preset-env

– create new file called webpack.config.js in the root of the project folder with these contents:

./webpack.config.js

module.exports = {
  mode: 'development',
  entry: [
    './src/app.js'
  ],
  output: {
    filename: '[name]-bundle.js',
  },  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};

What we just did:
– we created a brand new file called webpack.config.js in the root folder of our project and Webpack will look there and pick it up.
– we added mode: “development” insted of passing it through the build script (line 2)
– added entry point to to tell webpack where to look for the bundle files.
we could have skip this parameter if we name our bundle entry point was named ./src/index.js and Webpack was going to pick it automatically, but for the purpose of understanding Webpack config we customized it with ./src/app.js
– added output parameter to specify Where webpack should create the bundle.
By default if this parameter is not specified Webpack will create ./dist/main.js bundle.
– we added a Babel module config, that is going to transpile all JS files matching this RegExp pattern /\.js$/ and make them old fashioned JS that all browsers understand.
– we told Webpack to exclude node_modules “exclude: /node_modules/,” (line 13) since this folder is where npm installs packages and it doesn’t need to be transpiled.

Now we have to tell Babel how to transpile our files to JavaScript. Create .babelrc in the root of the project, with these contents:

./.babelrc

{
  "presets": [
    "@babel/preset-env"
  ]
}

Presets are like plug-ins. They describe which ES features we are going to use so Babel could transpile back to ES5 that all browsers will understand. In our case we added @babel/preset-env so we could use latest ES coding, and since we are going to add React we added @babel/preset-react which under the hood includes:

so Babel will know how to deal with react-jsx files.

Let’s give it a try:

yarn build-dev

and check the ./dist folder. Looks good right ?

But if you try to run in on old IE you will notice that the site break. We still need babel-polyfill to  emulate a full ES2015+ environment (no < Stage 4 proposals). This woks on the front end only.

Adding Babel-polyfill.

Let’s install the library first: yarn add @babel/polyfill

There are two different ways to add it: we could use require(“@babel/polyfill”); or import “@babel/polyfill”; or we could use Webpack to add it.

Let’s use Webpack:

./webpack.config.js

module.exports = {
  mode: 'development',
  entry: [
    '@babel/polyfill',
    './src/app.js'
  ],
  output: {
    filename: '[name]-bundle.js',
  },  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};

Now we added support for all old browsers.

Adding another pre-processor: Eslint.

Now we know how to add Webpack modules, let’s add another one, that will help us identify problems with our code even before we compile it.

yarn add eslint eslint-loader babel-eslint eslint-plugin-react --dev

what we just did:
– we installed:
eslint – the core JS linter
eslint-loader – tells webpack that we are going to use eslint in our build
babel-eslint – provides linting for ES6 code.
eslint-plugin-react – Since we are going to use React, we have to install the React plugin for
eslint.

The easiest way to configure eslint is through .eslintrc config file. Let’s create one in the root folder with these contents:

./.eslintrc

{
  "parser": "babel-eslint",
  "plugins": [
    "react"
  ],
  "rules": {
    "no-undef": 1
  }
}

what we just did:
– we set up the parser to be babel-eslint (line 1)
– we added react plugin
– we added one rule “no-undef” with a value ‘1’. The possible values are: 0 = off, 1 = warn, 2 = error

Now in the root folder execute:
./node_modules/eslint/bin/eslint.js .

the output probably will be similar to this:

~/babel-webpack-react-boilerplate/dist/main-bundle.js
45:48 warning ‘Symbol’ is not defined no-undef
46:44 warning ‘Symbol’ is not defined no-undef

~/babel-webpack-react-boilerplate/src/test.js
3:3 warning ‘console’ is not defined no-undef

✖ 3 problems (0 errors, 3 warnings)

….

Let’s add a script to execute linter through npm.

"scripts": {
  "clean": "rm -rf ./dist",
  "lint": "eslint .",
  "build-dev": "webpack --mode development ./src/app.js -o ./dist/main-bundle.js",
  "build-prod": "webpack --mode production ./src/app.js -o ./dist/main-bundle.js"
},

And give it a try:
yarn lint

 NPM always looks first in project’s node_modules folder, so if you have eslint (or any other module) installed globally and locally, it will always pick the local version.

Adding .eslintignore
And we could add the files that we don’t want to be checked into
./.eslintignore

dist
webpack.config.js

Let’s run the linter yarn lint

/Users/toninichev/Downloads/webpack-test/src/test.js
2:3 warning ‘console’ is not defined no-undef

✖ 1 problem (0 errors, 1 warning)

✨ Done in 1.38s.

Let’s say that we don’t want to `fix` this since this is not a real problem, but we want to tell Eslint to ignore it we could add /* eslint-disable no-undef */

./src/test.js

/* eslint-disable no-undef */

const test = (msg) => {
  console.log(msg);
}
export default test;

Run Eslint again and you sould not see any issues.

branch-name:  
Click To Copy

 

Webpack – Zero Config

branch-name:  
Click To Copy

Development vs Production modes.

When you ran the code from the previous tutorial you might notice this message:
WARNING in configuration
The ‘mode’ option has not been set. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for this environment.

Webpack 4 is coming with two predefined modes: “development” and “production” and automatically optimize the code for the corresponding build.
To enable the corresponding mode we simply need to call Webpack with –mode development or –mode production parameter.
Let’s go to package.json and add ‘build’ script in so we could call it with npm without passing all parameters:

./package.json

  "scripts": {
    "build": "rm -rf ./dist && webpack --mode development ./src/app.js -o ./dist/main-bundle.js"
  }

Let’s execute the script that we just added and create a new bundle.

yarn build

But why stopping there? We could configure Webpack with no parameters at all. Just passing –mode.

Setting Webpack with Zero config

Webpack by default will look for entry point in ./src/index.js and will output the bundle in ./dist/main.js

Change the script section in package.json as follows.

"scripts": {
  "clean": "rm -rf ./dist",
  "build": "webpack --mode development"
},

what we just did:
– we added a clean up script (line 2) that will take care of deleting the old bundle once executed.
– we added ‘build’ script, with almost 0 config parameters.

Now if we rename src/app.js to src/index.js Webpack will pick it ot automatically and will output the bundle code in dist/main.js

We also removed ‘rm -rf’ and added it into another script called clean.
Now in the console in the root of the project if we do:

yarn run clean
yarn run v1.3.2
$ rm -rf ./dist
✨ Done in 0.11s.
$ yarn run build
yarn run v1.3.2
$ rm -rf ./dist && webpack –mode development ./src/app.js -o ./dist/main-bundle.js
Hash: 3e7c9702fe472d8df3d2
Version: webpack 4.16.3
Time: 98ms
Built at: 08/01/2018 4:10:30 PM
Asset Size Chunks Chunk Names
main-bundle.js 4.48 KiB main [emitted] main
Entrypoint main = main-bundle.js
[./src/app.js] 55 bytes {main} [built]
[./src/test.js] 69 bytes {main} [built]
✨ Done in 1.06s.

We well see that Webpack prepared a bundle for us with Zero Config parameters.

Setting Webpack override

The Zero Config was great but if the complexity of the project grows, we will need to customize Webpack so let’s revert the changes back.
– Rename src/index.js back to src/app.js
– Change the scripts parameters in package.json to look like this:

"scripts": {
  "clean": "rm -rf ./dist",
  "build-dev": "webpack --mode development ./src/app.js -o ./dist/main-bundle.js",
  "build-prod": "webpack --mode production ./src/app.js -o ./dist/main-bundle.js"
},

What we just did:
– We added back “clean” script to remove ./dist folder.
– We added “build-dev” and “build-prod” build scripts.

Let’s give it a try. From the terminal run:

yarn clean

This will get rid of the ./dist folder.

yarn build-dev

and look at ./dist/main-bundle.js Nothing unusual there. The main-bundle.js was re-created.

Let’s check the production build script. Run:

yarn build-prod

now it gets interesting, if you look at ./dist/main-bundle.js you will notice that it is minified and prepared for production use. Webpack also uses UglifyJSPlugin when “–mode” is set up to “production”.

Now you have production build ready!

 What is far from true is that this is really production ready but we will figure out this later in this tutorial.

branch-name:  
Click To Copy