David Else TypeScript/JavaScript software developer | elsewebdevelopment.com

Deno 1.0: What you need to know

13 min read 3883

Deno 1.0: What You Need to Know

After almost two years, the wait is nearly over. The API has been frozen, and the countdown to Deno 1.0, officially scheduled for release on May 13, has begun.

Due to its famous creator and forward-thinking vision, Deno is sure to be the most exciting and controversial JavaScript-related release in recent memory.

Deno is a general-purpose JavaScript/TypeScript programming environment. It brings together many of the best open-source technologies and offers a comprehensive solution in one small executable file.

Created by Ryan Dahl, best known as the mastermind behind Node.js, Deno takes advantage of the JavaScript features that have been available since Node.js was released back in 2009. It also addresses the design flaws that Ryan talked about in his “10 Things I Regret About Node.js” lecture. Some are calling it the sequel to Node.js, although the author himself makes no such claim.

Unlike Node.js, which was written in C++, Deno is written in Rust. It builds on top of the Tokio platform and, like Node.js, executes JavaScript using the V8 engine. One outstanding feature is that TypeScript is built in. While it still needs compiling into JavaScript to run, it’s done internally, so TypeScript behaves to the user as if it were natively supported.

Getting started

To download Deno, follow the instructions on the homepage. To update to future versions, use deno upgrade.

To get help on any Deno subcommand, use either of the following.

  • deno [subcommand] -h for a summary
  • deno [subcommand] --help for full details

In this guide, we’ll cover all the killer features Deno 1.0 has to offer and provide examples of how to use them with the latest syntax. I’ll use TypeScript where possible, but the JavaScript equivalent should be obvious.

I’m convinced that you’ll fall in love with Deno by the time we’re done. This guide should give you everything you need to get started.

Security

Deno is secure by default. By comparison, Node.js has full access to your file system and network.

We made a custom demo for .
No really. Click here to check it out.

To run a program with no permissions, use:

deno run file-needing-to-run-a-subprocess.ts

If the code needs a permission setting, you’ll be alerted.

error: Uncaught PermissionDenied: access to run a subprocess, run again with the --allow-run flag

Deno uses command-line options to explicitly allow access to different parts of the system. The most commonly used include:

  • environment access
  • network access
  • file system read/write access
  • running a subprocess

To see a full list of permissions with examples, type deno run -h.

It’s a best practice to use permission whitelists for read, write, and net. These allow you to be even more specific about what Deno is allowed to access. For example, to allow Deno to read-only files within the /etc directory, use:

deno --allow-read=/etc

Shortcuts for using permissions

You may soon tire of explicitly enabling permissions every time you run your application. To get around this, you can take any of the following approaches.

1. Allow all permissions

You can enable all permissions using --allow-all or its shortcut -A. This is not recommended because it removes the security advantage of having specific permissions.

2. Make a bash script

Create a bash script with the minimum amount of permissions needed to run the program.

#!/bin/bash

// Allow running subprocesses and file system write access
deno run --allow-run --allow-write mod.ts

The disadvantage here is that you’ll probably need a few of them for things like run, test, and bundle.

3. Use a task runner

You can use the GNU tool make to create one file with a set of Deno commands, complete with permissions. You can also use the Deno-specific version, Drake.

4. Install an executable Deno program

Use deno install to install a Deno program complete with all the permissions it needs to execute. Once installed, you can access the program from anywhere in the $PATH.

The standard library

The Deno standard library is a collection of commonly used modules that are maintained by the Deno project and guaranteed to work with Deno. It covers code that users will most often need for common tasks and is based loosely on the standard library provided by the Go programming language.

JavaScript has always been plagued by its lack of a standard library. Users have been forced to reinvent the wheel again and again, and developers must often search npm for third-party modules to solve common problems that the makers of the platform should provide.

Third-party packages for complex problems solved by libraries like React are a good thing, but for simple things such as UUID generation, it’s far better to use an official standard library. These small libraries can serve as building blocks for larger libraries, making development faster and less anxiety-inducing. How many times has a once-popular library been abandoned, leaving the user to maintain it themselves or find a new one? In fact, between 10 and 20 percent of commonly-in-use OSS packages aren’t actively maintained.

Available modules and their npm equivalents

