Setting up WebPack 4 with TypeScript – Part 3.2 – The novice programmer
Tags: webpack, typescript, codesplitting, optimization
Published: 2019-01-01 13:19:11

---
title: Setting up WebPack 4 with TypeScript – Part 3.2 – The novice programmer
published: true
tags: webpack, typescript, codesplitting, optimization
canonical_url: https://www.atyantik.com/setting-up-webpack-with-typescript-part-3-2-the-novice-programmer/
---

As we saw in the previous episode that managing the project with the TypeScript is difficult and understanding modules can be difficult if we are not able to compile it properly. So we say with TypeScript either we can output individually compiled JS files or output everything into a single file with compiler option, module, set to AMD or System

## Target

Let us create a simple configuration that can output server.js and client.js, one for running our server and one for getting included on our client end.

## Webpack 4

Webpack has moved to almost zero configuration with the launch of version 4 but, that would not help us as we would need to handle .ts TypeScript extension with docker.

So let us start by installing <g id="56" data-gr-id="56">webpack</g> and <g id="63" data-gr-id="63">webpack</g>-dev-server and thereafter creating a simple webpack.config.js at our root folder.

```bash
npm i webpack webpack-cli
```

contents of webpack.config.js as below:

```javascript
const path = require('path');

const isProduction = typeof NODE_ENV !== 'undefined' && NODE_ENV === 'production';
const mode = isProduction ? 'production' : 'development';
const devtool = isProduction ? false : 'inline-source-map';
module.exports = [
  {
    entry: './src/client.ts',
    target: 'web',
    mode,
    devtool,
    module: {
      rules: [
        {
          test: /\.tsx?$/,
          use: 'ts-loader',
          exclude: /node_modules/,
          options: {
            compilerOptions: {
              "sourceMap": !isProduction,
            }
          }
        }
      ]
    },
    resolve: {
      extensions: ['.tsx', '.ts', '.js']
    },
    output: {
      filename: 'client.js',
      path: path.join(__dirname, 'dist', 'public')
    }
  },
  {
    entry: './src/server.ts',
    target: 'node',
    mode,
    devtool,
    module: {
      rules: [
        {
          test: /\.tsx?$/,
          use: 'ts-loader',
          exclude: /node_modules/
        }
      ]
    },
    resolve: {
      extensions: ['.tsx', '.ts', '.js']
    },
    output: {
      filename: 'server.js',
      path: path.resolve(__dirname, 'dist')
    },
    node: {
      __dirname: false,
      __filename: false,
    }
  }
];
```

