avatarDavid Kramer

Summary

This context provides a comprehensive tutorial on setting up a modern front-end web development environment using Visual Studio Code (VS Code) with support for TypeScript, JavaScript, React, and various testing and debugging tools.

Abstract

The tutorial aims to guide users through the process of creating a modern web development environment with support for modern languages and assets, intelligent code completion, continuous linting, continuous styling, continuous testing, and interactive remote debugging. It covers the installation and configuration of various tools and extensions, such as Homebrew, Node, jq, VS Code, Parcel, ESLint, TypeScript, Prettier, Jest, Babel, and React. The tutorial also includes steps for creating and configuring files for TypeScript, ESLint, Prettier, Babel, Enzyme, and Jest, as well as VS Code settings and launch configurations. Finally, the tutorial provides a practical example of test-driven development and debugging using the newly set up environment.

Opinions

  • The tutorial emphasizes the importance of modern languages and assets, such as TypeScript and modern JavaScript, for front-end web development.
  • Intelligent code completion is considered a significant advantage of using VS Code for JavaScript development.
  • Continuous linting, styling, and testing are recommended for maintaining code quality and consistency.
  • Interactive remote debugging is considered essential for efficient web application development.
  • The tutorial suggests using the Standard JS style guide rules for ESLint configuration.
  • The tutorial recommends using Prettier for code formatting and integrating it with ESLint.
  • The tutorial encourages using Jest for testing React components and integrating it with Enzyme.

Bootstrap a VS Code Front-End Web Development Environment

Setting up our environment for interactive debugging and continuous testing

Photo by Lingchor on Unsplash

Introduction

Would you like to do modern front-end web development but you’re not sure how to get started with a local development environment? Do you know some JavaScript and want to start learning TypeScript? Are you curious about React but want to build the toolchain yourself instead of using create-react-app?

In this tutorial, we are going to bootstrap a front-end web development environment with support for modern languages and assets, intelligent code completion, continuous linting, continuous styling, continuous testing, and interactive remote debugging. We will set up the environment in Visual Studio Code (VS Code) on a Mac.

Spoiler alert! The github.com/bacongravy/bootstrap-vscode-web project contains the result of following this tutorial. If you have trouble following this tutorial, you can try cloning that project and starting from there.

Goals

Before we begin the tutorial, let us review our six primary goals.

1. Modern languages and assets

We will have support for TypeScript and modern JavaScript (ES2019), with the latest features like arrow functions, import/export, async/await, and object rest/spread properties. We will also have support for JSX syntax, SCSS, and more.

Parcel is a web application bundler that we will use to package our app into a format that can be served to web browsers. Parcel will automatically use Babel to compile our code into a backwards compatible version of JavaScript that works in both current and older browsers. Besides being able to compile Typescript and JavaScript code, with or without JSX, Parcel also handles many other kinds of assets, including HTML, CSS, SCSS, and Vue.

2. Intelligent code completion

We will have intelligent code completion.

VS Code is a free, open-source code editor. It includes an IntelliSense feature based on the TypeScript language service, which provides smart completions and parameter info for both JavaScript and TypeScript. This feature is enabled by default and alone is a great reason to use VS Code for JavaScript development.

3. Continuous linting

We will have continuous linting and automatic fix-on-save behavior.

ESLint is a highly configurable static analyzer, able to report problems in your code before it runs. For instance, it can detect if you use variables that haven’t been defined. It can even fix your code for you (sometimes).

We will configure ESLint to use rules from the popular Standard JS project so that our code follows common standards. We will use a VS Code extension to integrate continuous static analysis into the source editor view, and we will configure VS Code to use ESLint to automatically fix everything it can, every time we save our code.

VS Code uses the TypeScript language service to lint TypeScript files by default, and provides opt-in support for linting JavaScript files. We will enable the feature so that the TypeScript language service infers the types in our JavaScript files and reports any errors it detects.

4. Continuous styling

We will have automatic style-on-save behavior.

