Brian Koponen

Programming and Tech Tips

Visual Studio Code Template: Webpack, Babel, Express Server and Client

I always find setting up the development environment for any new project fairly tedious, so I try to keep it as similar across all my projects as possible. To that end, I have created templates with all the common tools and settings I like so I can get right into work with as little friction as possible.

This is a Visual Studio Code project template for a client-server app running on Node / Express using a shared codebase. Webpack bundles the client-side code, using Babel to support the required browser versions. It supports using the integrated debugger for both the server and client side.


Download

You can download the template files here:


VS Code Extensions

There are two extensions for Visual Studio Code that I find useful during development.


Using the Template

Whenever I need to start a new project, I simply:

  1. Make a copy of the template directory.
  2. Fill in the project-specific information in package.json and webpack.config.js.
  3. Run npm install.
  4. Run npm outdated.
  5. Run npm update depending on which packages are outdated.

Finally, make sure everything is working as intended. The source files included have the most basic functionality to test that everything works.

Start Webpack by choosing Run Task > Webpack Watch. Make sure to say yes when VS Code asks if the Webpack Watch task is allowed to run when the project opens. If successful, Webpack will create a public/ directory with a bundle.web.js and index.html file.

From the Debug pane, choose "Launch Server and Client" then Start Debugging (F5). If everything is working, Chrome will open and display the simple index.html file in the client directory. Try setting some breakpoints in both index.js and app.js to ensure debugging works for both the client and server.


Template Files

Template Files

Let's take a look at the template files and see what they do.


launch.json

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Server",
            "program": "${workspaceFolder}/src/server/app.js",
            "internalConsoleOptions": "openOnSessionStart"
        },
        {
            "name": "Launch Client",
            "type": "chrome",
            "request": "launch",
            "url": "http://localhost:3000/",
            "internalConsoleOptions": "openOnSessionStart",
            "webRoot": "${workspaceFolder}/"
        }
    ],
    "compounds": [
        {
            "name": "Launch Server and Client",
            "configurations": [
                "Launch Server",
                "Launch Client"
            ]
        }
    ]
}


tasks.json

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Webpack Watch",
            "type": "npm",
            "script": "webpack",
            "problemMatcher": [],
            "runOptions": {"runOn": "folderOpen"}
        }
    ]
}


package.json

{
  "name": "template",
  "version": "1.0.0",
  "description": "Template for Webpack, Babel, Express Server and Client",
  "license": "ISC",
  "author": "",
  "main": "src/server/app.js",
  "scripts": {
    "start": "node src/server/app.js",
    "webpack": "npx webpack"
  },
  "dependencies": {
    "@babel/polyfill": "^7.2.5",
    "@babel/runtime": "^7.3.1",
    "babel-core": "^6.26.3",
    "express": "^4.16.3"
  },
  "devDependencies": {
    "@babel/cli": "^7.0.0",
    "@babel/core": "^7.0.0",
    "@babel/plugin-transform-runtime": "^7.0.0",
    "@babel/preset-env": "^7.0.0",
    "babel-loader": "^8.0.2",
    "bitbar-webpack-progress-plugin": "^1.0.0",
    "css-loader": "^2.1.0",
    "file-loader": "^3.0.1",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.11.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1"
  }
}


webpack.config.js

const path = require('path');

const BitBarWebpackProgressPlugin = require('bitbar-webpack-progress-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const webConfig = {
    target: 'web',
    mode: 'development',
    devtool: 'inline-source-map',
    entry: ['@babel/polyfill', './src/client/index.js'],
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            },
            {
                test: /\.scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader'
                ]
            },
            {
                test: /\.(png|svg|jpg|gif)$/,
                use: [
                    'file-loader'
                ]
            },
            {
                test: /\.html$/,
                use: [
                    'html-loader'
                ]
            }
        ]
    },

    externals: [],

    output: {
        path: path.resolve(__dirname, 'public'),
        filename: 'bundle.web.js'
    },
    plugins: [new BitBarWebpackProgressPlugin(),
              new HtmlWebpackPlugin({ title: 'Template' })
             ],
    watch: true
};

module.exports = [webConfig];


babel.config.js

const presets = [
    ["@babel/preset-env", {
        targets: {
        edge: "17",
        firefox: "60",
        chrome: "67",
        safari: "9"
        },
        useBuiltIns: "usage"
    }]
];

module.exports = { presets };


.gitignore

node_modules
public


Source Code Structure

How you organize your source code is largely dependent on the project and personal preference. In this case, I separate unique files for the client and server into two directories and allow shared files in the src directory.

If you do change this, don't forget to adjust the webpack.config.js, package.json and launch.json files to point to your new entry point locations.


Conclusion

This is a fairly complicated setup since it handles both the client and server in one codebase. I find this works well for the smaller projects I typically work on, but if you are working on a larger project, you probably want to split your codebase into completely separate modules.

I highly recommend creating your own templates. They are especially useful for those types of projects that you don't do that often and can easily forget what you need to install and how it should be configured.

I hope this has been helpful. Let me know if you have any questions or comments.

Question or Comment?