Deno 1.19 Release Notes
Deno 1.19 has been tagged and released with the following new features and changes:
deno vendor
- Permission prompt by default
- Files, network sockets, and stdio are now native web streams
CompressionStream
andDecompressionStream
are now supported- Better errors for ops and resource sanitizers in
Deno.test
console.log
now displays circular references in logged objectsDeno.File
has been renamed toDeno.FsFile
deno compile
now works more reliably- Allow disabling clear screen on file watcher restarts
deno coverage
gets--output
flag- Stabilization of signal listener APIs
- Serve HTTP over Unix sockets
- New unstable API:
Deno.Conn#setNoDelay()
andDeno.Conn#setKeepAlive()
- New unstable API:
Deno.getUid
- New unstable API:
Deno.networkInterfaces
- Improvements to the LSP
- V8 9.9
If you already have Deno installed, you can upgrade to 1.19 by running:
deno upgrade
If you are installing Deno for the first time, you can use one of the methods listed below:
# Using Shell (macOS and Linux):
curl -fsSL https://deno.land/x/install/install.sh | sh
# Using PowerShell (Windows):
iwr https://deno.land/x/install/install.ps1 -useb | iex
# Using Homebrew (macOS):
brew install deno
# Using Scoop (Windows):
scoop install deno
# Using Chocolatey (Windows):
choco install deno
New features and changes
deno vendor
Over the last couple of months we have received feedback that users would like to be able to vendor their program’s dependencies into their code repositories. This is a useful feature, if you want make sure that only some very specific code gets executed by your application.
Previously, we have recommended that users check in their DENO_DIR
into the
repository to do vendoring. While this generally works, it is not a great user
experience. The files in DENO_DIR
have non intuitive names made up of hex
strings that don’t look good in version control.
In this release we are introducing an improved method to vendor dependencies:
deno vendor
. This subcommand can be invoked with one or more entrypoints to
the modules to be vendored. Deno will then build a module graph from these files
by analyzing all of the imports and exports of the modules. The resulting list
of modules is then written to the vendor/
folder with names that as closely
resemble the original module names as possible. Sometimes we have to strip or
append some characters to the name to make the specifier a valid file system
path. Inside of the vendor folder we then generate an import map
that maps all of the remote modules that were vendored to the local vendor
directory.
To then use the vendored dependencies in your program, you just add
--import-map=vendor/import_map.json
to your Deno invocations. You can also add
--no-remote
to your invocation to completely disable fetching of remote
modules (only allow importing modules in the vendor directory).
$ deno vendor main.ts
$ tree
.
├── main.ts
└── vendor
├── deno.land
├── import_map.json
└── raw.githubusercontent.com
$ deno run --no-remote --import-map=vendor/import_map.json main.ts
The vendor directory should be checked into version control. The file names stay
as consistent as possible across runs of deno vendor
to minimize bloated git
diffs.
One other great use case for this feature is to allow you to temporarily add
some console.log
entries or similar to dependency code while debugging. To do
this you can just vendor the specific module you want to modify, and then edit
that source code in the vendor directory. Once you are done debugging, you can
just remove the vendor directory again and continue as normal.
We’d love to hear your feedback on this feature. We are aware of a few issues that we’ll iron out over the next few weeks, but it is working well in most cases.
Permission prompt by default
One complaint is that Deno requires so many command-line flags. Often these
flags are --allow-*
permission allowances. Before this version, Deno would
throw an exception if a permission check fails, requiring users to specify these
flags each time. Now in Deno 1.19, if access is not granted, a command-line
prompt will allow the user to interactively accept or deny each individual
access check.
# deno run https://deno.land/std/http/file_server.ts
⚠️ ️Deno requests read access to <CWD>. Run again with --allow-read to bypass this prompt.
Allow? [y/n (y = yes allow, n = no deny)] y
⚠️ ️Deno requests net access to "0.0.0.0:4507". Run again with --allow-net to bypass this prompt.
Allow? [y/n (y = yes allow, n = no deny)] y
HTTP server listening on http://localhost:4507/
This feature has long been available by using the --prompt
flag, but is now on
by default. To disable the prompt, use --no-prompt
. These permission prompts
will only happen if you’re connected to a TTY, so one shouldn’t need to supply
--no-prompt
in, for example, CI scripts.
Files, network sockets, and stdio are now native web streams
The Deno.FsFile
and Deno.Conn
interfaces now have readable
and writable
properties of type ReadableStream
and WritableStream
respectively. This
makes them integrate with other web APIs that use web streams very nicely. Some
examples:
// Download a file from the web, and stream it into a file on disk.
const file = await Deno.create("./example.html");
const resp = await fetch("https://example.com");
await resp.body.pipeTo(file.writable);
// Read from stdin, and stream it to a remote server (streaming upload).
// Would be used like this: `cat file.txt | deno run --allow-net=example.com main.ts`
const resp = await fetch("https://example.com", {
method: "POST",
body: Deno.stdin.readable,
});
console.log("Upload succeeded?", resp.ok);
Because all of the APIs in Deno now support web streams out of the box, including our native HTTP server, composing all these APIs together is now very simple:
import { serve } from "https://deno.land/std/http/server.ts";
serve(async (req) => {
if (req.method === "POST") {
// Save the incoming request body to a file on disk
const path = await Deno.makeTempFile();
const file = await Deno.create(path);
await req.body.pipeTo(file.writable);
return new Response(`Saved file to ${path}`);
} else {
// Serve a file from disk
const path = "./example.html";
const file = await Deno.open(path);
return new Response(file.readable, {
headers: { "content-type": "text/html" },
});
}
});
The new readable
and writable
properties can also be composed with built in
stream transformers like TextEncoderStream
or CompressionStream
. Here is an
example of a program that reads from stdin, performs gzip compression, and then
writes the result to stdout:
await Deno.stdin.readable
.pipeThrough(new CompressionStream("gzip"))
.pipeTo(Deno.stdout.writable);
CompressionStream
and DecompressionStream
are now supported
This release is adding two new built-in stream transformers called
CompressionStream
and DecompressionStream
. This web standard API allows you
to compress and decompress data in multiple file formats (currently gzip
and
deflate
).
The API has already shipped in Chrome, and is hopefully coming to other browsers soon.
Here is an example of performing streaming decompressing a .gz
file:
const input = await Deno.open("./file.txt.gz");
const output = await Deno.create("./file.txt");
await input.readable
.pipeThrough(new DecompressionStream("gzip"))
.pipeTo(output.writable);
We’re currently working with the web standards groups to add support for the
brotli
compression algorithm to the CompressionStream
and
DecompressionStream
APIs. You can follow
this issue for updates.
Deno.test
Better errors for ops and resource sanitizers in Since the first stable release of deno test
it has had resource and op
sanitizers. These sanitizers check that a test does not leak resources (like
open file handles) or async ops (like timers). This is done because leaking
resources or async ops can often cause hard to debug test failures or flakes,
and they often point to logic errors in the code.
This release completely overhauls the error messages from these sanitizers to
make them more usable, and easier to understand for new users. Take this test
that leaks a Deno.FsFile
resource, for example:
Deno.test("leaky", () => {
const file = Deno.openSync("/etc/passwd");
});
It would have previously failed with this error:
running 1 test from ./test.ts
test leaky ... FAILED (4ms)
failures:
leaky
AssertionError: Test case is leaking resources.
Before: {
"0": "stdin",
"1": "stdout",
"2": "stderr"
}
After: {
"0": "stdin",
"1": "stdout",
"2": "stderr",
"3": "fsFile"
}
Make sure to close all open resource handles returned from Deno APIs before
finishing test case.
at assert (deno:runtime/js/06_util.js:41:13)
at resourceSanitizer (deno:runtime/js/40_testing.js:153:7)
at async Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:169:9)
at async runTest (deno:runtime/js/40_testing.js:427:7)
at async Object.runTests (deno:runtime/js/40_testing.js:540:22)
failures:
leaky
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (11ms)
It now fails with a much more concise and useful message:
running 1 test from ./test.ts
test leaky ... FAILED (4ms)
failures:
leaky
AssertionError: Test case is leaking 1 resource:
- A file (rid 3) was opened during the test, but not closed during the test. Close the file handle by calling `file.close()`.
at assert (deno:runtime/js/06_util.js:46:13)
at resourceSanitizer (deno:runtime/js/40_testing.js:313:7)
at async Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:329:9)
at async runTest (deno:runtime/js/40_testing.js:587:7)
at async Object.runTests (deno:runtime/js/40_testing.js:700:22)
failures:
leaky
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (10ms)
The same treatment has also been applied to async op sanitizer errors. This test leaks a sleep operation:
Deno.test("leaky", () => {
setTimeout(() => {}, 1000);
});
Previously it would have failed with this error:
running 1 test from ./test.ts
test leaky ... FAILED (4ms)
failures:
leaky
AssertionError: Test case is leaking async ops.
Before:
- dispatched: 0
- completed: 0
After:
- dispatched: 2
- completed: 1
Ops:
op_sleep:
Before:
- dispatched: 0
- completed: 0
After:
- dispatched: 2
- completed: 1
Make sure to await all promises returned from Deno APIs before
finishing test case.
at assert (deno:runtime/js/06_util.js:41:13)
at asyncOpSanitizer (deno:runtime/js/40_testing.js:121:7)
at async resourceSanitizer (deno:runtime/js/40_testing.js:137:7)
at async Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:169:9)
at async runTest (deno:runtime/js/40_testing.js:427:7)
at async Object.runTests (deno:runtime/js/40_testing.js:540:22)
failures:
leaky
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (9ms)
The new error has a helpful hint about what causes this problem, and it even includes a stack trace of where the async operation was started so you don’t have to manually try to find it with console logs:
running 1 test from ./test.ts
test leaky ... FAILED (8ms)
failures:
leaky
Test case is leaking async ops.
- 1 async operation to sleep for a duration was started in this test, but never completed. This is often caused by not cancelling a `setTimeout` or `setInterval` call. The operation was started here:
at Object.opAsync (deno:core/01_core.js:155:42)
at runAfterTimeout (deno:ext/timers/01_timers.js:234:31)
at initializeTimer (deno:ext/timers/01_timers.js:200:5)
at setTimeout (deno:ext/timers/01_timers.js:337:12)
at ./test.ts:2:3
at testStepSanitizer (deno:runtime/js/40_testing.js:432:13)
at asyncOpSanitizer (deno:runtime/js/40_testing.js:145:15)
at resourceSanitizer (deno:runtime/js/40_testing.js:360:13)
at Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:415:15)
at runTest (deno:runtime/js/40_testing.js:673:18)
failures:
leaky
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (17ms)
console.log
now displays circular references in logged objects
Previously inspection of circular objects would only tell you that there is some sort of circularity, but not which objects are being referenced in it. With this change, we now provide a counter and reference indicator of the object causing a circular reference.
Example:
const x = { a: {}, b: {}, foo: { deno: "land", bar: 1 } };
x.a.x = x;
x.b.y = x;
console.log(x);
<ref *1> { a: { x: [Circular *1] }, b: { y: [Circular *1] }, foo: { deno: "land", bar: 1 } }
This doesn’t only apply to objects, but also Error.cause
s:
const x = new Error("foo");
const y = new Error("bar", { cause: x });
x.cause = y;
console.log(y);
<ref *1> Error: bar
at file:///dev/denoland/dotcom/test.ts:2:11
Caused by Error: foo
at file:///dev/denoland/dotcom/test.ts:1:11
Caused by [Circular *1]
Deno.File
has been renamed to Deno.FsFile
Deno’s abstraction for file system files was previously called Deno.File
.
Unfortunately this name causes a lot of confusion for users because of the
File
Web API that is
available globally both in browsers and Deno.
To avoid confusion in the future we’ve decided to deprecate Deno.File
and
replace it with Deno.FsFile
which should better convey the message that it
abstracts files on the file system. Deno.File
API will be available until Deno
2.0, but we suggest you migrate existing code immediately.
This is purely a rename - all methods on the Deno.File
class are still the
same. Only the name changes from Deno.File
to Deno.FsFile
.
A new lint was added to deno lint
that will help catch uses of Deno.File
and
suggest changes to avoid using a deprecated API.
deno compile
now works more reliably
Previously deno compile
would bundle your entire JS program into a single ES
module during compilation. This bundling sometimes leads to the code not
behaving exactly the same as prior to bundling: stack traces might be off,
import.meta.url
is incorrect, order of execution might differ slightly to
prior to bundling.
To combat this deno compile
now serializes your ES module graph into the
produced binary “as is” without bundling. This means that the order of execution
of the code stays correct, and things like import.meta.url
stay intact.
To do this we are making use of our eszip library and file format, which allows for very fast serialization and deserialization of ES module graphs.
Thank you to @williamtetlow for contributing this feature.
Allow disabling clear screen on file watcher restarts
Deno ships with a built-in file watcher that restarts running process when files
change; it can be used with multiple subcommands by passing the --watch
flag.
Deno automatically discovers which files should be watched, but you can
additionally pass paths you want to be watched
(deno run --watch=file1.ts,file2.js script.ts
).
The file watcher automatically cleared your terminal screen on each restart, but
users reported that in some situations this is not desirable. In this release we
added the --no-clear-screen
flag that can be used with the --watch
flag to
tell Deno not to clear the terminal screen:
deno lint --watch --no-clear-screen
Thank you to @ah-yu for contributing this feature.
deno coverage
gets --output
flag
This release adds --output
flag to deno coverage
subcommand that can be used
when producing lcov
report.
Before this release the report was printed to standard output and could be piped manually to a file:
deno coverage --lcov cov/ > cov.profile
Starting with this release you can instead use --output
flag to write to a
file directly:
deno coverage --lcov --output=cov.profile cov/
Thank you to @VishnuJin for contributing this feature.
Stabilization of signal listener APIs
We have stabilized the signal listener APIs in this release. These APIs can be
used to intercept and handle signals like SIGINT (Ctrl-C
in your shell) with
custom logic. You don’t need to pass the --unstable
flag to use these APIs
anymore.
const listener = () => {
console.log("Got SIGTERM!");
};
// Starts listening for SIGTERM
Deno.addSignalListener("SIGTERM", listener);
// Stops listening for SIGTERM
Deno.removeSignalListener("SIGTERM", listener);
Note: While this API is now stable, the API is not yet available on Windows. Please follow the issue #10236 for updates on this. Stabilization here means that the API surface will not be changed in future minor releases.
Serve HTTP over Unix sockets
Deno’s HTTP server API now supports connections established over Unix sockets in addition to TCP.
import { serveListener } from "https://deno.land/std/http/server.ts";
const listener = Deno.listen({ transport: "unix", path: "/path/to/socket" });
serveListener(listener, (req) => {
return new Response("Hello World");
});
Just like with Unix sockets in general, this API is still unstable. You need to
pass the --unstable
flag to use it.
Thank you to @ylxdzsw for contributing this feature.
Deno.Conn#setNoDelay()
and Deno.Conn#setKeepAlive()
New unstable API: Two new APIs were added to Deno.Conn
:
Deno.Conn.setNoDelay()
Deno.Conn.setKeepAlive()
These APIs allow to configure use of Nagle’s algorithm and keepalive functionality for TCP connections.
Example:
const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 });
// Enable Nagle's algorithm
conn.setNoDelay(false);
// Disable Nagle's algorithm
conn.setNoDelay(true);
// Enable keep-alive
conn.setKeepAlive(true);
// Disable keep-alive
conn.setKeepAlive(false);
Note: The API is still unstable. You need to pass the --unstable
flag to use
it.
Thank you to @yos1p for contributing this feature.
Deno.getUid
New unstable API: Deno 1.19 adds a new Deno.getUid
API. Upon invocation this returns the user ID
of the Deno process on Linux and macOS. You need to pass the --allow-env
permission flag to use this API.
const uid = Deno.getUid();
console.log(uid); // => Prints the user ID
$ deno run --unstable --allow-env getuid.ts
501
Note: The API is still unstable. You need to pass the --unstable
flag to use
it.
Deno.networkInterfaces
New unstable API: This release adds a new Deno.networkInterfaces
API. This API returns an array
of objects containing information about the available network interfaces. You
need to pass the --allow-env
permission to use this API.
const interfaces = Deno.networkInterfaces();
console.log(interfaces); // => Prints network interfaces
$ deno run --unstable --allow-env network_ifs.js
[
{
family: "IPv4",
name: "lo0",
address: "127.0.0.1",
netmask: "255.0.0.0",
scopeid: null,
cidr: "127.0.0.1/8",
mac: "00:00:00:00:00:00"
},
...
]
Note: The API is still unstable. You need to pass the --unstable
flag to use
it.
Improvements to the LSP
As always, this release brings another round of improvements to the LSP:
Code action to replace specifiers that are redirected
When importing an module from a registry like https://deno.land/x
without
specifying a version, the registry will automatically redirect the request to
the latest version of that module. If this happens, the LSP will now offer to
replace the specifier with the one that was redirected to. In combination with
the warning that is shown for unversioned imports, this will make it easier for
users to use versioned imports across their codebase.
Import completions now take into account import maps
Import completions now interact nicely with import maps. Specifiers specified in your import map will be offered as completions.
Symbol links in JSDoc comments are now clickable in hover cards
The hover cards that the Deno LSP offers now correctly render @link
tags in
JSDoc comments as clickable links to symbols. Here is an example of linking to a
symbol in a deprecation message:
V8 9.9
This release of Deno upgrades V8 to 9.9, which adds some new Intl
features:
Intl.Locale
extensions
This release adds seven new properties to the Intl.Locale
object: calendars
,
collations
, hourCycles
, numberingSystems
, timeZones
, textInfo
, and
weekInfo
.
These can be used to determine valid values for these properties for a given language. For example, here are all of the calendars that the Egyptian language supports:
const arabicEgyptLocale = new Intl.Locale("ar-EG");
arabicEgyptLocale.calendars;
// ['gregory', 'coptic', 'islamic', 'islamic-civil', 'islamic-tbla']
Intl Enumeration
This release also adds a new Intl.supportedValuesOf(code)
function that
returns the list supported identifiers in V8 for the Intl
APIs. For example to
get a list of all calendars supported by the Intl
APIs:
Intl.supportedValuesOf("calendar");
// ['buddhist', 'chinese', 'coptic', 'dangi', ...]
Or all supported currencies:
Intl.supportedValuesOf("currency");
// ['ADP', 'AED', 'AFA', 'AFN', 'ALK', 'ALL', 'AMD', ...]