Prettier is an opinionated code formatter that understands JavaScript code formatting conventions. It takes maximum line length into account, causing it to reflow single-line statements into multi-line statements if they get too long, making code more readable. It eliminates all custom styling by parsing the code to an abstract syntax tree (AST) and then re-printing it according to its own consistent rules.

Continuous styling isn’t just about making your code pretty. It can save you time, too. For instance, if you’re working on a team it can help avoid arguments between teammates over style during code reviews.

We will configure ESLint to apply Prettier styles as fixable linting rules and automatically style the code every time we save the code. We will also install a VS Code extension and configure it to automatically format-on-save other file formats (JSON, HTML, Markdown, YAML, etc.), too.

5. Continuous testing

We will have continuous testing that runs whenever we modify and save files in our project.

Jest is a testing framework with a focus on simplicity and speed. We will install a VS Code extension that runs Jest in watch mode while our workspace is open. We will immediately see if any of our tests are passing or failing whenever we save our code. The extension reports status inline with the source code and makes it easy to debug failing tests with one click.

6. Interactive remote debugging

We will have interactive remote debugging that lets us run our app in a web browser while stepping through the code in VS Code.

VS Code includes an interactive debugger that lets us set breakpoints, step through code, and examine variables. We will install two VS Code extensions that will allow us to debug our app from VS Code while the app is running remotely in either Edge or Chrome. We will configure VS Code to start the Parcel development server and launch the web browser whenever we begin a debug session. We will also configure VS Code to allow us to debug our Jest tests interactively.

Tutorial

We will start with a Mac fresh out of the box. You can skip the first few steps if you already have all of the software installed.

Pro tip! Do not open the workspace in VS Code until the tutorial tells you to, or else you will need to manually reload the window or app yourself to get the extensions to work properly.

First, we are going to use brew from Homebrew to install Node, jq, and VS Code, and then use code from VS Code to install the extensions. Next we will use npm from Node to install the modules. Finally, we will configure the components to work together. When we are done, we will take our development environment for a spin by writing a test and debugging it.

Install Homebrew

Homebrew is a package manager. It includes a command, brew, which we will use to install some packages. Follow the instructions at brew.sh to install it now.

Install Node

Node is the engine powering most of the tools we are going to use. We use Homebrew to install the Node package, which includes node, npm, and npx. Install the engine:

brew install node

Install jq

jq is a lightweight and flexible command-line JSON processor. We use jq to edit our config files in this tutorial. Install the processor:

brew install jq

Install VS Code

VS Code is an extensible source editor that will host our development environment. Install the app:

brew cask install visual-studio-code

In addition to Visual Studio Code.app, the Homebrew package also installs the command line tool code, which we will use to install our VS Code extensions next.

Install VS Code extensions

To fully integrate ESLint, Prettier, and Jest into VS Code, and to be able to debug our app running in a web browser, we need some VS Code extensions. Install the extensions:

code --install-extension dbaeumer.vscode-eslint
code --install-extension esbenp.prettier-vscode
code --install-extension Orta.vscode-jest

To be able to debug our app running in a web browser we also need another VS extension. You can either pick one to install, if you have a preference between Edge and Chrome, or you can choose to install both:

code --install-extension msjsdiag.debugger-for-edge
code --install-extension msjsdiag.debugger-for-chrome

Install web browsers

You probably already have the web browser you want to test with installed, but if not, here is how you can install Microsoft Edge and Google Chrome with Homebrew:

brew cask install microsoft-edge
brew cask install google-chrome

We only need one browser for this tutorial, but you can install both if you like.

Create a project

Before we can install Node modules, we need to be in a project directory. If you don’t already have one, then begin by creating an empty directory for the project and initializing it with npm. Run the following commands:

PROJECT_NAME="$(npx project-name-generator -o dashed)"
mkdir "${PROJECT_NAME}"
cd "${PROJECT_NAME}"
npm init -y

We use npx to run project-name-generator, which generates a random project name for us. Feel free to pick your own name instead, if you wish. We run npm init to generate a package.json file. The defaults are fine for now.

Install Parcel

