Zero to web with TypeScript, webpack and more

This post is a little long but the aim is to cover creating a simple TypeScript/HTML website without using any frameworks but using a whole bunch of standard tools.

The website will be pathetically unimpressive because I want to solely concentrate on just getting the various technologies working together.

We’ll be using Visual Code as the editor. We’ll be using TypeScript, ESLint and Jest (as per previous posts on this topics) and we’ll be using webpack to both create a distribution of our code and to host our code.

Creating the basics

  • Create the project’s folder, mine’s zerotoweb
  • Open Visual code against the new folder
  • Use the key combination (on mine it’s CTRL + ‘ on Windows) to open the terminal within Visual Code or if you prefer open a command prompt and navigate to the the folder you created
  • run yarn init –yes within the terminal. This will create the package.json file
  • run tsc –init within the terminal to create the tsconfig.json file
  • Change tsconfig.json to the following contents
    {
      "compilerOptions": {
        "target": "es6",
        "outDir": "./public",
        "rootDir": "./src",
        "allowJs": true,
        "skipLibCheck": true,
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "module": "esnext",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "noImplicitAny": false
      },
      "include": [
        "src"
      ],
      "exclude": [
        "node_modules"
      ]
    }
    
  • Next create the src folder off of the root
  • Add a file named index.ts to src folder and add the following code
    class App {
        getSalutation() {
            return "Hello World"
        } 
    }
    
    let app = new App();
    console.log(app.getSalutation());
    
  • We’ll want to run from node, which currently doesn’t support es6 so we’ll need to add babel, so run
    • yarn add babel-cli -D
    • yarn add babel-preset-es2015 -D
    • yarn add babel-preset-env -D
  • Let’s now add a couple of scripts to package.json
    "scripts": 
    {
       "build": "tsc --watch", 
       "start": "babel-node --presets es2015 ./public/index.js"
    }
    
  • Now we need to add a configuration file for babel, so add a .babelrc file to the root folder and put the following within it
    {
       "presets": ["env"]
    }
    
  • Next we want to add ESLint (this section is a duplication of the previous post but is here for completeness), so run the following
    • yarn add eslint -D
    • yarn add @typescript-eslint/parser -D
    • yarn add @typescript-eslint/eslint-plugin -D
    • Add a .eslintrc.js file to the root folder, place the following into the file
      module.exports = {
          "parser": '@typescript-eslint/parser',
          "plugins": ['@typescript-eslint'],
          "extends": ['plugin:@typescript-eslint/recommended'],
          "rules": {
              "@typescript-eslint/no-parameter-properties": "off",
              "@typescript-eslint/no-explicit-any": "off"
          }
      };
      
    • Now add the following “lint”: “eslint ./src/*.ts” to the scripts section of the package.json
  • Now run yarn build
  • Lets see some output, so run yarn start. If all went well you should see Hello World output.

Adding tests

I’ve covered installing Jest for testing React applications previously but let’s see the steps to take to get everything installed for our non-React world

  • yarn add jest -D
  • yarn add @types/jest -D
  • yarn add ts-jest -D
  • yarn add @types/node -D (not sure about this one)
  • Add __tests__ under the src folder
  • Add TypeScript tests to the __tests__ folder, convention suggests {name}.test.ts for the filenames
  • Add “test”: “jest” to the scripts section of packages.json if you want to run the tests yourself (i.e. no plugin or watch)
  • Finally in the root folder add the file jest.config.js with the following code
    module.exports = {
        roots: ['./src'],
        transform: {
          '^.+\\.tsx?$': 'ts-jest',
        },
        testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
        moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
      }
    

In Visual Code the plugin for Jest (Use Facebok’s Jest With Pleasure) from Orta is very useful to have.

Test coverage

Jest includes an option for running code coverage, simply change your packages.json script to

  • “test”: “jest –coverage”

Adding webpack

Next up we’re going to create our basic web site, extending on what we’ve already covered.

  • Create folder name public off of the root folder
  • Add a file named index.html to public folder, here’s the HTML
    <html>    
        <body>
            <script src="./index.js"></script>
        </body>
    </html>
    
  • Run the following commands
    • yarn add webpack -D
    • yarn add webpack-cli -D
    • yarn add webpack-dev-server -D
    • yarn add babel-loader@7 -D (@7 was required for bable-core)
    • yarn add babel-core -D
  • Create a webpack.config.js in the root folder here’s mine
    var path = require("path");
    module.exports = {
       entry: {
         app: ["./public/index.js"]
       },
      output: {
        path: path.resolve(__dirname, "build"),
        filename: "bundle.js"
      },
      devServer: {
        port: 9000,
        contentBase: "./public"
      },
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /(node_modules)/,
            use: {
              loader: "babel-loader",
              options: {
                presets: ["babel-preset-env"]
              }
            }
          }
        ]
      }
    };
    
  • Now replace the console.log line within the index.ts file with the following
    document.body.innerHTML = app.getSalutation();
    
  • Replace the previously created “start” script with “start”: “webpack-dev-server –open” in packages.json “scripts” section
  • Let’s run yarn start and if all went well you should see a browser window open and display our HTML page with the JavaScript created from our TypeScript file displayed

Time to create bundles

We’re going to use webpack to create some distribution bundles

  • Run the following commands
    • yarn add express -D
    • yarn add webpack-dev-middleware -D
    • yarn add html-webpack-plugin -D
    • yarn add clean-webpack-plugin -D
  • Change the webpack.config.json to
    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    
    module.exports = {
      mode: 'development',
      entry: {
        app: './public/index.js'
      },
      devtool: 'inline-source-map',
      devServer: {
        port: 9000,
        contentBase: './dist'
      },
      plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
          title: 'Demo'
        })
      ],
      output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/'
      }
    };
    
  • Add “bundle”: “webpack” to the scripts section of the packages.json file. This will create a dist folder with, both the HTML file and the JS files we’ve created and generated

Minifying

You’ll notice that the JavaScript file generated in the previous steps is not exactly small, containing comments, whitespace etc. So we’re going to minify it for distribution.

  • Run yarn add babel-minify-webpack-plugin -D
  • Within the webpack.config.js file, comment out devtool: ‘inline-source-map’,
  • Add this line to the top of the file
    const MinifyPlugin = require("babel-minify-webpack-plugin");
    
  • Add new MinifyPlugin to the file, so it looks like this
    plugins: [
       new CleanWebpackPlugin(),
       new HtmlWebpackPlugin({
          title: 'Demo'
       }),
       new MinifyPlugin()
    ],
    
  • Finally, run our script yarn bundle

Trying an alternate minify

Let’s try terser

  • Run yarn add terser-webpack-plugin -D
  • Add this line to the top of webpack.config.js
    const TerserPlugin = require('terser-webpack-plugin');
    
  • Add the following to webpack.config.js
    optimization: {
        minimizer: [new TerserPlugin({
          extractComments: true,
          test: /\.js(\?.*)?$/i,
        })],
      },
    

The optimization will need to be run webpack in production mode, i.e. webpack –mode=production or webpack-dev-server –open –mode=production or in webpack.config.js set mode: ‘production’.

Hence using yarn we can run yarn bundle –mode=production to see the bundle.js has been minified using terse. Obviously you can remove the new MinifyPlugin() also at this point if using TerserPlugin.