Deno Module Description npm Equivalents
colors Adds color to the terminal chalk, kleur, and colors
datetime Helps working with the JavaScript Date object
encoding Adds support for external data scructures like base32, binary, csv, toml and yaml
flags Helps working with command line arguments minimist
fs Helps with manipulation of the file system
http Allows serving local files over HTTP http-server
log Used for creating logs winston
testing For unit testing assertion and benchmarking chai
uuid UUID generation uuid
ws Helps with creating WebSocket client/server ws

Typescript is built into Deno

TypeScript LogoTypeScript is JavaScript but with added explicit types. Any valid JavaScript is also valid TypeScript, so converting your code to TypeScript is painless. Just change the extension to .ts and start adding the types.

To use TypeScript in Deno, there is nothing you need to do. Without Deno, TypeScript has to be compiled into JavaScript to run. Deno does this internally for you, making TypeScript even easier to adopt.

Using your own tsconfig.json

For those familiar with TypeScript, you’ll be used to having a tsconfig.json file to supply the compiler options. This is optional when you are using Deno because it already has its own default configuration. If you use your own tsconfig.json and it conflicts with Deno, you will be alerted.

This feature requires the -c option and your tsconfig.json.

deno run -c tsconfig.json [file-to-run.ts]

If you’re like most developers, you’ll be overjoyed to learn that Deno uses strict mode by default. Unless some miscreant overrides it, Deno will justifiably alert the user about as many sloppy coding practices as it can.

Deno uses web standards where possible

w3 LogoIt takes a long time to create a web standard, and once it’s set in stone, it’s unwise to ignore it. While frameworks come and go, web standards will remain. Time spent invested in learning a standardized API is never wasted because nobody dares break the web; it could well be in use for decades, maybe even the rest of your career.

The fetch web API provides an interface for fetching resources. There is a JavaScript fetch() method that is available in the browser. If you want to use this standard in Node.js, you’ll need to reach for the third party library Node Fetch. In Deno, it is built in and works just like the browser version, right out of the box.

Deno 1.0 provides the following web-compatible APIs.

  • addEventListener
  • atob
  • btoa
  • clearInterval
  • clearTimeout
  • dispatchEvent
  • fetch
  • queueMicrotask
  • removeEventListener
  • setInterval
  • setTimeout
  • AbortSignal
  • Blob
  • File
  • FormData
  • Headers
  • ReadableStream
  • Request
  • Response
  • URL
  • URLSearchParams
  • console
  • isConsoleInstance
  • location
  • onload
  • onunload
  • self
  • window
  • AbortController
  • CustomEvent
  • DOMException
  • ErrorEvent
  • Event
  • EventTarget
  • MessageEvent
  • TextDecoder
  • TextEncoder
  • Worker
  • ImportMeta
  • Location

These are all available on the top-level scope of your program. It means that if you avoid using any methods on the Deno() namespace, your code should be compatible with both Deno and the browser. While not all of these Deno APIs are 100 percent compliant with their equivalent web specification, this is still a massive bonus for frontend developers.

ECMAScript modules

One of the main breaking changes in Deno from Node.js is that Deno uses the official ECMAScript module standard rather than legacy CommonJS. It took Node.js until the end of 2019 to enable ECMAScript modules with version 13.2.0, but even then, support was half-baked and it still included the controversial .mjs file extension.

Deno breaks free of the past by using modern web standards for its module system. The module is referenced using a URL or file path and includes a mandatory file extension. For example:

import * as log from "https://deno.land/std/log/mod.ts";
import { outputToConsole } from "./view.ts";

The problem with using file extensions

Deno expects modules to have file extensions, but TypeScript does not.

TypeScript Extension Problem

Using a file extension everywhere is logical and seems like the obvious way to go. In reality, unfortunately, things are more complex than that. For now, you can use the Visual Studio Code Extension to solve this for Deno-only projects.

The problem seems controversial for the creators of TypeScript. Until we can finally ditch CommonJS, I don’t see a quick and easy solution.

Let’s take a moment to pray to the wise and ancient gods of programming. Let them strike down these legacy formats and punish those who hold onto them to the detriment of us all.

Package management

Dog Sitting in Front of Cardboard BoxesThere has been a radical rethink regarding the way package management works in Deno. Rather than relying on a central repository, it is decentralized. Anyone can host a package just like anyone can host any type of file on the web.

There are advantages and disadvantages to using a centralized repository like npm, and this aspect of Deno is sure to be the most controversial.

How Deno’s new package management works

It’s so radically simplified that it may shock you.

import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