Parcel is a web application bundler. It also includes a development server with support for hot module replacement (HMR), making for a quick, iterative development experience. Install the bundler:

npm install -D parcel

Install ESLint

ESLint is a static analyzer, but by itself, it doesn’t do much. We will be installing configs and plugins later in this tutorial to tell ESLint how to do its magic. Install the static analyzer:

npm install -D eslint

Install TypeScript

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. We will use Parcel to compile our TypeScript code, but we will use the TypeScript compiler tsc to lint our code from the command-line, in order to match the built-in support for TypeScript linting provided by VS Code. ESLint doesn’t understand TypeScript out-of-the-box so we also need to install a parser and plugin to integrate them together. Install the compiler and integrations:

npm install -D typescript \
               @typescript-eslint/parser \
               @typescript-eslint/eslint-plugin

Install the Standard JS style guide rules

Standard JS is a popular style guide for JavaScript that has been extended to support TypeScript. Use npx to run the install-peerdeps command to install the TypeScript-compatible Standard JS config for ESLint, along with its peer dependencies:

npx install-peerdeps -D eslint-config-standard-with-typescript

Install Prettier

Prettier is an opinionated code formatter. It integrates with ESLint by first disabling all of the ESLint rules that would conflict with Prettier and then adding rules to enforce the Prettier style. Install the formatter and integrations:

npm install -D prettier \
               eslint-config-prettier \
               eslint-plugin-prettier

Install Jest

Jest is a fast and easy-to-use test runner. There is a Jest plugin for ESLint that keeps our test suites looking nice. There is a Jest types package so that the VS Code type checker doesn’t complain about the Jest globals. Install the test runner and integrations:

npm install -D jest \
               eslint-plugin-jest-formatting \
               @types/jest

Install Babel presets

Babel is a JavaScript compiler. Parcel takes care of configuring Babel for itself, but not for Jest. To allow Jest to test our code we will need to configure Babel with preset packages. Install the presets:

npm install -D @babel/preset-env \
               @babel/preset-react \
               @babel/preset-typescript

Install React

React is a JavaScript library for building user interfaces. To make React easier to use we will also install ESLint plugins for React, and TypeScript types. Install the library and integrations:

npm install react react-dom
npm install -D eslint-plugin-react \
               eslint-plugin-react-hooks \
               eslint-plugin-jsx-a11y \
               @types/react \
               @types/react-dom

Install Enzyme

Enzyme is a JavaScript library for testing React components. Enzyme has an integration that enhances Jest and requires an adaptor to connect it to the specific version of React we are using. (If you’re reading this in the future you might need to change which adaptor you use.) Install the library, integration, adaptor, and corresponding TypeScript types:

npm install -D enzyme \
               jest-enzyme \
               enzyme-adapter-react-16 \
               @types/enzyme \
               @types/enzyme-adapter-react-16

Review

We began by installing Homebrew, Node, jq, VS Code, and some VS Code extensions. We then created a project. Finally we installed Parcel, ESLint, TypeScript, the Standard JS style guide rules, Prettier, Jest, Babel presets, React, Enzyme, and all of the extra bits to glue them together.

Configure Everything

The components we just installed need to be configured to work with each other. We are going to create some files in our project directory to configure them.

Create tsconfig.json

This file configures TypeScript. Run the following command in the project directory to create the file:

cat >tsconfig.json <<EOF
{
  "include": ["src/**/*", ".enzyme.ts"],
  "exclude": ["dist"],
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "allowJs": true,
    "checkJs": true,
    "jsx": "preserve",
    "noEmit": true,
    "strict": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true
  }
}
EOF

