Tag Archives: java script

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

 

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

 

Webpack – Getting Started

How to use this tutorial ?

The table of contents of this tutorial is located on the right side

There are two projects that we need:
– the front-end website that we are going to build in this tutorial,
GraphQL server which we are going to use later in “Adding Apollo provider and introduction to making queries to GraphQL“.

There are two GitHub repositories with the complete projects located here:
GitHub:WebpackReactReduxApolloTutorial

And the GraphQL server located here
GitHub:graphql-boilerplate

Instructions of how to set up GraphQL server could be found HERE.

When you go through each chapter you could check out the appropriate branch which contains the chapter specific code, instead of looking at the complete project.
This way will be easier to follow the developing process. Each branch name could be found at the beginning and the end of the tutorial. Look for branch-name: XXXXX

In general it would be better if you follow the tutorial and build the website yourself but if you get stuck somewhere feel free to clone the repository and check out the appropriate branch.

Setting up the project

branch-name:  
Click To Copy

 

mkdir babel-webpack-boilerplate
cd babel-webpack-boilerplate
yarn init -y
yarn add webpack webpack-cli --dev

What we just did:
– line 1: created project’s folder
– line 2: navigate to project’s folder root
– line 3: executing yarn init -y initialized our new project. – y simply means ‘answer yes and use the default init settings’. A package.json is created, which is used to describe the npm project dependencies and executable scripts.
– line 4: we added Webpack as a dev dependency (–save-dev) in our project.

Creating project’s files

Using your favorite text editor create index.html in the root folder of the project, with plain html, and a script tag to include our bundled Java Script code (Line 9).

./index.html

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

Create folder to store the actual bundle files:

mkdir ./src

Create the actual bundle files:

src/app.js

import test from './test';
test('Babel is running!');

src/test.js

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

and dist folder to store compiled bundle:

mkdir ./dist

The project’s layout should look like this:

babel-webpack-react-boilerplate
├── dist
├── index.html
├── node_modules
├── package.json
├── src
|        ├── app.js
|       └── test.js
└─ package.lock

* package.lock is used to describe which versions of node modules are installed, and is create automatically after executing npm install, so don’t worry if it’s not there yet.

Running Webpack and creating Java Script bundle.

Now we have laid out a simple project, let’s create a bundle and run it.
In the terminal in the project’s root folder execute

./node_modules/webpack/bin/webpack.js ./src/app.js -o ./dist/main-bundle.js

What we just did:
– we called Webpack from the command line.
– passed the entry point ./src/app.js to create the bundle.
– and the output file -o ./dist/main-bundle.js

Now if we look at the ./dist folder, we will see that the bundle.js is created.

Testing the project

Open your browser and navigate to the the project’s root.

Look at the console and you will see ‘Babel is running!’.

Congratulations, you just created your first bundle!

branch-name:  
Click To Copy

 

 

where to go from here ?

Add Two Numbers

[tabby title=”Task”]

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
Examples :

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.

This problem was taken from Leetcode

[tabby title=”Solution”]

The solution:

Java Script

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */



function ListNode(val, next) {
  this.val = (val===undefined ? 0 : val)
  this.next = (next===undefined ? null : next)
}


/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var addTwoNumbers = function(l1, l2) {
  
  function doSum(a,b, carry) {
    var result = {
      val: 0,
      carry: 0
    }
    var sum = a + b + carry;
    if(sum > 9) {
      result.val = sum - 10;
      result.carry = 1;
    }
    else {
      result.val = sum;
      result.carry = 0;
    }
    return result;
  }

  var nodeA = l1;
  var nodeB = l2;
  var end = false;
  var carry = 0;
  var result = new ListNode(0, null);
  var resultPointer = result;
  var tmp = null;

  while(nodeA != null || nodeB != null) {
    var valA = nodeA == null ? 0 : nodeA.val;
    var valB = nodeB == null ? 0 : nodeB.val;
    var sumResult = doSum(valA, valB, carry);
    carry = sumResult.carry;
    
    resultPointer.val = sumResult.val;
    resultPointer.next = new ListNode(0, null);
    tmp = resultPointer;
    resultPointer = resultPointer.next;

    nodeA = nodeA ? nodeA.next : null;
    nodeB = nodeB ? nodeB.next : null;
  }

  if(carry != 0) {
    tmp.next.val = carry; // add carry to the val of the last node
  }
  else 
    tmp.next = null; // remove the last empty node.
  return result;
};




var l1 = new ListNode(9, 
          new ListNode(9,
          new ListNode(9,
          new ListNode(9, null)
      ))
);

var l2 = new ListNode(9, 
          new ListNode(9,
          new ListNode(9,
          new ListNode(9,
          new ListNode(9,
          new ListNode(9,                    
          new ListNode(9, null)
      )))))
)

// [9,9,9] , [9,9,9,9,9]
// result: [8,9,9,0,0,1]

var result = addTwoNumbers(l1, l2);

console.log(result);

[tabbyending]

Understanding Java Script Map, Filter and Reduce

 

The array