Let’s break down the changes.

  • There is no more centralized package manager. You import ECMAScript modules directly from the web
  • There is no more “magical” Node.js module resolution. Now, the syntax is explicit, which makes things much easier to reason about
  • There is no more node_modules directory. Instead, the dependencies are downloaded and hidden away on your hard drive, out of sight. If you want to refresh the cache and download them again, just add --reload to your command

If you want to download dependencies alongside project code instead of using a global cache, use the $DENO_DIR env variable.

Finding compatible third-party libraries

There is a user area for Deno-compatible third-party modules, but the navigation is very basic at the time of writing. For example, there is no facility to search by popularity or number of downloads. I predict that the user area will either expand or other alternative sites will be created for contributed modules.

Although there is no official support for backward compatibility with Node.js, there are still many libraries and applications available that will work fine with Deno. Some will out of the box, while others require a little effort to get working.

Type of library Compatibility
  • Runs in the browser
  • Uses ESM syntax
Confirm compatibility with the Pika package catalog and use NPM or the Pika CDN
  • Runs in the browser
  • Uses CommonJS syntax
Use jspm.io to wrap modules in ESM syntax or the Pika CDN
  • Doesn’t run in the browser
  • Doesn’t use Node.js APIs
Use jspm.io to wrap modules in ESM syntax or the Pika CDN
  • Uses Node.js APIs
This probably won’t work, but try the official compatibility layer for the NodeJS standard library

Installing third-party modules

Deno is still very new and the surrounding ecosystem is still forming. At the time of writing, I recommend Pika as the first place to start looking for compatible modules, after the standard and user libraries. Any package available on NPM should also be on the Pika CDN and have been automatically converted to work with import.

The developers behind Pika have worked with Deno to provide TypeScript types via ECMAScript modules called X-TypeScript-Types. You can take advantage of this by simply using their CDN platform.

Going beyond Package.Json

Most of the JavaScript ecosystem still revolves around using package.json. It has been bloated out to include many responsibilities, such as:

  • Holding metadata about the project
  • Listing project dependencies with versioning
  • Categorizing dependencies as either dependencies or devDependencies
  • Defining the entry point of the program
  • Storing shell scripts related to the project
  • Defining a type category, recently introduced to improve ECMAScript module support
{
  "name": "Project Name", // metadata
  "version": "1.0.0", // metadata
  "description": "My application", // metadata
  "type": "module", // module functionality
  "main": "src/mod.ts", // module functionality
  "scripts": {
    "build": "npm run _copy-build-files && rollup -c",
    "build-watch": "npm run _copy-build-files && rollup -cw"
  }, // scripting functionality
  "license": "gpl-3.0", // metadata
  "devDependencies": {
    "@rollup/plugin-typescript": "^3.1.1",
    "rollup": "^1.32.1",
    "typescript": "^3.8.3"
  }, // versioning and categorizing functionality
  "dependencies": {
    "tplant": "^2.3.3"
  } // versioning and categorizing functionality
}

All these practices have come together over time and now represent the standard way in which the JavaScript ecosystem works. It’s easy to forget that this isn’t an official standard; it was only conjured up as these features became a necessity. Now that JavaScript has caught up, it’s time for a major rethink.

Deno can’t yet replace all the functionality of package.json, but there are some current solutions.

Using deps.ts and URLs for versioning

There is a Deno convention for package versioning, and that is to use a special file called deps.ts. Inside, the dependencies are re-exported. This allows different modules in the application to all refer to the same source.

Rather than telling npm which version of a module to download, it is referenced in the URL in deps.ts.

export { assert } from "https://deno.land/std@v0.39.0/testing/asserts.ts";
export { green, bold } from "https://deno.land/std@v0.39.0/fmt/colors.ts";

If you want to update any modules, you can change the URLs in deps.ts. For example, replace @v0.39.0 with @v0.41.0 and the new version will be used everywhere. If you instead imported https://deno.land/std@v0.39.0/fmt/colors.ts directly into each module, you would have to painstakingly go through the entire application and change each reference.

It would be a security risk to assume that a module you downloaded before could not have been tampered with afterward. That’s why there is also an option to create a lock file. This will ensure that the newly downloaded module is identical to the one you originally downloaded.

deno doc and using JSDoc for metadata

JSDoc was released in 1999, 21 years ago. It is now the most-used and supported way to document JavaScript and TypeScript. While not an official web standard, it’s a perfect replacement for all that metadata in your package.json.

