Chapter 1. Setting Up a Development Environment

You may have heard it said that the “tools make the developer.” While that’s something of an exaggeration, no one wants to be left in front of a wall of JavaScript code without their favorite tools to edit, analyze, and debug it.

When you’re setting up your own development environment, the first tool you’ll consider is a code editor. Even the most basic editor adds essentials like autocompletion and syntax highlighting—two simple features that prevent piles of potential mistakes. Modern code editors add many more features, such as integration with a source control service like GitHub, line-by-line debugging, and smart refactoring. Sometimes these features will snap into your editor with a plug-in. Sometimes you’ll run them from the terminal or as part of a build process. But no matter how you use your tools, assembling the right combination to suit your coding style, development environment, and project types is part of the fun. It’s like a home improvement pro collecting tools, or an aspiring chef investing in just the right cooking gear.

Tool choices aren’t static. As a developer, your preferences may shift. You’ll grow your kit as you evolve and as new tools prove themselves useful. This chapter explores the minimum toolset that every JavaScript developer should consider before they tackle a project. But there’s plenty of room to choose between different, broadly equivalent options. And, as many a wise person has remarked, there’s no accounting for taste!

Note

In this chapter, we’re putting on our advocacy hat. You’ll see some of our favorite tools, and references to other, equally good options. But we don’t attempt to cover every tool, just some excellent default choices you can start with.

Choosing a Code Editor

Problem

You want to write code in an editor that understands JavaScript syntax.

Solution

If you’re in a hurry, you won’t go wrong with our favorite choice, Visual Studio Code (often shortened to just VS Code). You can download this free, open source editor for Windows, Macintosh, or Linux.

If you have time to research, there are a number of other editors you might consider. The list in Table 1-1 is far from complete, but shows some of the most consistently popular editors.

Table 1-1. Desktop code editors
Editor Supported platforms Open source Cost Notes

Visual Studio Code

Windows, Macintosh, Linux

Yes

Free

A great choice for any language, and our first choice for JavaScript development

Atom

Windows, Macintosh, Linux

Yes

Free

Most of the chapters in this book were written using Atom with plug-ins for AsciiDoc support

WebStorm

Windows, Macintosh, Linux

No

Free for open source developers and educational users, otherwise roughly $60 per year for an individual

A heavier-weight environment that’s closer to a traditional IDE than a code editor

Sublime Text

Windows, Macintosh, Linux

No

A one-time payment of $80 for an individual, although there is no license enforcement or time limit

A popular editor with a reputation for fast performance with massive text files

Brackets

Windows, Macintosh

Yes

Free

An Adobe-sponsored project that’s focused on web development

No matter what code editor you choose, you’ll follow a similar process to start a new project. Begin by creating a new folder for your project (like test-site). Then, in your code editor, look for a command like File > Open Folder, and choose the project folder you created. Most code editors will immediately show the contents of the project folder in a handy list or tree panel, so you can quickly jump between files.

Having a project folder also gives you a place to put the packages you use (“Downloading a Package with npm”) and store application-specific configuration files and linting rules (“Enforcing Code Standards with a Linter”). And if your editor has a built-in terminal (“Extra: Using a Terminal and Shell”), it always starts in the current project folder.

Discussion

Recommending a best editor is a little like me choosing your dessert. Personal taste is definitely a factor, and there are at least a dozen reasonable choices. Most of the suggestions listed in Table 1-1 tick off all the important boxes, meaning they’re:

  • Cross-platform, so it doesn’t matter what operating system you’re using.

  • Plug-in-based, so you can snap in whatever features you need. Many of the tools mentioned in this book (like the Prettier code formatter described in “Enforcing Code Standards with a Linter”) have plug-ins that integrate with different editors.

  • Multilanguage, allowing you to go beyond HTML, CSS, and JavaScript to write code in other programming languages (with the right plug-in).

  • Community-driven, which gives you confidence that they’ll be maintained and improved long into the future.

  • Free, or available for a modest cost.

Our top choice, VS Code, is a Microsoft-built code editor with native JavaScript support. In fact, the editor itself is written in JavaScript, and hosted in Electron. (More precisely, it’s written in TypeScript, a stricter superset of JavaScript that’s transpiled into JavaScript before it’s distributed or executed.)

In many ways, VS Code is the younger, trendier sibling to Microsoft’s sprawling Visual Studio IDE, which is also available in a free Community edition, and also supports JavaScript coding. But VS Code strikes a better balance for developers that aren’t already working with the Microsoft .NET stack. That’s because it starts out lightweight, but is endlessly customizable through its library with thousands of community plug-ins. In Stack Overflow’s developer survey, VS Code regularly ranks as the most popular code editor across as languages.

See Also

For an introduction to VS Code’s basic features and overall organization, there’s an excellent set of introductory videos. In this chapter, you’ll also learn how to use Emmet shortcuts in VS Code (“Filling in HTML Boilerplate with Emmet Shortcuts”), and how to add the ESLint (“Enforcing Code Standards with a Linter”) and Prettier (“Styling Code Consistently with a Formatter”) plug-ins.

Using the Developer Console in Your Browser

Problem

You want to see the errors that occur in your web page and the messages you write to the console.

Solution

Use the developer console in your browser. Table 1-2 shows how to load the developer tools in every modern desktop browser.

Table 1-2. Shortcut key to load the developer console
Browser Operating system Shortcut

Chrome

Windows or Linux

F12 or Ctrl+Shift+J

Chrome

Macintosh

Cmd-Option-J

Edge

Windows or Linux

F12 or Ctrl+Shift+J

Firefox

Windows or Linux

F12 or Ctrl+Shift+J

Firefox

Macintosh

Cmd-Shift-J

Safaria

Macintosh

Cmd-Option-C

Opera

Windows

Ctrl+Shift+J

Opera

Macintosh

Cmd-Option-J

a Before you can use the developer console in Safari, you must enable it. To do so, choose Safari Menu > Preferences from the menu, click the Advanced tab, and check Show Develop menu in the menu bar.