// The array
var people = [
  { name: 'john', age: 24, weight: 84 },
  { name: 'tim', age: 34, weight: 76 },
  { name: 'Jack', age: 26, weight: 65 },
];

 

MAP

// map - use it if I want to do the same operation for each element in the array
// and return the same amount of items in the array.
// array.map(
// current item of the array,
// curent index,
// the entire array
// );
var result = people.map((person, index, persons) => {
  return `${person.name}: ${person.age}`;
});
console.log('map', result);

 

FILTER

// filter - return only items that match certain criteria.
// array.filter(
// current item of the array,
// curent index,
// the entire array
// )

var result = people.filter((person, index, persons) => {
  return person.age < 30;
});
console.log('filter', result);

 

REDUCE

// reduce - return something completely new after iteration through each element
// in the array
// array.reduce(
// current value of the end value,
// current item,
// curent index,
// the entire array
// )
var initialValue = 10;
var combined_weight = people.reduce((weight, person, index, persons) => {
  return weight += person.weight;
}, initialValue);
console.log('reduce', combined_weight);

Another example of reduce, that will convert the array of objects, to object of objects, where person.name is going to be the key

var reducer = (accumolator, person) => {
  accumolator[person.name] = person;
  return accumolator;
}

var initialValue = {};

// reduce takes reducer function as first parameter, and initial value
var result = people.reduce(reducer, initialValue);

console.log('reduce', result);

 

Climbing stairs

[tabby title=”Task”]

You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Note: Given n will be a positive integer.


Example 1:

Input: 2
Output: 2

Explanation:

 There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps


Example 2:

Input: 3
Output: 3

Explanation:

 There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

 

This problem was taken from Leetcode

[tabby title=”Solution”]

The solution:

Let’s look at the possible scenarios:

[table id=1 /]

If we look at the # of ways to climb, we clearly see that the ways to climb grows in Fibonacci sequence: 1, 2,  3,  5,  8,  13, 21

Then all we need to do is to iterate n times (where n  is the number of stairs) and to find the corresponding Fibonacci number.

Using while loop

  • basically we use temp to store previous Fibonacci number temp = a , initially 1
  • then we calculate the next number a = a + b , initially a = 0 + 1
  • then we set up b = temp which stores the last Fibonacci number in order to be ready for the next iteration. Initially 1.

Looking at the numbers from the first iteration, it is clear that on the second one we will have:
temp = a = 1
a = a + b = 1 + 1 = 2
b = temp = 1

third iteration will produce:
temp = a = 2
a = a + b = 2 + 1 = 3
b = temp = 2
forth iteration:
temp = a = 3
a = a + b = 3 + 2 = 5
b = temp = 3

and so on … a will always return the Fibonacci number for the current n position.

Java Script

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n) {
    if( n < 4) 
        return n;

    fibonacci = function(n) {

        var a = 1, b = 0, temp, num = 1;

        while(num <= n) {
            temp = a;
            a = a + b;
            b = temp;
            num ++;
        }
        return a;
    }

    return fibonacci(n);
};

Using For loop

This is the easiest to comprehend:

  • we create an array to store the Fibonacci numbers var fn = [1,1] with the first two numbers pre-set so the for loop could generate the next one.
  • created for loop that picks the fn[i - 1] and fn[i - 2] numbers and generate the next one.
  • pushed newly calculated Fibonacci number into an array so we could use to generate next one.
  • keep looping till we reached i < n + 1 which is the  answer.

Java Script

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n) {
    if( n  < 3)
    return n;
    
    var fn = [1,1];
    for(var i = 2; i < n + 1; i ++) {
        var nextNumber = fn[i - 1] + fn[i - 2]; 
        fn.push(nextNumber);
    }

    return fn[fn.length - 1];
};

 

Third solution: Using recursive function.

Java Script

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n) {
    if( n < 4) 
        return n;

    fibonacci = function(n) {
        if(n < 2) {
            return n;
        }
        var result = fibonacci(n - 1) + fibonacci(n - 2);        
        return result;
    }

    return fibonacci(n + 1);
};

The idea here is to call fibonacci(n) function to generate the next Fibonacci number.
For n < 2 the function will simply return n 0,1,2.
For n = 3 the function will call itself recursively asking for previous two Fibonacci numbers (2 and 1) and will calculate the next number which is the sum of the previous two, and so on.

Optimize the recursive function to use Memoization.

Memoization is simple technique where we are storing the value of already computed parameter into a HashMap function and quickly retrieve it when we need it.

In our current example since the keys are just a numbers we could store the Fibonacci numbers in a regular array (line 9). Then we simply check if the value already is set up into memo array and return it or call fibonacci function once again.
Then we set up memo array with the next solved Fibonacci number with index n

Java Script

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n) {
    if( n < 4) 
        return n;

    var memo = [];
    fibonacci = function(n) {
        if(n < 2) {
            return n;
        }
        var a = memo[n - 1] ? memo[n - 1] : fibonacci(n - 1);
        var b = memo[n - 2] ? memo[n - 2] : fibonacci(n - 2);

        return memo[n] = a + b;
    }

    return fibonacci(n + 1);
};

 

You might also want to check Fibonacci sequence generator.

[tabbyending]