/**
 * @file Manages the configuration settings for the widget
 * @author Lucio Fulci
 * @copyright 2020 Intervision
 * @license gpl-3.0
 * @version 1.0
 *

Deno supports JSDoc out of the box and uses it for its built-in documentation system. While it does not currently use the metadata above, the command deno doc reads a function’s description and descriptions of its parameters.

/**
 * Returns a value of (true?) if the rule is to be included
 *
 * @param key Current key name of rule being checked
 * @param val Current value of rule being checked
 **/

You can use deno doc <filename> to see the documentation of your program.

deno doc mod.ts

function rulesToRemove(key: string, val: any[]): boolean
  Returns a value of if the rule is to be included

When your program is hosted online, use the online documentation viewer to see it in greater detail.

Deno’s built-in tooling

Woman Screaming Surrounded by Software Logos

This is the area that will have the greatest impact on frontend developers. The current state of JavaScript tooling is overwhelming chaos. When you add in TypeScript tooling, the complexity increases even further.

One of the best things about JavaScript is that it does not require compiling, so it can be run immediately in the browser. This makes it very easy to get feedback about your coding right away. There is a very low barrier to entry; all you need to write software is a text editor and a browser.

Unfortunately, this simplicity and accessibility have been undermined by what could be described as a cult of excessive tooling. It has turned JavaScript development into a nightmare of complexity. I’ve even seen an entire course devoted to configuring Webpack. This nonsense needs to end — life is too short.

The tooling chaos has grown to the point that many developers are desperate to get back to actually writing code rather than playing around with config files and agonizing over which of the multiple competing standards they should adopt. One emerging project that addresses this is Facebook’s Rome. At the time of writing, this is in its infancy. While it could prove beneficial, Deno has the potential to be a much more substantial solution.

Deno is an entire ecosystem in itself, complete with runtime and its own module/package managing system. This gives it a much greater scope to have all its own tooling built in. Let’s examine what tooling is available in 1.0 and how you can use it to reduce reliance on third-party libraries and simplify development.

It’s not yet possible to replace an entire frontend build pipeline in Deno, but it won’t be long until you can.

Testing

The test runner is built into the core of Deno using the Deno.test() function. The assertion library is provided in the standard library. All your favorites, such as assertEquals() and assertStrictEq(), are included, along with some less common assertions such as assertThrowsAsync().

At the time of writing, there is no test coverage feature, and the watch mode needs to be set up using third-party tooling such as Denon.

To see all the test runner options, use deno test --help. While they are quite limited, there are many features you may be familiar with from programs like Mocha. For example, --failfast will stop on the first error encountered, and --filter can be used to filter which tests to run.

Using the test runner

The most basic syntax is deno test. This will run all files in the working directory that end in _test or .test with the extension .js, .ts, .jsx, or .tsx (e.g., example_test.ts)

import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

Deno.test({
  name: "testing example",
  fn(): void {
    assertEquals("world", "world");
    assertEquals({ hello: "world" }, { hello: "world" });
  },
});

If your code uses the DOM, you will need to supply your own tsconfig.json with lib: ["dom", "esnext"]. We’ll go into more detail below.

Formatting

Formatting is provided by dprint, a blazing-fast alternative to Prettier that clones all the established Prettier 2.0 rules.

To format a file or files, use either deno fmt <files> or the Visual Studio Code extension (more on this later).

Compiling and bundling

Deno can create a simple bundle from the command line using deno bundle, but it also exposes an internal compiler API so the user can create their own output, something that can be customized for frontend use. This API is currently marked as unstable, so you need to use the --unstable flag.

While Deno has some web-compatible APIs, they are not complete. If you want to compile any frontend TypeScript that references the DOM, you need to tell Deno about these types when compiling or bundling. You can use the compiler API option lib.

index.html
<!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>
    <h1 id="greeter">Replace me</h1>
  </body>
</html>
test-dom.ts
let greeter: HTMLElement | null = document.getElementById("greeter")!; // Please forgive the Non-Null Assertion Operator

greeter.innerText = "Hello world!";
compile.ts
const [errors, emitted] = await Deno.compile("test-dom.ts", undefined, {
  lib: ["dom", "esnext"], // include "deno.ns" for deno namespace
  outDir: "dist",
});

if (errors) {
  console.log("There was an error:");
  console.error(errors);
} else {
  console.log(emitted); // normally we would write the file
}