You can get the official reference for setting up TypeScript with WebPack at this link: [https://webpack.js.org/guides/typescript/](https://webpack.js.org/guides/typescript/) as you can see we are using ts-loader for loading files with extension .ts & .tsx. We can also use [awesome-typescript-loader](https://github.com/s-panferov/awesome-typescript-loader) for the same purpose. I prefer to go with [ts-loader](https://github.com/TypeStrong/ts-loader) as mentioned in WebPack docs.

So let us install ts-loader as well:

```bash
npm i ts-loader
```

Also, have a look at the following section in our compiler for server.js

```javascript
node: {
  __dirname: false,
  __filename: false,
}
```

We informed node to provide us with variable \_\_dirname and \_\_filename inside the compiled file.

Let us change the folder structure and naming of the files to make more sense.

- Rename `src/index.ts` to `src/client.ts`
- Let the output folder for the compiled server.ts be `<project-root>/dist` 
- Let the output folder for the compiled client.ts be `<project-root>/dist/public` so that we do not expose our server.js
- Make changes to our server.js accordingly to server static content from `<project-root>/dist/public`
- Update our package.json scripts to compile server & client and watch them for changes and server accordingly.

Update the scripts in package.json to below:

```json
"scripts": {
  "start": "webpack --colors && concurrently -r -n \"Webpack,Nodemon\" \"npm run webpack-watch\" \"npm run server-watch\"",
  "webpack-watch": "webpack --watch --colors",
  "server-watch": "nodemon --watch dist/server.js dist/server.js",
  "test": "echo \"Error: no test specified\" && exit 1"
},
```

What we are doing is: We are first simply compiling with WebPack so we have an Initial server.js and client.js to work with, then we start concurrently WebPack and Nodemon to watch changes over our application and restart <g id="140" data-gr-id="140">accorindly</g>.

This configuration <g id="72" data-gr-id="72">give</g> us our required output as below:

![](https://i2.wp.com/www.atyantik.com/wp-content/uploads/2019/01/image.png?w=640&ssl=1)

Which is beautiful, we Included component/global.ts and output the name inside the global namespace i.e. “Tirth”.

## Code Splitting

The above is good is for novice projects but, In real life, we want to implement code splitting and make our app robust. Let us implement that! After TypeScript 2.4 dynamic import <g id="240" data-gr-id="240">are</g> available but with the inclusion of <g id="215" data-gr-id="215">ESNext</g> as the part of the lib option in compilerOptions.

Updating our tsconfig.json as below:

```json
{
  "compilerOptions": {
    "noEmitOnError": true,
    "target": "es5",
    "lib": ["DOM", "ESNext", "DOM.Iterable", "ScriptHost", "es2015.promise"],
    "outDir": "dist"
  },
  "include": [
    "src/**/*"
  ]
}
```

Also to make our code work we would need to include respective polyfills, for now as let us simply include the polyfill.js from polyfill.io in our HTML inside our server.ts.

> Dynamic import makes use of Promise so we the browser must have support for Promise or a polyfill must be added.

_Contents of server.ts_

```javascript
import express = require("express");
const path = require("path");

const app: express.Application = express();
const port: number = 3000;

app.use("/static", express.static(path.join(__dirname, 'public')));

app.get('/', (req: express.Request, res: express.Response): express.Response => {
  return res.send(`<!DOCTYPE html>
    <html>
      <head>
        <title>My experiments with TypeScript</title>
        <!---- ADDING POLYFILL -->
        <script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
        <style>
          html,body {
            margin:0;
            padding:0;
            font-family: monospace;
            font-size: 20px;
          }
          #logger {
            background-color: #eee; 
            padding: 20px; 
            max-width: 100%; 
            height: 600px; 
            max-height: calc( 90vh - 2em - 35px); 
            margin: 10px;
            overflow-y: auto;
          }
          .log-entry {
            max-width: 100%;
            padding: 5px;
            background-color: #f6f6f6;
          }
          .title {
            margin: 10px;
          }
        </style>
      </head>
      <body>
        <h1 class="title">Logger:</h1>
        <div id="logger"></div>
        <script src="/static/client.js" async></script>
      </body>
    </html>
  `);
});
app.listen(port, (): void => console.log(`Example app listening on port ${port}!`));
```

So basically we want our code split, which is no big of a deal with the WebPack. Webpack and TypeScript do support dynamic import but only when the module option is set to “<g id="57" data-gr-id="57">esnext</g>“. So we will need to update three files in this case:  
  1. tsconfig.json  
  2. webpack.config.js  
  3. server.ts  
  
Add <g id="385" data-gr-id="385">compilerOption</g> module as “<g id="402" data-gr-id="402">esnext</g>“

```json
{
  "compilerOptions": {
    "noEmitOnError": true,
    "target": "es5",
    "module": "esnext",
    "lib": ["DOM", "ESNext", "DOM.Iterable", "ScriptHost", "es2015.promise"],
    "outDir": "dist"
  },
  "include": [
    "src/**/*"
  ]
}
```

In webpack.config.js we would need to inform WebPack that it should optimize with “<g id="88" data-gr-id="88">splitChunks</g>” and update output & entry as below:

```javascript
    entry: {
      client: './src/client.ts'
    },
    output: {
      filename: (chunkData) => {
        // do not change the chunk name as of now as we are importing client.js in server.ts
        return chunkData.chunk.name === 'client' ? '[name].js': '[name].[chunkhash].js';
      },
      // We have the prefix /static/ thus adding public path for generated chunks
      publicPath: '/static/',
      // Chunkhash
      chunkFilename: '[chunkhash].js',
      path: path.join(__dirname, 'dist', 'public')
    },
    optimization: {
      splitChunks: {
        chunks: 'all',
        minSize: 0
      }
    },
```

Other configurations remain as it is.

As we have changed the <g id="50" data-gr-id="50">compilerOption</g> for module to “<g id="63" data-gr-id="63">esnext</g>” our code  
`import express = require` won’t work any more thus we will have to update our code to following:

```javascript
import * as express from 'express';
```

That is all for our code splitting. Let us test it out and update our client.ts to following:

```javascript
let str: string = "Hello, World!";
function log(s: any): void {
  const logger: HTMLElement = document.getElementById("logger");
  const logWrapper: HTMLElement = document.createElement("div");
  logWrapper.className = "log-entry";
  logWrapper.innerHTML = `${(new Date).toLocaleTimeString()}: (${typeof s}):: ${JSON.stringify(s)}`;
  logger.appendChild(logWrapper);
}
log(str);

setTimeout(() => {
  import('./component/global').then(Global => {
    log(Global);
  });
}, 2000);
```

So we are requesting for Global component after 2 seconds, which should load the chunk after 2 seconds and log whatever is loaded.

[![](https://i0.wp.com/www.atyantik.com/wp-content/uploads/2019/01/image-1.png?w=640&ssl=1)](https://i0.wp.com/www.atyantik.com/wp-content/uploads/2019/01/image-1.png?ssl=1)

Which is pretty cool!

PS: The above tutorial is not production ready and should be used for experimenting and learning purpose only!

Reference to previous blog series/episodes:

- [Setting up a <g id="10" data-gr-id="10">project</g> with a directory structure and compiling with TSC](https://dev.to/tirthbodawala/getting-started-with-typescript--part-1--the-novice-programmer-16jh-temp-slug-8330376)
- [Creating an HTTP server with ExpressJS and TypeScript, thus creating a web interface that can help us deliver our compiled file and log our output to the Logger Console.](https://dev.to/tirthbodawala/expressjs-server-with-typescript--part-2--the-novice-programmer-2p98)
- [Modules – understanding imports in TypeScript – Part 3.1 – The novice programmer](https://dev.to/tirthbodawala/modules--understanding-imports-in-typescript--part-31--the-novice-programmer-21e5-temp-slug-6980064)

The code base of this episode, Part 3.2, is available at:  
[https://github.com/Atyantik/typescript-series/tree/part-3.2](https://github.com/Atyantik/typescript-series/tree/part-3.2)

The post [Setting up WebPack 4 with TypeScript – Part 3.2 – The novice programmer](https://www.atyantik.com/setting-up-webpack-with-typescript-part-3-2-the-novice-programmer/) appeared first on [Atyantik Technologies](https://www.atyantik.com).