We configure the TypeScript compiler to act only as a linter for our project because we will be using Parcel and Babel to actually compile TypeScript code. The settings in this file also affect the VS Code TypeScript linter.

  • The include setting tells the TypeScript compiler to lint all files in src, and to read type imports from .enzyme.ts.
  • The exclude setting tells the VS Code TypeScript language service to ignore the files generated by Parcel in dist.
  • The target and module settings tell the TypeScript compiler to output modern JavaScript code with no extra polyfills or glue code.
  • The allowJS and checkJS settings tell TypeScript to also lint JavaScript files.
  • The jsx setting tells TypeScript to allow JSX in JavaScript and TypeScript files.
  • The noEmit setting tells the TypeScript compiler to only lint our code and not produce any output files.
  • The strict setting tells TypeScript to enable all strict type checking options.
  • The allowSyntheticDefaultImports setting tells TypeScript to make traditional ES modules look like TypeScript modules with a default export when importing them into TypeScript code.
  • The forceConsistentCasingInFileNames setting tells TypeScript to error if an import uses the wrong casing in a filename.

Create .eslintrc

This file configures ESLint. Run the following command to create the file:

cat >.eslintrc <<EOF
{
  "extends": [
    "plugin:jest-formatting/recommended",
    "plugin:jsx-a11y/recommended",
    "plugin:react-hooks/recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "standard-with-typescript",
    "prettier/@typescript-eslint",
    "plugin:prettier/recommended"
  ],
  "env": {
    "es6": true,
    "browser": true,
    "jest": true
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "project": "./tsconfig.json",
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "rules": {}
}
EOF

This configures ESLint to understand TypeScript and to use the Standard JS style guide, the React plugins, and the Jest and Prettier integrations.

  • The extends setting tells ESLint to use recommended rules from each of the configurations that we’ve installed.
  • The env setting tells ESLint which environments we are targeting, which mostly affects which global variables are allowed.
  • The parser settings tells ESLint to use the TypeScript parser.
  • The parserOptions setting tells the TypeScript parser where our tsconfig.json file is and what syntax to expect.
  • The settings setting tells the React plugin to detect the version of React being used.
  • The rules setting is where you can add your own linting rules and overrides later.

Create .eslintignore

This file configures ESLint to ignore the files generated by Parcel in the dist directory. Run the following command to create the file:

cat >.eslintignore <<EOF
dist/
EOF

Create .prettierrc

This file configures Prettier to match the Standard JS style guide we’ve chosen to use. Run the following command to create the file:

cat >.prettierrc <<EOF
{
  "tabWidth": 2,
  "semi": false,
  "singleQuote": true,
  "trailingComma": "all"
}
EOF

Create .babelrc

This file configures Babel. We wouldn’t normally need this file when using Parcel, because Parcel configures Babel for us, but Jest also needs to know how to compile our code. Run the following command to create the file:

cat >.babelrc <<EOF
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-typescript",
    "@babel/preset-react"
  ]
}
EOF

Create .enzyme.ts

This file makes the TypeScript linter aware of the jest-enzyme types and configures Enzyme to use an adapter. Run the following command to create the file:

cat >.enzyme.ts <<EOF
import 'jest-enzyme'
import { configure } from 'enzyme'
import EnzymeAdapter from 'enzyme-adapter-react-16'
configure({ adapter: new EnzymeAdapter() })
EOF

Create jest.config.json

This file configures Jest to ignore the dist directory and to use the Enzyme configuration we just created along with the setup files from jest-enzyme. Run the following command to create the file:

cat >jest.config.json <<EOF
{
  "testPathIgnorePatterns": [
    "<rootDir>/dist/",
    "<rootDir>/node_modules/"
  ],
  "setupFilesAfterEnv": [
    "<rootDir>/.enzyme.ts",
    "<rootDir>/node_modules/jest-enzyme/lib/index.js"
  ]
}
EOF

Create .vscode

VS Code looks in this directory in our project for workspace settings and launch configurations. Run the following command to create the directory:

mkdir .vscode

Create .vscode/settings.json

This file configures the VS Code editor. Run the following command to create the file:

cat >.vscode/settings.json <<EOF
{
  "editor.codeActionsOnSave": { "source.fixAll": true },
  "editor.formatOnSave": true,
  "[javascript]": { "editor.formatOnSave": false },
  "[javascriptreact]": { "editor.formatOnSave": false },
  "[typescript]": { "editor.formatOnSave": false },
  "[typescriptreact]": { "editor.formatOnSave": false },
  "prettier.disableLanguages": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
  "[jsonc]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }
}
EOF