The developer tools are usually presented as a tabbed group of panes at the right or bottom of the web browser window. The Console panel is the one that shows the messages you output with console.log() and any unhandled errors.

Here’s the full code for a page that writes to the console and then fails with an error:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Log and Error Test</title>
  </head>
  <body>
    <h1>Log and Error Test</h1>

<script>
  console.log('This appears in the developer console');
</script>

<script>
  // This will cause an error that appears in the console
  const myNumber =
</script>
  </body>
</html>

Figure 1-1 shows the output in the developer console. The logged message appears first, followed by the error (a SyntaxError for “Unexpected end of input”). Errors are displayed in red lettering, and Chrome helpfully adds links next to each message, so you can quickly view the source code that caused the message. Lines in your web pages and script files are numbered automatically. In this example, that makes it easy to distinguish between the source of the message (line 13) and the source of the error (the closing </script> tag on line 19).

jsc3 0101
Figure 1-1. Viewing the output in Chrome’s developer console

Discussion

We use console.log() throughout this book, often to write quick testing messages. However, there are other console methods you can use. Table 1-3 lists some of the most useful.

Table 1-3. Console methods
Method Description

console.warn(object)

Similar to console.log(), but outputs text with a yellow background.

console.error(object)

Similar to console.log(), but outputs text with a red background. It’s typically used to log error objects.

console.assert(expression, object)

If the expression is false, the message is written to the console along with a stack trace.

console.trace()

Displays a stack trace.

console.count(label)

Displays the number of times you’ve called this method with this label.

console.dir(object)

Displays all the properties of an object in an expandable, tree-like list.

console.group()

Starts a new group with the title you supply. The following console messages are indented underneath this heading, so they appear to be part of one logically related section. You use console.groupEnd() to end the group.

console.time(label)

Starts a timer with a label you use to identify it.

console.timeEnd(label)

Stops the timer associated with the label and displays the elapsed time.

Note

The consoles in modern browsers sometimes use lazy evaluation with objects and arrays. This issue may appear if you output an object with console.log(), then change it, and then output the same object a second time. If you do this from the script code in a web page, you’ll often find that both calls to console.log() emit the same changed object, even though the first call preceded the actual change!

To avoid this quirk, you can explicitly convert your object to a string before you log it. This trick works because the console doesn’t use lazy evaluation with strings. This technique isn’t always convenient (for example, it doesn’t help if you want to log a complete array that contains objects), but it does let you work around most cases.

Of course, the console is only one panel (or tab) in the developer tools. Look around, and you’ll find quite a bit of useful functionality packed into the other panels. The exact arrangement and naming depends on your browser, but here are some highlights in Chrome:

Elements

Use this panel to view the HTML markup for specific parts of your page, and inspect the CSS rules that apply to individual elements. You can even change markup and styles (temporarily) to quickly test potential edits.

Sources

Use this panel to browse all the files the current page is using, including JavaScript libraries, images, and style sheets.

Network

Use the panel tab to watch the size and download time of your page and its resources, and to view the asynchronous messages being sent over the wire (for example, as part of a fetch request).

Performance

Use this panel to start tracking the time your code takes to execute (see “Analyzing Runtime Performance”).

Application

Use this panel to review all the data the current site is storing with cookies, in local storage or with the IndexedDB API.

You can play around with most of these panels to get an idea about how they work, or you can review Google’s documentation.

See Also

“Running Blocks of Code in the Developer Console” explains how to run ad hoc bits of code in the developer console.

Running Blocks of Code in the Developer Console

Problem

You want to try out a snippet of code without opening an editor and creating HTML and JavaScript files.

Solution

Use the developer console in your browser. First, open the developer tools (as explained in “Using the Developer Console in Your Browser”). Make sure the Console panel is selected. Then, paste or type your JavaScript.

Press Enter to run your code immediately. If you need to type multiple lines of code, press Shift+Enter at the end of each line to insert a soft return. Only press Enter when you’re finished and you want to run your full block of code.

Often, you’ll want to modify the same piece of code and rerun it. In all modern browsers, the developer console has a history feature that makes this easy. To use it, press the up arrow key to show the previously executed code block. If you want to see the code you ran before that, press the up arrow multiple times.

Figure 1-2 shows an example with a code block that didn’t run successfully the first time because of a syntax error. The code was then called up in the history, edited, and executed, with the output (15) appearing underneath.

jsc3 0102
Figure 1-2. Running code in the console

The history feature only works if you don’t start typing in any new code. If the console command line isn’t empty, the up arrow key will just move through the current code block rather than stepping back through the history.

Discussion

In the developer console, you can enter JavaScript code exactly as you would in a script block. In other words, you can add functions and call them, or define a class and then instantiate it. You can also access the document object, interact with HTML elements in the current page, show alerts, and write to the console. (The messages will appear directly below.)

There’s one potential stumbling block when using the console for longer code examples. You may run into a naming clash, because JavaScript won’t allow you to define the same variables or function names in the same scope more than once. For example, consider a simple block of code like this:

const testValue = 40+12;
console.log(testValue);

This works fine if you run it once. But if you call it back up in the history to make a modification (by pressing the up arrow), and you try to run it again, you’ll get an error informing you that testValue is already declared. You could rename your variable, but if you’re trying to perfect a snippet of code with multiple values and functions, this renaming gets awkward fast. Alternatively, you could execute the command location.reload() to refresh the page, but that can be slow for complex pages, and you might lose some page state you’re trying to keep.

Fortunately, there’s a simpler solution. Simply enclose your entire block of code in an extra set of braces to create a new naming scope. You can then safely run the code multiple times, because each time a new context is created (and then discarded).

{
  const testValue = 40+12;
  console.log(testValue);
}

See Also

“Debugging JavaScript” explores the art of debugging in the developer console. “Analyzing Runtime Performance” shows how to use the developer console for performance analysis.

Using Strict Mode to Catch Common Mistakes

Problem

You want to disallow potentially risky features, like automatic variable creation and some statements that fail silently.

Solution

Add the use strict directive at the top of your JavaScript code file, like this:

'use strict';

Alternatively, consider writing your JavaScript in a module, which is always loaded in strict mode (“Organizing Your JavaScript Classes with Modules”).

Discussion

JavaScript has a (somewhat deserved) reputation for tolerating sloppy code practices. The problem is that languages that ignore minor rule breaking put developers at a disadvantage. After all, you can’t fix a problem that you never notice.

The following example demonstrates an example of JavaScript gone bad. Can you find the mistake?

// This function adds a list of consecutive numbers
function addRange(start, end) {
  let sum = 0;
  for (let i = start; i < end+1; i++) {
    sum += i;
  }
  return sum;
}

// Add numbers from 10 to 15
let startNumber = 10;
let endNumber = 15;
console.log(addRange(startNumber,endNumber));   // Displays 75

// Now add numbers from 1 to 5
startnumber = 1;
endNumber = 5;
console.log(addRange(startNumber,endNumber));   // Displays 0, but we expect 15

Although the code runs without an error, the results aren’t what we expect. The problem occurs in this line:

startnumber = 1;

The issue here is that JavaScript creates variables whenever you assign a value, even if you don’t explicitly define the variable. So if you assign to startnumber when you really want startNumber, JavaScript quietly creates a new startnumber variable. The end result is that the value you intended to assign to startNumber vanishes into another variable, never to be seen or used again.

To catch this problem, add the strict mode directive to the top of the file, before the function code:

'use strict';

Now a ReferenceError occurs when JavaScript reaches the startnumber assignment. This interrupts your code, ending the script. However, the error appears in red lettering in the developer console, explaining the problem and the line number where it happened. Now, a fix is trivially easy.

Strict mode catches a number of small but pernicious errors. Some examples include:

Many of these actions would fail without strict mode. However, they would fail silently, potentially leading to a maddening situation where your code doesn’t work the way you expect it to, and you have no idea why.

Tip

You may be able to configure your editor to insert the use strict directive to every new code file. For example, Visual Studio Code has at least three small extensions that offer to perform this task.

Strict mode catches a relatively small set of errors. Most developers also use a linting tool (“Enforcing Code Standards with a Linter”) to catch a much broader range of bugs and potentially risky actions. In fact, developers rely on linters to such an extent that they sometimes don’t bother to apply strict mode at all. However, it’s always recommended to have strict mode as a basic level of protection against shooting yourself in the foot.

See Also

For the full details on what strict mode won’t accept, see the strict mode documentation. To see how to use modules, which always execute in strict mode, see “Organizing Your JavaScript Classes with Modules”.

Filling in HTML Boilerplate with Emmet Shortcuts

Problem

You want to add a common chunk of HTML boilerplate without painstakingly typing each start and end tag.

Solution

Emmet is an editor feature that automatically changes predefined text abbreviations into standard blocks of HTML. Some code editors, like Visual Studio and WebStorm, support Emmet natively. Other editors, like Atom and Sublime Text, require the use of an editor plug-in. You can usually find the right plug-in by searching the plug-in library for “Emmet,” but if you’re in doubt, there’s a master list of Emmet-supporting plug-ins.

To use Emmet, create a new file and save it with a .html or .htm extension, so your code editor recognizes it as an HTML document. Then, type one of Emmet’s abbreviations, followed by the Tab key. (In some editors, you might use a different shortcut, like Enter or Ctrl+E, but the Tab key is most common.) Your text will be automatically expanded into the corresponding block of markup.

For example, the Emmet abbreviation input:time expands into this markup:

<input type="time" name="" id="" />

Figure 1-3 shows how VS Code recognizes an Emmet abbreviation as you type it. VS Code provides autocomplete support for Emmet, so you can see possible choices, and it adds the note “Emmet Abbreviation” to the autocomplete menu to signal that you aren’t writing HTML, but an Emmet shortcut that will be translated into HTML.

jsc3 0103
Figure 1-3. Using Emmet in VS Code

Discussion

Emmet provides a straightforward syntax, but it’s surprisingly flexible. You can write more complicated expressions that create nested combinations of elements, set attributes, and incorporate sequential numbers into names. For example, to create a bulleted list with five items, you use the abbreviation ul>li*5, which adds the following block of markup:

<ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
</ul>

Or, you can create the starting skeleton for an HTML5 web page (the modern standard) with the shortcut html:5.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

</body>
</html>

All of these features are described in the Emmet documentation. If you’re in a hurry, start with the patterns in the useful cheatsheet.

Installing the npm Package Manager (with Node.js)

Problem

You want to install npm, so you can easily download JavaScript libraries from the npm registry and add them to web projects.

Solution

The Node Package Manager (npm) hosts the largest (and currently most popular) software registry in the world. The easiest way to get software from the npm registry is using npm, which is bundled with Node.js. To install Node, download an installer for your operating system (Windows, MacOS, or Linux) from the Node website.

Once you finish installing Node, you can test that it’s available using the command line. Open a terminal window and type the command node -v. To check if npm is installed, type npm -v. You’ll see the version number of both packages:

$ node -v
v14.15.4
$ npm -v
6.14.10

Discussion

npm is included with Node.js, a JavaScript runtime environment and web server. You might use Node to run a server-side JavaScript framework like Express, or to build a JavaScript desktop application with Electron. But even if you don’t plan to use Node, you’ll almost certainly still install it just to get access to the npm package manager.

The Node Package Manager is a tool that can download packages from the npm registry, a free catalog that tracks tens of thousands of JavaScript libraries. In fact, you’ll be hard-pressed to find a computer that’s used for JavaScript development that doesn’t have an installation of Node and npm.

The work of a package manager goes beyond simply downloading useful libraries. The package manager also has the responsibility of tracking what libraries your project is using (called dependencies), downloading the packages they depend on (sometimes called subdependencies), storing versioning information, and distinguishing between test and production builds. Thanks to npm, you can take a completed application to another computer and install all the dependencies it needs with a single command, as explained in “Downloading a Package with npm”.

