Written by David Else✏️
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.
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 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]
See the Deno manual for full details of the default tsconfig.json
settings.
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
It 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.
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
There 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 |
|
Confirm compatibility with the Pika package catalog and use NPM or the Pika CDN |
|
Use jspm.io to wrap modules in ESM syntax or the Pika CDN |
|
Use jspm.io to wrap modules in ESM syntax or the Pika CDN |
|
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.
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
ordevDependencies
- 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
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 - 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.
Plug: LogRocket, a DVR for web apps
LogRocket is a frontend logging tool 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.
Try it for free.
The post Deno 1.0: What you need to know appeared first on LogRocket Blog.
Top comments (0)