In this file we begin by configuring VS Code to automatically fix and format all files on save. Then we disable the Prettier format-on-save behavior for JavaScript and TypeScript files and enable ESLint validation for them so that the TypeScript language service, ESLint, and the Prettier ESLint plugin can do their jobs. Finally, we set Prettier as the default formatter for JSON files.

Create .vscode/tasks.json

This file describes some custom tasks that we will use to help us debug our code. Run the following command to create the file:

cat >.vscode/tasks.json <<'EOF'
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "debug-start",
      "command": "npx",
      "args": ["parcel", "src/index.html"],
      "isBackground": true,
      "problemMatcher": {
        "fileLocation": "relative",
        "pattern": {
          "regexp": "^.*$",
          "file": 1,
          "location": 2,
          "severity": 3,
          "code": 4,
          "message": 5
        },
        "background": {
          "activeOnStart": true,
          "beginsPattern": "^.*$",
          "endsPattern": "^Server running at .*$"
        }
      }
    },
    {
      "label": "debug-stop",
      "command": "echo ${input:terminate}",
      "type": "shell"
    }
  ],
  "inputs": [
    {
      "id": "terminate",
      "type": "command",
      "command": "workbench.action.tasks.terminate",
      "args": "debug-start"
    }
  ]
}
EOF

These tasks are used to start the Parcel development server whenever a debug session begins and stop the server when the session ends. The debug-start task runs the parcel command in the background, and the debug-stop task terminates the debug-start task.

Create .vscode/launch.json

This file describes to VS Code how to debug our code. Run the following command to create the file:

cat >.vscode/launch.json <<'EOF'
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "edge",
      "type": "edge",
      "request": "launch",
      "preLaunchTask": "debug-start",
      "postDebugTask": "debug-stop",
      "skipFiles": [
        "<node_internals>/**",
        "${workspaceFolder}/node_modules/**"
      ],
      "url": "http://localhost:1234",
      "webRoot": "${workspaceFolder}/dist"
    },
    {
      "name": "chrome",
      "type": "chrome",
      "request": "launch",
      "preLaunchTask": "debug-start",
      "postDebugTask": "debug-stop",
      "skipFiles": [
        "<node_internals>/**",
        "${workspaceFolder}/node_modules/**"
      ],
      "url": "http://localhost:1234",
      "webRoot": "${workspaceRoot}/dist"
    },
    {
      "name": "vscode-jest-tests",
      "type": "node",
      "request": "launch",
      "skipFiles": [
        "<node_internals>/**",
        "${workspaceFolder}/node_modules/**"
      ],
      "args": ["--runInBand"],
      "cwd": "${workspaceFolder}",
      "program": "${workspaceFolder}/node_modules/jest/bin/jest"
    }
  ]
}
EOF

In this file we include three ways to debug our code. The first two configurations allow us to debug our app in either the Edge or Chrome browsers. The third configuration is used by the Jest extension to run individual tests and also allows us to debug the entire test suite.

Update package.json

This file contains our package configuration. We use jq to merge some scripts into the existing file. Run the following command to update the file:

jq -s '.[0] * .[1]' package.json <(cat <<EOF
{
  "scripts": {
    "start": "parcel src/index.html",
    "build": "parcel build src/index.html",
    "clean": "rm -rf dist",
    "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix --color && tsc",
    "lint:check": "eslint --print-config .eslintrc | eslint-config-prettier-check",
    "test": "jest",
    "test:watch": "jest --watchAll"
  }
}
EOF) > package-new.json && mv package-new.json package.json
  • The start script uses Parcel to bundle our app and then serve it using the built-in Parcel development server.
  • The build script uses Parcel to bundle our app for production.
  • The clean script deletes the dist directory containing the bundled app.
  • The lint script checks the code for lint issues, fixing any issues which can be fixed.
  • The lint:check script verifies that the ESLint rules don’t conflict with the Prettier rules.
  • The test script runs Jest.
  • The test:watch script runs Jest in watch mode.