Below is the resulting emit map output in the terminal.

{
 dist/test-dom.js.map: "{"version":3,"file":"test-dom.js","sourceRoot":"","sources":["file:///home/david/Downloads/deno-arti...",
 dist/test-dom.js: ""use strict";nlet greeter = document.getElementById("greeter");ngreeter.innerText = "Hello world!";n..."
}

In the example above, we compiled the test-dom.ts file that references the DOM. Using the lib option in Deno.compile() overrides any lib default option used by Deno, so you need to add back esnext and, optionally, deno.ns to use the Deno namespace.

This is all still a bit experimental, but I’m hoping the bundle command will evolve to take care of things like tree shaking and act more like Rollup.js.

Debugging

Deno has built-in debugging, but at the time of writing, the Visual Studio Code extension does not support it. To debug, manually use the following.

  • deno run -A --inspect-brk fileToDebug.ts (Note: Use minimum permissions for your module)
  • Open chrome://inspect in Chrome or Chromium. You’ll see a similar screen to the one below
    Inspect With Chrome
  • Click “inspect” to connect and start debugging your code

File watching

Deno has file watching built in using the Rust notify library via the Deno.watchFs() API. Deno likes to provide the heavy lifting behind the scenes with its APIs and let the user implement their code how they like. Rather than supplying a --watch flag, you’ll need to create your own implementation or use a third-party module.

The only nontrivial aspect of making your own file watcher is the debouncing. The API can trigger multiple events in quick succession, and you probably don’t want to run your action multiple times. User Caesar2011 solved the problem in 23 lines of TypeScript using Date.now().

There is also a more advanced Deno file watching solution called Denon. It’s the equivalent of nodemon. If you would like to watch your workspace for changes and rerun your tests, it’s as easy as:

denon test

Visual Studio Code plugin

The best extension by far is axetroy’s, available from Visual Studio Market Place. After installation, create a file .vscode/settings.json in your project folder and enable the extension on a per-project basis.

// .vscode/settings.json
{
  "deno.enable": true,
}

You will now have access to full IntelliSense support and everything you need to get coding.

Conclusion

The fast rate of change in the JavaScript ecosystem has proven to be a mixed blessing. On the positive side, there have never been more high-quality tools available. On the negative side, there is a general sense of cynicism and weariness around the neverending barrage of new frameworks and libraries thrown at us.

Deno successfully removes many of the drawbacks from JavaScript development. Below are just a few.

  • By using web standards, Deno future-proofs its APIs. This gives developers confidence that they are not wasting their time learning something that will soon be obsolete
  • Having TypeScript in addition to JavaScript removes the burden of compilation and allows tighter integration
  • Built-in tooling means there is no need to waste time searching for something that is provided out of the box
  • Decentralized package management liberates users from npm, and ECMAScript modules bring a breath of fresh air compared to using decrepit CommonJS

While it may not completely replace Node.js just yet, Deno is already a fantastic programming environment for daily use.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
David Else TypeScript/JavaScript software developer | elsewebdevelopment.com

9 Replies to “Deno 1.0: What you need to know”

  1. Great article, which let Deno newer to learn about Deno very well.

    Can I translate it into Chinese and reprint it to the Chinese community?

    I will keep the Author, URL and your Plug while following the original meaning as much as possible

    Thx for your great job!

    1. Thanks for the support. For now, our policy is not to approve translations on third-party sites.

  2. Thanks for writing this, very informative. Regarding to the “web standards” section, is there a source/link I can reference of that “web-compatible APIs” list?

  3. Great work! I’ve been meaning to learn a little about Deno and this was the perfect amount of information to understand what it is and how its different from Node.js.

  4. If you type `deno doc` on the command line you will see documentation for all runtime built-ins, or for a specific one use `deno doc –builtin`, for example: `deno doc –builtin fetch`.

    There is no official list I am aware of for only the web-compatible APIs.

  5. What is the solution for more complex dependency trees? Different versions of the same module required by different (sub-sub) branches of the dep tree? multiple imports of the same module in different distant dep tree branches? npm5+ does some good job with tree flattening, There is A LOT of science behind the dependencies resolution, and from mvn through npm up to crates and competing go systems it is continuous discovery proces, where people try to respond to the one question: how to do it right?!?!?! Can you point me to any docs on the solution you decided to follow?

Leave a Reply