Deno 1.39: The Return of WebGPU
Deno 1.39 marks a significant update in the Deno ecosystem, featuring the much-anticipated return of WebGPU, enhancing capabilities for graphics, gaming, and machine learning. We’ve also introduced new deno coverage reporters for improved codebase analytics and made substantial strides in Node.js compatibility, easing the transition for Node.js developers. Finally, this release includes updates to the Standard Library, performance optimizations, and the latest TypeScript 5.3 support.
If you already have Deno installed, upgrade to version 1.39 in your terminal with:
deno upgrade
If you don’t yet have Deno installed, you can install it with one of the following commands, or many other ways.
MacOS / Linux Install
curl -fsSL https://deno.land/x/install/install.sh | sh
Windows Install
irm https://deno.land/install.ps1 | iex
Here’s an overview of what’s new in Deno 1.39:
- WebGPU is back
- New
deno coverage
reporters - Updates to
deno compile
- Enhanced Language Server
- Node.js compatibility improvements
- Changes to
Deno
APIs - Changes to Web APIs
- Standard Library updates
- TypeScript 5.3
- Upcoming changes to the decorators
WebGPU is back
The WebGPU API gives developers a low level, high performance, cross architecture way to program GPU hardware from JavaScript. It is the effective successor to WebGL on the Web. The spec has been finalized and Chrome has already shipped the API. Support is underway in Firefox and Safari.
Deno first introduced WebGPU back in early 2021 but it was removed earlier this year due to performance problems. With this release, we are happy to re-introduce it with all the performance issues worked out.
GPUs have the ability to compute certain numerical operations with extreme parallelism as compared to CPUs. This is useful for a variety of applications, beyond just rendering and games. For example, machine learning algorithms can often be expressed as a series of matrix operations, which can be computed extremely efficiently on a GPU.
Our WebGPU implementation is based on the same underlying system as the upcoming WebGPU implementation in Firefox, so we’re confident that it’s a solid foundation for developers to build on.
A basic example of getting information about the GPU via WebGPU:
// Try to get an adapter from the user agent.
const adapter = await navigator.gpu.requestAdapter();
if (adapter) {
// Print out some basic details about the adapter.
const adapterInfo = await adapter.requestAdapterInfo();
console.log(`Found adapter: ${adapterInfo.device}`); // On some systems this will be blank
const features = [...adapter.features.values()];
console.log(`Supported features: ${features.join(", ")}`);
} else {
console.error("No adapter found");
}
Further examples can be viewed over at our webgpu-examples repository.
Although the specification is stable, WebGPU is still considered unstable in
Deno. To access it in Deno use the --unstable-webgpu
flag. We’re planning to
stabilize it soon, after we’ve had a chance to get more feedback from the
community and more time to verify the implementation against the
specification test suite.
For more webgpu functionalities, we’ve also added std/webgpu
(more info below).
Thanks to the wgpu team for the help provided to achieve this.
deno coverage
reporters
New In this release, deno coverage
received two new reporters: summary
and
html
. You can now also omit the directory value for the --coverage
flag in
deno test
, as we now default to using ./coverage/
directory.
The summary
reporter is the new default reporter. It outputs the coverage
summary in a concise table giving you information about coverage in particular
files and overall summary:
$ deno coverage
----------------------------------
File | Branch % | Line % |
----------------------------------
bar.ts | 0.0 | 57.1 |
baz/quux.ts | 0.0 | 28.6 |
baz/qux.ts | 100.0 | 100.0 |
foo.ts | 50.0 | 76.9 |
----------------------------------
All files | 40.0 | 61.0 |
----------------------------------
The previously default reporter that prints uncovered lines in all files to the
terminal is still available using the --detailed
flag.
You can also specify --html
flag to get the detailed coverage report in HTML
format.
$ deno test --coverage
$ deno coverage --html
HTML coverage report has been generated at file:///path/to/project/coverage/html/index.html
The examples of the coverage report look like the below:
The output of deno coverage --html
is completely static and can be hosted on
any static file server, such as GitHub Pages.
We still support the --lcov
flag, which outputs the coverage report in the
LCOV format, which is useful for
integrating with other tools such as Codecov or
Coveralls.
We have further plans to improve deno coverage
and make it more useful. If you
have feedback you’d like to share, please comment on
this issue.
deno compile
Updates to Deno 1.39 introduces significant advancements in the deno compile
feature:
Better node_modules
support: With the --unstable-byonm
flag,
deno compile
now supports “bring your own node_modules”, enhancing Node.js
compatibility. This feature allows you to use npm packages directly in your Deno
projects, bridging the gap between Deno and the extensive npm ecosystem. Learn
more about this feature in
our previous blog post.
Flexible Naming for Executables: Deno has removed restrictions on executable
naming. You can now name your compiled programs with leading digits, such as
3d_modeler
, offering more flexibility in naming conventions.
More Dynamic Import Support: More dynamic import patterns are now supported
in deno compile
. This is interesting because Deno needs to statically include
all modules that may be imported at runtime in the binary produced by
deno compile
. Because of this, dynamic imports can be problematic. Now, Deno
can handle more dynamic import patterns, such as:
await import(`./dir/${expr}`);
In this example, Deno will include all modules from ./dir/
and its
subdirectories into the compiled binary. This allows you to import any of these
files at runtime. This update simplifies dependency management by ensuring that
all modules referenced dynamically are available at runtime.
Enhanced Language Server
In our ongoing commitment to improving the Deno Language Server (LSP), this release introduces significant performance enhancements.
Responsive Typing Experience: We’ve optimized the handling of rapid request bursts during fast typing, ensuring a smoother and more responsive editing experience in your IDE.
Shutdown Timeout: To tackle the issue of lingering ‘zombie’ LSP instances, we’ve implemented a shutdown-timeout mechanism. This feature forcefully terminates LSP processes when you close your editor after a set timeout, promoting efficient resource utilization.
Update Notifications: The language server now actively informs you of the latest Deno updates. This feature aims to simplify the process of keeping your Deno installation current, ensuring you always have access to the latest features and fixes.
Enhanced Diagnostics: We’ve introduced a new deno.logFile
setting in
VSCode. When used alongside deno.internalDebug
, this feature allows for the
capture of detailed diagnostic data, aiding in performance analysis and
troubleshooting.
We are dedicated to continuously enhancing the Deno LSP. Should you encounter any performance issues with the LSP or within VSCode Deno, your feedback is invaluable. Please let us know about your experience here.
Node.js compatibility improvements
Sloppy imports
Migrating existing TypeScript codebases to Deno can be a daunting task. One of the biggest hurdles is the fact that Deno requires you to explicitly specify file extensions in the import statements.
Imagine a TypeScript project that had a file foo.ts
that imported bar.ts
:
// foo.ts
import { Example } from "./bar";
console.log(Example);
// bar.ts
export const Example = "Example";
In previous versions of Deno, this would error out with an unhelpful message:
# Deno v1.38.5
$ deno run foo.ts
error: Module not found "file:///dev/bar"
at file:///dev/foo.ts:1:25
Now in Deno 1.39 this error message is improved and an escape hatch made available:
# Deno v1.39.0
$ deno run foo.ts
error: Module not found "file:///dev/bar". Maybe add a '.ts' extension or run with --unstable-sloppy-imports
at file:///dev/foo.ts:1:25
Running with the --unstable-sloppy-imports
flag will execute the code without
any changes:
# Deno v1.39.0
$ deno run --unstable-sloppy-imports foo.ts
Warning Sloppy imports are not recommended and have a negative impact on performance.
Warning Sloppy module resolution (hint: add .ts extension)
at file:///dev/foo.ts:1:25
Example
In addition to resolving imports without file extension, this feature also
allows to resolve “directory” imports, as well as importing .ts
files using
.js
extension:
// routes/index.ts
export default {
"/": () => "Hello World",
"/example": () => "Example",
};
// bar.ts
export const bar = "bar";
// foo.ts
import routes from "./routes";
import { bar } from "./bar.js";
console.log(routes);
console.log(bar);
$ deno run --unstable-sloppy-imports foo.ts
Warning Sloppy imports are not recommended and have a negative impact on performance.
Warning Sloppy module resolution (hint: specify path to index.ts file in directory instead)
at file:///Users/ib/dev/deno/foo.ts:1:20
Warning Sloppy module resolution (hint: update .js extension to .ts)
at file:///Users/ib/dev/deno/foo.ts:2:21
{ "/": [Function: /], "/example": [Function: /example] }
bar
As seen above, using this flag will print warnings that guide you towards migrating the code to the recommended import syntax. You will also get diagnostics in your editor, along with “quick fixes” that will make it easier to apply necessary changes.
We hope this feature will make it much easier to try out and migrate existing projects to Deno.
node_modules/.bin/
executables in deno task
Support running deno task
can run tasks defined in deno.json
, as well as scripts
defined
in package.json
. This release adds support for running executables in
node_modules/.bin/
directory in deno task
.
If you have package.json
that looks like this:
{
"scripts": {
"dev": "vite dev"
}
}
You can run vite dev
with deno task
:
$ deno task dev
Deno will look for vite
executable in node_modules/.bin/
directory and run
it using Deno - even if the node_modules/.bin/vite
uses shebang defining
Node.js as an interpreter.
node_modules
CommonJS entrypoints in Deno will now correctly handle CommonJS entrypoints in node_modules
,
respecting the type
setting from package.json
for the relevant package. This
should greatly improve compatibility with packages that still haven’t migrated
to ESM.
Object.prototype.__proto__
Support for Deno made a conscious decision to not support Object.prototype.__proto__
for
security reasons. However there are still many packages on npm
that rely on
this property to work correctly.
In this release we introduced a new --unstable-unsafe-proto
flag that allows
you to enable this property. It is not recommended to use this flag, but if you
really need to use a package that relies on it, the escape hatch is now
available to you.
Node.js APIs updates
Following Node.js APIs are now available:
crypto.createPrivateKey
http.ClientRequest.setTimeout
http.globalAgent
perf_hooks.performance
process.geteuid
process.report
util.parseArgs
vm.runInNewContext
Additionally we fixed several bugs in already supported Node.js APIs:
child_process.spawnSync
handling ofstatus
was incorrectchild_process.spawnSync
properly normalizesstdio
child_process
can handle IPC pipes on Unix systems (Windows support coming soon)crypto.sign
now works with PEM private keysfs.existsSync
is now faster when file does not existprocess.exitCode
should change exit code of process- allow
null
value forhttp.OutgoingMessage.setHeader
- fix
Buffer.copy
whensourceStart
>source.length
- fix
os.freemem
- fix
stream.Writable
- handle closing
process.stdin
more than once
Deno
APIs
Changes to This release includes several changes to the Deno
APIs:
Deno.serve()
support for Unix sockets is now stable
Deno.serve(
{ transport: "unix", address: "/tmp/my.sock" },
(req) => new Response("Hello!"),
);
Deno.HttpServer.shutdown()
is now stable
const server = Deno.serve((req) => new Response("Hello!"));
// Shutdown the server gracefully when the process is interrupted.
Deno.addSignalListener("SIGINT", () => {
server.shutdown();
});
await server.finished;
Deno.HttpClient
can now be declared with using
keyword
Following up on
Explicit Resource Management
introduced to Deno
APIs
in the previous release, this release brings
support for using
keyword to Deno.HttpClient
:
{
using client = Deno.createHttpClient({});
const response = await fetch("http://localhost:4545/assets/fixture.json", {
client,
});
const json = await response.json();
}
// `client` is closed here
KV watch
The new, unstable Deno.Kv.watch()
API to watch for changes to the given keys
in the given database. This API returns a ReadableStream
that emits a new
value whenever any of the watched keys change their versionstamp. The emitted
value is an array Deno.KvEntryMaybe
objects, with the same length and order as
thekeys
array. Read more about the KV watch feature here.
const db = await Deno.openKv();
const stream = db.watch([["foo"], ["bar"]]);
for await (const entries of stream) {
entries[0].key; // ["foo"]
entries[0].value; // "bar"
entries[0].versionstamp; // "00000000000000010000"
entries[1].key; // ["bar"]
entries[1].value; // null
entries[1].versionstamp; // null
}
Cron
The Deno.cron
function, introduced recently, has seen an exciting update in
this release. It now supports an intuitive JSON format for defining schedules,
making it easier to understand and implement cron jobs. For more in-depth
information,
explore our detailed blog post about the cron feature.
Traditional Unix Cron Format
Previously, setting up a task to run every 20 minutes required the Unix cron format:
Deno.cron("myCron", "*/20 * * * *", () => {
console.log("Running every 20 minutes");
});
New JSON Format
Now, you can achieve the same with a more readable JSON format:
Deno.cron("myCron", {
minutes: { every: 20 },
}, () => {
console.log("Running every 20 minutes");
});
Deprecated IO interfaces
We’ve been pushing Deno
APIs to use
Web Streams API
for multiple months now and to finalize this change the v1.39 release brings a
deprecation to the various IO interfaces in Deno.
The following interfaces are now deprecated and are slated to be removed in Deno 2:
Deno.Reader
Deno.ReaderSync
Deno.Writer
Deno.WriterSync
Deno.Closer
You will now get a warning in your editor when using these interfaces, with suggestions to use Web Streams APIs instead.
Changes to Web APIs
AbortSignal.any()
This API allows for listening to multiple
AbortSignals
,
and will abort if any of the specified signals is aborted.
This can be useful if you have an API that provides an AbortSignal
(for
example, Request
),
and you want to additionally depend on another AbortSignal
.
ImageData
The ImageData
Web API is a class that can be used to represent an image in a
standardized format.
A quick example of creating a 1x2 pixels red image:
const rawImage = new Uint8ClampedArray([255, 0, 0, 1, 255, 0, 0, 1]);
new ImageData(rawImage, 1, 2);
Thanks to @jamsinclair
who contributed this
feature.
ReadableStream.read
min
option
The min
option for ReadableStreamBYOBReader.read
allows for specifying the
minimum amount of bytes to read. This is useful for various encoding formats
that require reading an n amount of bytes, which can be achieved by setting the
min
option to the same length as the byte length of the buffer.
For example:
const response = await fetch("https://example.com");
const reader = response.body!.getReader({ mode: "byob" });
const buffer = new Uint8Array(10);
// value will contain at least 5 bytes written to it
const { value } = await reader.read(buffer, { min: 5 });
URLPattern
performance
Improved The URLPattern
API has received performance improvements that make it 6 to 8
times faster when matching against multiple patterns, this is an optimization
directed towards a popular scenario of using URLPattern
in an HTTP router.
Standard Library updates
std/webgpu
Alongside releasing WebGPU in the CLI, we also added std/webgpu
in this
release.
This module adds some additional functionalities, including
createTextureWithData
for creating a GPUTexture
with existing data,
describeTextureFormat
to get information about a texture format, and
createCapture
to take a “capture” using a texture, among others.
Some of these functionalities have been ported/based on
wgpu
features, and further functionalities
are planned.
std/expect
In this release std/expect
has been added. The
module exports two functions:
expect
and
fn
. These are designed to be
compatible with Jest.
import { expect } from "https://deno.land/[email protected]/expect/mod.ts";
expect(Math.max(5, 8)).not.toEqual(5);
expect(Math.max(5, 8)).toEqual(8);
expect(Math.pow(3, 5)).toBeGreaterThan(100);
expect(Math.pow(3, 5)).toBeLessThan(300);
Currently expect
supports the following matcher and modifier APIs: not
,
resolves
, rejects
, toBe
, toEqual
, toStrictEqual
, toMatch
,
toMatchObject
, toBeDefined
, toBeUndefined
, toBeNull
, toBeNaN
,
toBeTruthy
, toBeFalsy
, toContain
, toContainEqual
, toHaveLength
,
toBeGreaterThan
, toBeGreaterThanOrEqual
, toBeLessThan
,
toBeLessThanOrEqual
, toBeCloseTo
, toBeInstanceOf
, toThrow
,
toHaveProperty
, toHaveLength
.
The module also provides mock related util and assertions:
import { expect, fn } from "https://deno.land/[email protected]/expect/mod.ts";
const mockFn = fn();
mockFn();
mockFn();
mockFn();
expect(mockFn).toHaveBeenCalled();
expect(mockFn).not.toHaveBeenCalledTimes(1);
expect(mockFn).toHaveBeenCalledTimes(3);
The following mock related matchers are implemented: toHaveBeenCalled
,
toHaveBeenCalledTimes
, toHaveBeenCalledWith
, toHaveBeenLastCalledWith
,
toHaveBeenNthCalledWith
, toHaveReturned
, toHaveReturnedTimes
,
toHaveReturnedWith
, toHaveLastReturnedWith
, toHaveNthReturnedWith
.
The module is not completely compatible with expect
API of Jest. The following
APIs are not implemented yet: toMatchSnapShot
, toMatchInlineSnapShot
,
toThrowErrorMatchingSnapShot
, toThrowErrorMatchingInlineSnapShot
,
expect.anything
, expect.any
, expect.arrayContaining
,
expect.not.arrayContaining
, expect.closedTo
, expect.objectContaining
,
expect.not.objectContaining
, expect.stringContaining
,
expect.not.stringContaining
, expect.stringMatching
,
expect.not.stringMatching
, expect.assertions
, expect.hasAssertions
,
expect.addEqualityTester
, expect.addSnapshotSerializer
, expect.extend
.
Standard Library also has
std/testing/bdd
module. Now you can
write your test cases in BDD style using Standard Library.
import { describe, it } from "https://deno.land/[email protected]/testing/bdd.ts";
import { expect } from "https://deno.land/[email protected]/expect/mod.ts";
describe("Math", () => {
describe("max", () => {
it("returns max value from the given args", () => {
expect(Math.max(1, 2, 3)).toEqual(3);
expect(Math.max(-3, -2, -1)).toEqual(-1);
});
});
});
Save this file as my_test.ts
and you can execute it with the command:
$ deno test my_test.ts
Check file:///path/to/my_test.ts
running 1 test from ./my_test.ts
Math ...
max ...
returns max value from the given args ... ok (1ms)
max ... ok (1ms)
Math ... ok (1ms)
ok | 1 passed (2 steps) | 0 failed (1ms)
Thanks Thomas Cruveilher for contributing to this feature.
std/ini
In this release std/ini
has been added. The
module provides the parser and serializer for working with
INI files.
Thanks Aaron Huggins for contributing to this feature.
std/data_structures
std/data_structures
has been added in
this release. This module exports classes that implements
data structure algorithms.
Currently RedBlackTree
, BinarySearchTree
, and BinaryHeap
classes are
available.
std/text
std/text
has been added in this release. This
module exports utility functions for comparing/sorting/choosing texts with
certain criteria:
import {
closestString,
levenshteinDistance,
} from "https://deno.land/[email protected]/text/mod.ts";
// Calculates levenshtein distance of 2 words
console.log(levenshteinDistance("hello", "halo")); // => prints 2
console.log(levenshteinDistance("hello", "world")); // => prints 4
console.log(closestString("puhs", ["commit", "pull", "push"])); // => prints "push"
Thanks Jeff Hykin for contributing these features.
std/cli
std/cli
has been added in this release. This
module currently exports 2 APIs: parseArgs
and promptSecret
.
import {
parseArgs,
promptSecret,
} from "https://deno.land/[email protected]/cli/mod.ts";
const parsedArgs = parseArgs(["--foo", "--bar=baz", "./quux.txt"]);
console.log(parsedArgs);
// prints: { foo: true, bar: "baz", _: ["./quux.txt"] }
// This is typically used as `parseArgs(Deno.args)`
const credential = promptSecret("Enter the credential ");
// This asks for the credential in the terminal and the user inputs are masked with '*'
Note: parseArgs
is the same as parse
in std/flags
, but we realized the
scope of std/flags
is too limited, and decided to explore more CLI-related
features under std/cli
.
Thanks Alisue for proposing and implementing
promptSecret
.
std/net
std/net
has been added in this release. This
module exports 1 API: getAvailablePort
, which is useful for choosing available
TCP port.
import { getAvailablePort } from "https://deno.land/[email protected]/net/mod.ts";
const port = await getAvailablePort();
Thanks Harry Solovay for contributing to this feature.
TypeScript 5.3
Deno v1.39 ships with latest release of TypeScript 5.3, read more in Announcing TypeScript 5.3 blog post.
Upcoming changes to the decorators
Currently, Deno enables experimental TypeScript decorators by default. Due to the TC39 Decorator proposal being at Stage 3 and a popular request we’ve decided to change the default setting for decorators in the upcoming Deno v1.40 release.
In the next release the experimental TypeScript decorators will be disabled by default and the TC39 Decorators will be enabled by default.
If you are using experimental TS decorators, prepare yourself for this change by
adding this configuration to your deno.json
:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
Thank you to our contributors!
We couldn’t build Deno without the help of our community! Whether by answering questions in our community Discord server or reporting bugs, we are incredibly grateful for your support. In particular, we’d like to thank the following people for their contributions to Deno 1.39: Aravind, Birk Skyum, Bolat Azamat, Chen Su, Daniel Mizerski, Florian Schwalm, Gasman, Ian Bull, Jacob Hummer, Jakub Jirutka, Jamie, Jesse Jackson, John Spurlock, Jordan Harband, Julien Cayzac, Jérôme Benoit, Kenta Moriuchi, Laurence Rowe, Max Goodhart, Raashid Anwar, Tareque Md Hanif, btoo, citrusmunch, lionel-rowe, liruifengv, pk, scarf, ud2, and 林炳权.
Would you like to join the ranks of Deno contributors? Check out our contribution docs here, and we’ll see you on the list next time.
Believe it or not, the changes listed above still don’t tell you everything that got better in 1.39. You can view the full list of pull requests merged in Deno 1.39 on GitHub here.
Thank you for catching up with our 1.39 release, and we hope you love building with Deno!
🍋 Fresh 1.6 is out.
Fresh v1.6 apps have expanded plugin APIs, faster route matching, and official Tailwind CSS support.