Most of the time you won’t need to use any of these scripts from VS Code because we have configured VS Code to do all of this for us: the IDE continuously lints and tests while we are editing, and automatically starts the Parcel development server when we begin debugging. The scripts are most useful if you need to access the project from the command-line, outside of VS Code.

Try It Out

To get a feel for our new development environment, we are going to practice test-driven design (TDD) by writing a test that fails and then implementing the code to make the test pass.

We can use the code command to open a new workspace window in VS Code from our current directory. We will create a directory, touch some files to create them, and then open the workspace:

mkdir src
touch src/index.html src/index.css src/index.jsx \
      src/App.tsx src/App.test.tsx
code .

We should be greeted with a view like this:

The VS Code welcome screen and our workspace

We already have one error in our workspace (look in the bottom left of the window, in the blue status bar) because Jest doesn’t like it when test files are empty, and we haven’t put anything in App.test.tsx yet:

A Jest error

Now we will fill in the files with content. We will have an HTML file that links to a JavaScript JSX file. The JavaScript JSX file will import a CSS file, and a component from a TypeScript JSX file. Finally, we will have a test for our component, also written in TypeScript JSX.

Click on “> src” in the sidebar file explorer to reveal the files we previously created:

Our project’s files in the VS Code explorer

Put the following content into index.html and save:

The <div id="app"> element is where our React app will get mounted, and the <script src="index.jsx"> element loads the code that does the mounting.

Let us create that code now. Put the following into index.jsx and save:

The code in this file imports a CSS file which causes the imported styles to get mounted into the HTML <head>. The code then imports our App component and renders it to the app element in the document.

Put the following into index.css and save:

The CSS in this file sets the background color of the entire page, and is a quick way to visually verify that our code is loading in the browser.

Put the following into App.tsx and save:

Here we define the App component. Notice that this file is written in TypeScript — the render method is annotated with the return type JSX.Element.

Put the following into App.test.tsx and save:

This tests our App component by first rendering it using enzyme and then comparing it against expectations. Once we save this file Jest will re-run our test and show us that our test is failing. The error is reported in the lower left corner of the window:

One error in our workspace

Clicking on this status bar item takes us to to the Problems view where we can see the error message:

A description of the error in our workspace

Jest is telling us that our test expected our component to render “Hello, World!” but instead rendered “Hello”. We can fix the problem by changing the code in App.tsx from <h1>Hello</h1> to <h1>Hello, World!<h1>. Make the change now and save. Verify that there are now no more errors in the workspace:

Our workspace with no errors

Next we will try debugging our code. Back inApp.tsx, set a breakpoint on the render method by hovering and clicking in the gutter to the left of the code:

Setting the breakpoint in the editor

Now navigate to the Run view:

Run view in VS Code

At the top we can select which launch configuration to use:

Run controls in VS Code

Choose edge or chrome from the menu, depending on your preference (and what you chose to install), and then click the green triangle Start Debugging button. The Parcel development server will start and the browser will launch and load our web app. Be patient, and after a few seconds we should see the background color of the page change:

Our app partly loaded and paused in the browser

A moment later our breakpoint should be hit back in VS Code:

VS Code stopped on our breakpoint

Click Continue to allow the component to render:

Back in the browser we can now see our component rendered:

We can now make changes to our code and see it update live in the browser when we save. Try changing the background color in index.css from lemonchiffon to lightcoral and then saving. Notice the background color in the browser immediately updates:

Play around some more. Try adding more elements to the App component, or more styles to the CSS file. When you’re done, press the Stop button. The browser exits, the Parcel development server stops, and the debug session ends.

Congratulations, you now know how to use your fully-functioning modern development environment for writing front-end web applications in React. I can’t wait to see what you create.

Acknowledgements

The following articles were influential to this tutorial:

Thank you.

Software Development
JavaScript
Programming
Visual Studio Code
Front End Development
Recommended from ReadMedium