Although npm is currently the most popular package manager for JavaScript, it’s not the only one you might encounter. Yarn is favored by some developers who find it offers faster package installation. Pnpm is another option that aims to be command-line compatible with npm, while requiring less diskspace and offering better installation performance.

See Also

To install a package with npm, see “Downloading a Package with npm”.

If you’re using Node for development (not just npm), you should consider installing it with nvm, the Node version manager. That way you can easily switch between different Node versions and quickly update your installation when new releases are available (which is often). For more information, see “Managing Node Versions with Node Version Manager”. And if you need help to get started running code in the Node environment, Chapter 17 has many more examples.

Extra: Using a Terminal and Shell

To run Node or npm, you use the terminal. Technically, a terminal is a text-based interface that communicates with a shell to execute commands. Many different terminal programs exist, along with many different shells. The terminal and shell program that you use depends on your operating system (and your personal preference, because there are plenty of third-party alternatives).

Here are some of the most common terminal and shell combinations you’ll encounter:

  • On a Macintosh computer, go to Applications, open the Utilities folder, and choose Terminal. This launches the default terminal program, which uses bash as its shell.

  • On a Linux computer, the terminal program depends on the distro. There’s often a shortcut named Terminal, and it almost always uses the bash shell.

  • On Windows, you can launch PowerShell from the Start menu. Technically, PowerShell is the shell and it’s wrapped in a terminal process called conhost. Microsoft is developing a modern conhost replacement called Windows Terminal, which early adopters can install from the Windows Store (or download from GitHub). Microsoft also includes the bash shell as part of its Windows Subsystem for Linux, although that’s a relatively recent addition to the operating system.

  • Code editors sometimes include their own terminals. For example, if you open the terminal window in VS Code (use the Ctrl + ` shortcut [that’s a backtick, not a single quote] or choose View > Terminal from the menu) you get VS Code’s integrated terminal window. By default, it communicates with PowerShell on Windows and bash on other systems, although you can configure its settings.

When we direct you to use a terminal command, you can use the terminal window in your code editor, the terminal program that’s specific to your computer, or one of the many third-party terminal and shell applications. They all get the same environment variables (which means they have access to Node and npm once they’re installed), and they all have the ability to run programs in the current path. You can also use your terminal for the usual filesystem maintenance tasks, like creating folders and files.

Note

In this book, when we show the commands you should type in a terminal (as in “Installing the npm Package Manager (with Node.js)”), we preceded them with the $ character. This is the traditional prompt for bash. However, different shells have different conventions. If you’re using PowerShell you’ll see a folder name followed by the > character instead (as in C:\Projects\Sites\WebTest>). Either way, the commands you use to run utilities (like npm) don’t change.

Downloading a Package with npm

Problem

You want to install a specific software package from the npm registry.

Solution

First, you must have npm on your computer (see “Installing the npm Package Manager (with Node.js)” for instructions). Assuming you do, open a terminal window (“Extra: Using a Terminal and Shell”), and go to the project directory for your website.

Next, you should create a package.json file, if your application doesn’t already have one. You don’t actually need this file to install packages, but it does become important for some other tasks (like restoring your packages to another development computer). The easiest way to create a package.json file is with npm’s init command:

$ npm init -y

The -y parameter (for yes) means that npm will simply choose default values rather than prompt you for specific information about your application. If you don’t include the -y parameter, you’ll be asked a variety of questions about your application (its package name, description, version, license, and so on). However, you don’t need to fill in any of these details at first (or at all), so it’s perfectly acceptable to press Enter to leave each field blank and create the basic package.json boilerplate. For more information about the descriptive information inside package.json, see “Extra: Understanding package.json”.

Once you’ve initialized your application, you’re ready to install a package. You must know the exact name of the package you want to install. By convention, npm names are made up of dash-separated lowercase words, like fs-extra or react-dom. To install your package of choice, run the npm install command with the package name. For example, here’s how you would install the popular Lodash library:

$ npm install lodash

npm adds the packages you install to the package.json file. It also records more detailed versioning information about each package in a file named package-lock.json.

When you install a package, npm downloads its files and places them in a folder named node_modules. For example, if you install Lodash in a project folder named test-site, the Lodash script files will be placed in the folder test-site/node_modules/lodash.

You can remove a package by name using npm uninstall:

$ npm uninstall lodash

Discussion

The genius of npm (or any package manager) becomes apparent when you have a typical web project with half a dozen or more packages, each of which depends on additional packages. Because all these dependencies are tracked in the package-lock.json file, it’s easy to figure out what a web application needs. You can see a full report by executing this command from your project folder:

$ npm list

It’s also easy to re-download these packages on a new computer. For example, if you copy your website to another computer with the package.json and package-lock.json files, but without the node_modules folder, you can install all the dependent packages like this:

$ npm install

So far, you’ve seen how to install packages locally (as part of the current web application). npm also allows packages to be installed globally (in a system-specific folder, so the same version is available to all the web applications on your computer). For most software packages, local installation is best. It gives you the flexibility to control the exact version of a package that you use, and it lets you use different versions of the same package with different applications, so you never break compatibility. (This potential problem becomes magnified when one package depends on the specific version of another package.) However, global installation is useful for certain types of packages, particularly development tools that have command-line utilities. Some examples of packages that are sometimes installed globally include create-react-app (used to create a new React project), http-server (used to run a test web server), typescript (used to compile TypeScript code into JavaScript), and jest (used to run automated tests on your code).

To see all the global npm packages installed on your computer, run this command:

`npm list -g --depth 0`

Here, the --depth parameter makes sure that you only see the top layer of global packages, not the other packages that these global packages use. npm has additional features that we won’t cover here, including the ability to:

  • Designate some dependencies as developer dependencies, meaning they’re required for development but not deployment (like a unit testing tool). You’ll see this technique in Recipes and .

  • Audit your dependencies by searching the npm registry for reports of known vulnerabilities, which it may be able to fix by installing new versions.

  • Run command-line tasks through a bundled utility called npx. You can even launch tasks automatically by adding them to package.json, like prepping your site for production deployment or starting a web server during development testing. You’ll see this technique with the test server in “Setting Up a Local Test Server”.

npm isn’t the only package manager that JavaScript developers use. Yarn is a similar package manager that was initially developed by Facebook. It has a performance edge in some scenarios, due to the way that it downloads packages in parallel and uses caching. Historically, it’s also enforced stricter security checks. There’s no reason not to use Yarn, but npm remains significantly more popular in the JavaScript community.

To learn everything there is to know about npm, you can spend some quality time with the npm developer docs. You can also take a peek at Yarn.

Extra: Understanding package.json

The package.json file is an application configuration file that was introduced with Node, but is now used for a variety of purposes. It stores descriptive information about your project, its creator, and its license, which becomes important if you ever decide to publish your project as a package on npm (a topic covered in “Converting Your Library into a Node Module”). The package.json file also tracks your dependencies (the packages your application uses) and can store extra configuration steps for debugging and deployment.

It’s a good practice to begin by creating a package.json file whenever you start a new project. You can create the file by hand, or using the npm init -y command, which is what we use in the examples in this chapter. Your newly generated file will look something like this (assuming your project folder is named test_site):

{
  "name": "test_site",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

As you may notice, the package.json file uses the JSON (JavaScript Object Notation) format. It holds a comma-separated list of property settings, all wrapped inside {} braces. You can edit package.json in your code editor at any time.

When you install a package with npm, that dependency is recorded in package.json using a property named dependencies. For example, if you install Lodash, the package.json file will look like this:

{
  "name": "test_site",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.20"
  }
}

Don’t confuse package.json with package-lock.json. The package.json file stores basic project settings and lists all the packages you use. The package-lock.json file specifies the exact version and checksum of every package you use (and the version and checksum of each package those packages use). For example, here’s the automatically created package-lock.json file after you install Lodash:

{
  "name": "test-site",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "lodash": {
      "version": "4.17.20",
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
      "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5h
agpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
    }
  }
}

In other words, package-lock.json “locks” your packages to a specific version. This is useful if you’re deploying your project to another computer, and you want to install exactly the same versions of every package that you used during development.

There are two common reasons you might edit your application’s package.json file. First, you might want to add more descriptive details for completeness before you share the project with anyone else. You’ll definitely want to make sure this information is correct if you’re planning to share your package in the npm registry (“Converting Your Library into a Node Module”). Second, you might decide to configure command-line tasks for debugging, like starting a test server (“Setting Up a Local Test Server”). For a complete, property-by-property description of what you can put in package.json, refer to the npm documentation.

Updating a Package with npm

Problem

You want to update an npm package to a newer version.

Solution

For minor updates, use npm update. You can name the specific package you want to update, or ask npm to check for new versions of every package your site uses, and update them all in one fell swoop:

$ npm update

npm will examine the package.json file and update every dependency and subdependency. It will also download any missing packages. Finally, it will update the package-lock.json file to match the new versions.

Discussion

It’s a good practice to regularly update the packages you use. However, not all updates can happen automatically. npm updates follow the rules of semver (semantic versioning). npm will install updates that have greater patch numbers (for example, updating 2.1.2 to 2.1.3) or minor version numbers (2.1.2 to 2.2.0), but it won’t upgrade a dependency if the new release changes the major version number (2.1.2 to 3.0.0). This behavior guards against breaking changes when you update or deploy your application.

You can review what updates are available for all of your dependencies using the npm outdated command:

$ npm outdated

This produces output like this:

Package                Current   Wanted   Latest  Location
-------                -------   ------   ------  --------
eslint                  7.18.0   7.25.0   7.25.0  my-site
eslint-plugin-promise    4.2.1    4.3.1    5.1.0  my-site
lodash                 4.17.20  4.17.21  4.17.21  npm-test

The Wanted column shows available updates that will be installed the next time you run npm update. The Latest column shows the most recent version of the package. In the example above, both lodash and eslint can be updated to the latest package version. But the eslint-plugin-promise package will only be updated to version 4.3.1. The latest version, 5.1.0, changes the major version number, which means that according to the rules of semver it can’t be applied automatically.

Note

This is a slight simplification, because npm gives you the ability to specify versioning policies more specifically in the package.json file. But in practice, this is the way that almost all npm updates will work. For more information about npm versioning, see the npm documentation.

If you want to update a dependency to use a new major version, you need to do it deliberately. Options include editing the package.json file by hand (slightly painful) or using a tool that can do it for you, like npm-check-updates. The npm-check-updates tool allows you to review your dependencies, see what updates are available, and choose to update the package.json file to allow a new major version update. Once you’ve done that, call npm update to download the new version.

Setting Up a Local Test Server

Problem

You want to test your web pages during development, without local security restrictions, and without deploying them to a live web server.

Solution

Install a local test server on your computer. The test server will handle requests and send web pages to your browser, just like a real web server. The only difference is that the test server won’t accept remote connections from other computers.

There are many choices for a test server (see the Discussion section). However, two simple, reliable choices are the http-server and lite-server packages that you can install through npm. We use lite-server here, because it adds a live update feature that automatically refreshes the page in the browser when you save changed code in your editor.

Before you install lite-server, it helps to have a sample web page to request. If you haven’t already done so, make a project folder and configure it with the npm init -y command (“Downloading a Package with npm”). Then, add a file named index.html with a basic content. If you’re in a hurry, here’s a minimal but valid HTML document you can use to test where your code is running:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Test Page</title>
  </head>
  <body>
    <p>This is the index page</p>
    <script>
if (window.location.protocol === 'file:') {
  console.log('Running as local file!');
}
else if (window.location.host.startsWith('localhost')) {
  console.log('Running on a local server');
}
else {
  console.log('Running on a remote web server');
}
    </script>
  </body>
</html>

Now you’re ready to make this document accessible to your browser through a test server.

To install lite-server, use npm with the --save-dev option. That way it’s marked as a developer dependency that won’t be deployed in a production build.

npm install lite-server --save-dev

Now you can run lite-server directly from a terminal window using npm’s package runner, npx:

npx lite-server

This launches lite-server, opens a new browser tab, and requests http://localhost:3000 (where 3000 is whatever port lite-server acquires dynamically). The lite-server attempts to return index.html, or just displays “Cannot GET /” if you don’t have a file with that name. If you used the sample page from this section, you’ll see the “This is the index page” message on the page and “Running on a local server” in the developer console. If you don’t have an index.html page in your test site, you can load up a different page by editing the URL in the address bar (for example, http://localhost:3000/someOtherPage.html).

Now try making some changes. The lite-server instance watches your project folder. Whenever you change a file, it automatically forces the browser to refresh the page. In the terminal, you’ll see a “Reloading Browsers” message whenever this happens.

To end the server, press Ctrl+C at the terminal (Command-C on a Macintosh) and answer Y. Or, close the terminal window (or use the Kill Terminal trashcan icon in VS Code).

Note

Behind the scenes, lite-server uses a popular browser automation tool called BrowserSync to implement its live reloading. The only requirement is that your web page must have a <body> section. (Create a super-simple test page without that detail, and you won’t see the automatic refreshing behavior.)

Discussion

You can save a web page on your local computer, open it in a web browser, and run its code. However, web browsers greatly restrict pages that are opened from the local filesystem. Entire features are unavailable and will fail quietly (like web workers, ES modules, and certain Canvas operations). To avoid hitting these security barriers or—even worse—being confused at why code isn’t working the way you expect, it’s always better to run your web pages from a test web server.

While testing, it’s common to use a development server. There are many options, and your decision will depend somewhat on the other server-side technologies that you plan to use. For example, if you want to run PHP code in your web pages, you’ll need a web server that supports it. If you plan to build part of the backend of your application using JavaScript or a JavaScript-powered server-side framework like Express, you’ll need to use Node.js. But if you’re running web pages with traditional client-side JavaScript, a simple server that sends static files is enough, like http-server or lite-server. There are many more and code editors often have their own plug-in-based test server. For example, if you’re using Visual Studio Code you can search the extension library for the popular Live Server plug-in.

In the Solution section, you saw how to run lite-server with npx. However, a more convenient setup is to make a development run task that automatically starts the server. You can do that by editing the package.json file and adding the following instruction to the scripts section:

{
...
  "scripts": {
    "dev": "lite-server"
  }
}

The scripts section holds executable tasks that you want to run regularly. These might include verifying your code with a linter, checking it into source control, packaging your files for deployment, or running a unit test. You can add as many scripts as you need—for example, it’s common to use one task to run your application, another to test it with an automated testing tool (“Writing Unit Tests for Your Code”), another to prepare it for distribution, and so on. In this example, the script is named dev, which is a convention that identifies a task you plan to use while developing your application.

Once you’ve defined a script in package.json, you can run it with the npm run command at the terminal:

npm run dev

This launches lite-server with npx.

Some code editors have additional support for this configuration detail. For example, if you open the package.json file in VS Code you’ll see that a “Debug” link is added just above the dev setting. Click this link and VS Code opens a new terminal and launches lite-server automatically.

See Also

To learn more about using Node as a test server, see the recipes in Chapter 17. For more information about running tasks with npm, you can read this good overview.

Enforcing Code Standards with a Linter

Problem

You want to standardize your JavaScript code, follow best practices, and avoid common pitfalls that can lead to bugs.

Solution

Check your code with a linter, which warns you when you deviate from the rules you’ve chosen to follow. The most popular JavaScript linter is ESLint.

To use ESLint, you first need npm (see “Installing the npm Package Manager (with Node.js)”). Open a terminal window in your project folder. If you haven’t already created the package.json file, get npm to create it now:

$ npm init -y

Next, install the eslint package using the --save-dev option, because you want ESLint to be a developer dependency that’s installed on developer computers, but not deployed to a production server:

$ npm install eslint --save-dev

If you don’t already have an ESLint configuration file, you need to create one now. Use npx to run the ESLint setup:

$ npx eslint --init

ESLint will ask you a series of questions to assess the type of rules it should enforce. Often, it presents a small menu of choices, and you must use the arrow keys to pick the option you want.

The first question is “How would you like to use ESLint?” Here you have three options, arranged from least strict to most strict:

Check syntax only

Uses ESLint to catch errors. It’s not any stricter than the error-highlighting feature in most code editors.

Check syntax and find problems

Enforces ESLint’s recommended practices (the ones marked with a checkmark). This is an excellent starting point, and you can override individual rules to your preference later on.

Check syntax, find problems, and enforce code style

Is a good choice if you want to use a specific JavaScript style guide, like Airbnb, to enforce a broader set of style conventions. If you choose this option, you’ll be asked to pick the style guide later in the process.

Next, you’ll be asked a series of technical questions: are you using modules, the React or Vue framework, or the TypeScript language? Choose JavaScript modules to get support for the ES6 modules standard described in “Organizing Your JavaScript Classes with Modules”, and choose No for other questions unless you’re using the technology in question.

Next, you’ll be asked “Where does your code run?” Choose Browser for a traditional website with client-side JavaScript code (the usual), or Node if you’re building a server-side application that runs in the Node.js server.

If you’ve chosen to use a style guide, JavaScript will now prompt you to pick one from a small list of choices. It then installs these rules automatically using one or more separate packages, provided you allow it.

Finally, ESLint asks “What format do you want your config file to be in?” All the format choices work equally well. We prefer to use JSON for symmetry with the package.json file, in which case ESList stores its configuration in a file named .eslintrc.json. If you use a JavaScript configuration file, the extension is .js, and if you choose a YAML configuration file, the extension is .yaml.

Here’s what you’ll see in the .eslintrc.json file if you’ve asked ESLint to “check syntax and find problems” without the addition of a separate style guide:

{
  "env": {
      "browser": true,
      "es2021": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
      "ecmaVersion": 12,
      "sourceType": "module"
    },
    "rules": {
  }
}

Now you can ESLint to check your files in the terminal:

npx eslint my-script.js

But a far more practical option is to use a plug-in that integrates ESLint with your code editor. All the code editors introduced in “Choosing a Code Editor” support ESLint, and you can browse the full list of ESLint-supporting plug-ins.

To add ESLint to your code editor, go to its plug-in library. For example, in Visual Studio Code you begin by clicking Extensions in the left panel, and then searching the library for “eslint,” then clicking Install. Once you’ve installed ESLint, you will need to officially allow it through the plug-in’s settings page (or by clicking the lightbulb icon that appears when you open a code file in the editor, and then choosing Allow). You may also need to install ESLint globally across your entire computer so the plug-in can find it:

$ npm install -g eslint

Once ESLint is enabled, you’ll see the squiggly underlines that denote ESLint errors and warnings. Figure 1-4 shows an example where ESLint detects a case in a switch statement that falls through to the next case, which isn’t allowed in ESLint’s standard settings. The “eslint” label in the pop-up identifies that this message is from the ESLint plug-in, not VS Code’s standard error checking.

Note

If ESLint isn’t catching the issues that you expect it to catch, it could be due to another error in your file, possibly even one in a different section of code. Try resolving any outstanding issues, and then recheck your file.

jsc3 0104
Figure 1-4. ESLint flags an error in VS Code

Click Quick Fix (or the lightbulb icon in the margin) to learn more about the problem or attempt a fix (if possible). You can also disable checking for this issue in the current line or file, in which case your override is recorded in a special comment. For example, this disables the rule against declaring variables that you don’t use:

/* eslint-disable no-unused-vars */

If you must override ESLint with comments, it’s probably best to be as targeted and judicious as possible. Instead of disabling checking for an entire file, override it for a single, specific line, like this:

// eslint-disable-next-line no-unused-vars
let futureUseVariable;

or this (replacing eslint-disable-next-line with eslint-disable-line):

let futureUseVariable;  // eslint-disable-line no-unused-vars

If you want to resume checking for the issue, just remove the comment.

Discussion

JavaScript is a permissive language that gives developers a great deal of flexibility. Sometimes this flexibility can lead to problems. For example, it can hide errors or cause ambiguity that makes the code harder to understand. A linter works to prevent these problems by enforcing a range of standards, even if they don’t correspond to outright errors. It flags potential issues in the making, and suspicious practices that don’t trigger your code editor’s error checker but may eventually come back to haunt you.

ESLint is an opinionated linter, which means it flags issues that you may not consider problems, like variables you declare but don’t use, parameter values you change in a function, empty conditional blocks, and regular expressions that include literal spaces (to name just a few). If you want to allow some of these, you have the power to override any of these settings in the ESLint configuration file (or on a file-by-file or line-by-line basis with a comment). But usually you’ll just decide to change your ways to get along, knowing that ESLint’s choices will eventually avoid a future headache.

ESLint also has the ability to correct certain types of errors automatically, and enforce style conventions (like tabs versus spaces, single quotes versus double quotes, brace and indent styles, and so on). Using the ESLint plug-in for an editor like VS Code, you can configure it to perform these corrections automatically when you save your file. Or, you can use ESLint to flag potential problems only, and use a formatter (“Styling Code Consistently with a Formatter”) to enforce code style conventions.

If you work in a team, you may simply receive a preordained ESLint configuration file to use. If not, you need to decide which set of ESLint defaults to follow. You can lean more about ESLint recommended set (used in this recipe), which provides examples of nonconforming code for every issue the ESLint can check. If you want to use a more thorough JavaScript style guide, we recommend the popular Airbnb JavaScript Style Guide, which can be installed automatically with eslint -init.

Styling Code Consistently with a Formatter

Problem

You want to format your JavaScript consistently to improve readability and reduce ambiguity.

Solution

Use the Prettier code formatter to automatically format your code according to the rules you’ve established. Prettier enforces consistency on style details like indentation, use of single and double quotes, spacing inside brackets, spacing for function parameter lists, and the wrapping of long code lines. But unlike a linter (“Enforcing Code Standards with a Linter”), Prettier doesn’t flag these issues for you to fix them. Instead, it applies its formatting automatically every time you save your JavaScript code, HTML markup, or CSS style rules.

Although Prettier exists as a package you can install with npm and use at the command line, it’s much more useful to use a plug-in for your code editor. All the code editors introduced in “Choosing a Code Editor” have a Prettier plug-in. Most of them are listed at the Prettier website.

To add Prettier to your code editor, go to its plug-in library. For example, in Visual Studio Code you click Extensions in the left panel, search the library for “prettier,” and then click Install.

Once you’ve installed Prettier, you’ll be able to use it when you’re editing a code file. Right-click next to your code in the editor and choose Format Document. You can configure the plug-in settings to change a small set of options (like the maximum allowed width before code lines are split, and whether you prefer spaces to tabs).

Tip

In VS Code, you can also configure Prettier to run automatically every time you save a file. To activate this behavior, choose File > Preferences > Settings, go to the Text Editor > Formatting section, and choose Format On Save.

Discussion

Although many code editors have their own automatic formatting features, a code formatter goes beyond these. For example, the Prettier formatter strips away any custom formatting. It parses all the code and reformats it according to the conventions you’ve set, with almost no consideration to how it was originally written. (Blank lines and object literals are the only two exceptions.) This approach guarantees that the same code is always presented in the same way, and that code from different developers is completely consistent. And like a linter, the rules for a code formatter are defined in a configuration file, which means you can easily distribute them to different members of a team, even if they’re using different code editors.

The Prettier formatter takes particular care with line breaks. By default, the maximum line length is set to 80, but Prettier will allows some lines to stretch a bit longer if it avoids a confusing line break. And if a line break is required, Prettier does it intelligently. For example, it would prefer to fit a function call into one line:

myFunction(argA(), argB(), argC());

But if that isn’t practical, it doesn’t just wrap the code however it fits. It chooses the most pleasing arrangement it understands:

myFunction(
  reallyLongArg(),
  omgSoManyParameters(),
  IShouldRefactorThis(),
  isThereSeriouslyAnotherOne()
);

Of course, no matter how intelligent a formatter like Prettier is, you may prefer your own idiosyncratic code arrangements. It’s sometimes said that “Nobody loves what Prettier does to their syntax. Everyone loves what Prettier does to their coworkers’ syntax.” In other words, the value of an aggressive, opinionated formatter like Prettier is the way it unifies different developers, cleans up legacy code, and irons out bizarre habits. And if you decide to use Prettier, you’ll have the unchecked freedom to write your code without thinking about spacing, line breaks, or presentation. In the end, your code will still be converted to the same canonical form.

Tip

If you’re not entirely certain that you want to use a code formatter, or you’re not sure how to configure its settings, spend some time in the Prettier playground to explore how it works.

A linter like ESLint and a formatter like Prettier have some overlap. However, their goals are different and their use is complementary. If you’re using both ESLint and Prettier, you should keep the ESLint rules that catch suspicious coding practices, but disable the ones that enforce formatting conventions about indents, quotes, and spacing. Fortunately, this is easy to do by adding an extra ESLint configuration rule that turns off potential settings that could conflict with Prettier. And the easiest way to do that is by adding the eslint-config-prettier package to your project:

$ npm install --save-dev eslint-config-prettier

Lastly, you need to add prettier to the extends section in your .eslintrc.json file. The extends section will hold a list wrapped in square brackets, and prettier should be at the very end. Here’s an example:

{
  "env": {
      "browser": true,
      "es2021": true
    },
    "extends": ["eslint:recommended", "prettier"],
    "parserOptions": {
      "ecmaVersion": 12,
      "sourceType": "module"
    },
    "rules": {
  }
}

To review the most recent installation instructions, check out the documentation for the eslint-config-prettier package.

Experimenting in a JavaScript Playground

Problem

You want to quickly test or share a code idea without building a project and spinning up your desktop code editor.

Solution

Use a JavaScript playground, which is a website where you can edit and run JavaScript code. There are well over a dozen JavaScript playgrounds, but Table 1-4 lists five of the most popular.

Table 1-4. JavaScript playgrounds
Website Notes

JSFiddle

Arguably the first JavaScript playground, JSFiddle is still at the forefront with features for simulating asynchronous calls and GitHub integration.

JS Bin

A classic playground with a simple tab-based interface that lets you pop different sections (JavaScript, HTML, CSS) into view one at a time. The code for JS Bin is also available as an open source project.

CodePen

One of the more attractively designed playgrounds, with an emphasis on the social (popular examples are promoted in the CodePen community). Its polished interface is particularly suitable for novice users.

CodeSandbox

One of the newer playgrounds, it uses an IDE-like layout that feels a lot like a web-hosted version of Visual Studio Code.

Glitch

Another IDE-in-a-browser, Glitch is notable for its VS Code plug-in, which lets you switch between editing in a browser playground or using your desktop editor on the same project.

All these JavaScript playgrounds are powerful, practical choices. They all work similarly, although they can look strikingly different. For example, compare the dense developer cockpit of JSFiddle (Figure 1-5) to the more spaced-out editor in CodePen (Figure 1-6).

jsc3 0105
Figure 1-5. The JavaScript playground JSFiddle
jsc3 0106
Figure 1-6. A simple example in CodePen

Here’s how you use a JavaScript playground. When you visit the site, you can start coding immediately at a blank page. Even though your JavaScript, HTML, and CSS are presented separately, you don’t need to explicitly add a <script> element to connect your JavaScript or a <link> element for your style sheet. These details are already filled into the markup of your page or, more commonly, are an implicit part of boilerplate that’s hidden behind the scenes.

All JavaScript playgrounds let you see the page you’re working on beside your code window. In some (like CodePen), the preview is refreshed automatically as you make changes. In others (like JSFiddle), you need to explicitly click a Play or Run button to reload your page. If you write messages with console.log(), some JavaScript playgrounds send that directly to the browser console (like CodePen), while others can also show it in a dedicated panel that’s visible on the page (like JSFiddle).

When you’re finished you can save your work, at which point you receive a newly generated, shareable link. However, it’s a better idea to sign up for an account first, so you’re able to return to the JavaScript playground, find all the examples you’ve created, and edit them. (If you save an example anonymously, you can’t edit it, although you can use it as a starting point to build another example.) All the playgrounds listed in Table 1-4 let you create an account and save your work for free.

Note

The exact terminology for the kind of example you create in a JavaScript playground varies based on the site. It might be called a fiddle, a pen, a snippet, or something else.

Discussion

JavaScript playgrounds are a useful idea that’s been picked up by more than a dozen websites. Almost all of them share some important characteristics:

  • They’re free to use. However, many have a subscription option for premium features, like being able to save your work and keep it private.

  • You can save your work indefinitely. This is particularly handy if you want to share a quick mock-up or collaborate on a new experiment with others.

  • They support a wide range of popular JavaScript libraries and frameworks. For example, you can quickly add Lodash, React, or jQuery to your example, just by picking it from a list.

  • You can edit HTML, JavaScript, and CSS all in one window. Depending on the playground, it may be divided into panels that are all visible at once (like JSFiddle) or tabs that you switch between (like JS Bin). Or, it may be customizable (like CodePen).

  • They provide some level of autocompletion, error checking, and syntax highlighting (colorizing different code ingredients), although it’s not as complete as what you’ll get in a desktop code editor.

  • They provide a preview of your page so you can jump easily between coding and testing.

JavaScript playgrounds also have limits. For example, you may not be able to host other resources like images, interact with backend services like databases, or use asynchronous requests with fetch.

JavaScript playgrounds should also be distinguished from full cloud-based programming environments. For example, you can use VS Code online in a completely hosted environment called GitHub Codespaces, or AWS Cloud9 from Amazon, or Google Cloud. None of these products are free, but all are appealing if you want to set up a specific development environment that you can use in your browser, on different devices, and with no setup or performance concerns.

Get JavaScript Cookbook, 3rd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.