Deno 1.45: Workspace and Monorepo Support
Deno continues to evolve with Deno 1.45. The standout feature in this release is
the introduction of workspaces, providing a robust solution for managing
monorepos. This addition simplifies dependency management, configuration
sharing, and module organization across large codebases. Alongside workspaces,
this update includes improvements to Node.js compatibility, updates to
deno install
, the new deno init --lib
command, the deprecation of
deno vendor
, and more.
To upgrade to Deno 1.45, run the following in your terminal:
deno upgrade
If Deno is not yet installed, run one of the following commands to install or learn how to install it here.
# Using Homebrew (macOS):
brew install deno
# Using Shell (macOS and Linux):
curl -fsSL https://deno.land/install.sh | sh
# Using PowerShell (Windows):
iwr https://deno.land/install.ps1 -useb | iex
What’s New in Deno 1.45
- Workspace support
- Node.js compatibility improvements
- Frozen lockfile
deno install
updatesdeno init --lib
deno vendor
is now deprecateddeno test
file discovery- Jupyter notebooks improvements
deno compile
supports--env
flag- More flexible language server
- The Standard Library is closer to being stable
- V8 12.7 and TypeScript 5.5.2
- Try out Deno 2 features with
DENO_FUTURE=1
- Acknowledgments
Workspace support
Deno v1.45 adds support for workspaces and monorepos. There are two forms of
supported workspaces: Deno-first workspaces defined in deno.json
and
backwards-compatible npm workspaces.
Deno workspaces are straightforward. Configuration defined globally is applied to each member package but can be overridden by members. You can mix and match npm and Deno workspaces—an npm package inside a Deno workspace or vice versa.
Publishing workspace members to JSR is as easy as running deno publish
. The
standard library is a good example of
how to use workspaces effectively.
To get started, define a "workspace"
element inside your deno.json
and list
the member directories.
{
"workspace": ["./add", "./subtract"]
}
npm
workspaces also work
seamlessly in Deno. Whether you’re including a Deno library in a larger npm
workspace or an npm library in a larger Deno workspace, dependencies will
correctly resolve.
To learn more about workspace support, visit the Deno Docs.
We will also host a one-hour livestream on YouTube on Tuesday, July 16, at 9 AM PT (UTC-7) to cover our new workspace support in detail and answer your questions. RSVP here.
Node.js compatibility improvements
Node-API support
has been completely revamped,
fixing many existing issues with packages like prisma
, sqlite3
, paper
,
duckdb
, nodejs-polars
.
Other Node.js compatibility improvements include:
We’re working on supporting dd-trace. It’s not fully there yet, but we’re getting closer with:
- The addition of
net.BlockList
andnet.SocketAddress
- Addining missing APIs to
node:diagnostics_channel
module - Support for
Module.parent
- The addition of
fs.lutimes
andfs.lutimesSync
are now supported, so is [fs.lchown
andprocess.getegid
] (https://github.com/denoland/deno/pull/24418)node:crypto
andnode:zlib
constants were addedIn
node:fs
,Dirent.path
andDirent.parentPath
are now availableThe
node:http
module received significant updates:Server#close()
now does a graceful shutdown, allowing in-flight requests to finishServerResponse#appendHeader()
was added- All
ServerResponse#writeHead()
signatures are now supported ServerResponse#setHeaders
now handles arrays of headersServerResponse
handles chunked writes correctlyServerResponse
doesn’t crash the process when streamingServer
returnsnull
foraddress
if it hasn’t started listening yetClientRequest
now correctly send requests
The
node:vm
will consume less memorycrypto.Hash
implementation was revamped@grpc/grpc-js
support is now more robust with correct setting ofend_stream
flagnode:child_process
APIs now support"ipc"
value forstdio
optionreadline/promises
module is now available in ES modules
Additionally, there are other impovements related to npm
support:
- Deno will
discover
.npmrc
configuration file placed in your home directory for better private registry support - Types shipped with the package are
now preferred over types from
@types
scope - Deno now supports more forms of SemVer constraints
Frozen lockfile
A new --frozen
(alias --frozen-lockfile
) flag has been added that controls
the behavior of the lockfile.
You can use this flag to tell Deno to error out any time the lockfile gets out of date. This is especially useful in context of CI pipelines, where you should ensure that all the pushed code is up to date and there are no new or surprising changes to your dependencies.
When running a deno command with the --frozen
flag, any attempts to update the
lockfile with new contents will cause the command to exit with an error showing
the modifications that would have been made.
For example, let’s say somewhere in your project you were importing
npm:chalk@5.3.0
. Then later down the line, someone copies a snippet of code
from a slightly out of date AI chatbot that only knows about chalk version
5.2.0
, so a file ends up with an import like:
import chalk from "npm:chalk@5.2.0";
Luckily, your CI pipeline has a test step with --frozen
specified:
deno test --frozen --coverage
Instead of quietly adding a second (out of date) version of chalk to your dependency tree, it fails, showing that chalk 5.3.0 would’ve been added to your lockfile:
error: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it.
changes:
5 | - "npm:chalk@5.3.0": "npm:chalk@5.3.0"
6 | - },
7 | - "npm": {
5 | + "npm:chalk@5.2.0": "npm:chalk@5.2.0",
6 | + "npm:chalk@5.3.0": "npm:chalk@5.3.0"
7 | + },
8 | + "npm": {
9 | + "chalk@5.2.0": {
10 | + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==",
11 | + "dependencies": {}
12 | + },
If you intend to explicitly update your lockfile, you can specify
--frozen=false
, which will update the lockfile without error.
With the above setting, every dependency update will require running a command
or a task with --frozen=false
flag, making these updates intentional and
explicit.
Finally, the --lock-write
is now deprecated and will be removed in Deno 2. You
can replace --lock-write
usages with --frozen=false
.
deno install
updates
In Deno 2, the deno install
subcommand will behave more like npm install
to
support common workflows. Currently in Deno, deno install <pkg>
installs a
binary package globally. In Deno 2, deno install <pkg>
will install a package
locally by default, adding a new dependency to the project (similar to
deno add <pkg>
) and caching it. In addition, deno install
without an
argument will cache local dependencies listed in package.json
or an import map
as well as set up a local node_modules
directory, if applicable. To install
globally, you’ll need to specify the --global
(-g
) flag:
deno install --global <pkg>
.
To try out the new install command in your projects, run with DENO_FUTURE=1
:
DENO_FUTURE=1 deno install
with an argument, like deno add
:
DENO_FUTURE=1 deno install @david/dax
We encourage you to try out the new deno install
and report any issues you
encounter!
npm lifecycle scripts support
Some scripts in package.json
are special in that they are executed
automatically by npm
on certain operations. There are
many
lifecycle scripts supported by npm
, but as a consumer of a package the main
relavant scripts are pre/post install scripts, which are executed when a package
is installed (i.e. normally during npm install
).
Some packages rely on their install scripts to perform setup steps (for instance, downloading or building artifacts of native addons), and won’t work properly if they aren’t executed. Previously, Deno did not support executing lifecycle scripts, so this could result in confusing errors when using some packages, and there weren’t easy solutions.
Now, Deno supports running lifecycle scripts in deno cache
(and
DENO_FUTURE=1 deno install
), and will warn if it detects that a package has a
lifecycle script that hasn’t been run.
By specifying the --allow-scripts
flag with deno cache
(or
DENO_FUTURE=1 deno install
), you can opt into running lifecycle scripts for
specific packages:
deno cache --allow-scripts=npm:duckdb main.ts
ℹ️ Note
Currently we only support lifecycle scripts when using a localnode_modules
directory ("nodeModulesDir": true
indeno.json
).
In the future we plan to add support for lifecycle scripts withoutnode_modules
, but this will be best-effort, as some packages rely on being in anode_modules
directory.
deno init --lib
- easily set up a new library
deno init
subcommand was introduced in Deno v1.25
and allowed you to start a minimal scaffold for Deno project in a few
keystrokes.
Since introduction of JSR users have been asking for a way to quickly start a project that will be published to JSR.
You can do just that with Deno v1.45 and deno init --lib
:
✅ Project initialized
Run these commands to get started
# Run the tests
deno test
# Run the tests and watch for file changes
deno task dev
# Publish to JSR (dry run)
deno publish --dry-run
Just make sure to update name
and version
fields in the generated
deno.json
file before publishing!
deno vendor
is now deprecated
deno vendor
was added in Deno v1.19 to allow users
to vendor all dependencies inside the project directory.
Since then, another way of vendoring dependencies
was introduced in Deno v1.37
with --vendor
flag or { "vendor": true }
option in the config file.
This option received a lot of positive feedback and pointed out shortcomings and
poor DX or deno vendor
subcommand. With that in mind, deno vendor
is now
deprecated and scheduled to be removed in Deno 2.
Please migrate to using --vendor
flag or vendor
option in the config file.
deno test
file discovery
deno test
can automatically discover and run tests for files that match
certain patterns:
- File name ends with
_test
- eg.app_test.ts
,component_test.tsx
- File name ends with
.test
- eg.router.test.ts
,controller.test.js
- File name is
test
- eg.test.ts
,test.js
To improve compatibility with wider ecosystem, deno test
will now
automatically discover and run files that are under __tests__
directory:
$ tree
.
├── __tests__
│ ├── integration.ts
│ └── unit.ts
└── main.ts
2 directories, 3 files
In Deno v1.44:
$ deno test
error: No test modules found
In Deno v1.45:
$ deno test
deno test
Check file:///Users/ib/dev/test_discovery/__tests__/integration.ts
Check file:///Users/ib/dev/test_discovery/__tests__/unit.ts
running 1 test from ./__tests__/integration.ts
integration test ... ok (0ms)
running 1 test from ./__tests__/unit.ts
unit test ... ok (0ms)
ok | 2 passed | 0 failed (11ms)
Blob.bytes()
Following up on changes from Deno v1.44
and updates to the Web File API specification, Blob.bytes()
is now supported:
const jsonStr = JSON.stringify({ hello: "world" }, null, 2);
// Before:
const blob = new Blob([jsonStr], { type: "application/json" });
const buffer = new Uint8Array(await blob.arrayBuffer());
// After:
const blob = new Blob([jsonStr], { type: "application/json" });
const buffer = await blob.bytes();
Jupyter notebooks improvements
You can now use prompt
and confirm
API in Jupyter notebooks to provide more
flexibility and interactivity.
To further improve Data Science ecosystem for JavaScript (and TypeScript) we’re aiming to add support for interactive widgets using JSX and React in the next release.
deno compile
supports --env
flag
--env
flag was added in Deno v1.38, adding
native support for loading env vars for the process from .env
files.
With Deno v1.45 --env
flag can be used to base in certain environmental
variables into binaries produced with deno compile
.
🛑 Caution
Keep in mind, that these env vars can still be read when inspecting contents of the shipped program, so use this feature with care. It’s probably not the best idea to ship production keys written into the binary.
When executing a program created with deno compile
, all calls to
Deno.env.get(<var_name>)
for variables provided in the .env
file, will
return a value specified during compilation process, not the current variable on
user system:
$ cat .env
HELLO_THERE=deno
$ cat main.ts
console.log("Hello there")
console.log(Deno.env.get("HELLO_THERE") + "!");
$ deno compile --env --allow-env main.ts
...
In Deno v1.44.4
HELLO_THERE="General Kenobi" ./main
Hello there
General Kenobi!
In Deno v1.45.0
HELLO_THERE="General Kenobi" ./main
Hello there
Deno!
More flexible language server
Previously, the VSCode extension could only read and incorporate the deno.json
or deno.jsonc
file located at the root of the workspace. The configuration
from there would be applied to every open source file. This made certain
monorepo configurations impossible.
1.45 makes the language server more independent of the root folder open in the
editor. Configuration files in subdirectories are now detected, even if there
are multiple. Each discovered deno.json
or deno.jsonc
will produce a
separate scope with its own typechecking environment, module resolution and
more. You can configure different entries for compilerOptions.libs
or import
modules which make augmentations to global types, these will not pollute the
environment of other deno.json[c]
scopes.
The Standard Library is closer to being stable
The Deno Standard Library offers a set of high quality packages that are audited by the core team and guaranteed to work with Deno.
As mentioned in our previous blog post, the Standard Library is currently undergoing stabilization efforts, with the goal of stabilizing 31 out of 38 packages.
To date, we’ve stabilized 13 packages:
@std/assert
@std/bytes
@std/collections
@std/crypto
@std/data-structures
@std/encoding
@std/html
@std/media-types
@std/msgpack
@std/path
@std/regexp
@std/toml
@std/uuid
These packages have reached version 1.0.0, adhere to SemVer semantics and ensure the compatibility throughout their 1.x.x versions.
The remaining 18 packages have had their release candidate (RC) versions published (see the roadmap issue for list). If you’re currently using any of these packages, please consider testing the RC versions and sharing your feedback with us!
For more details on the stabilization timeline, please refer to the roadmap issue.
V8 12.7 and TypeScript 5.5.2
Deno 1.45 ships with V8 12.7 and TypeScript 5.5.2.
DENO_FUTURE=1
Try out Deno 2 features with We encourage you to try running your existing projects with DENO_FUTURE=1
environmetal variable, which enables Deno 2 features. We anticipate minimal to
very low effort migration. If your experience is different
share it with us.
Acknowledgments
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.45: Adam Gregory, Andreas Kohn, Andrew Johnston, Filip Skokan, HasanAlrimawi, Kenta Moriuchi, Luca Bruno, Oliver Medhurst, Richard Carson, Tom Alcorn, Victor Turansky, Yazan AbdAl-Rahman, Zander Hill, Zebreus, muddlebee, safaa-mojahed, ud2.
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.45. You can view the full list of pull requests merged in Deno 1.45 on GitHub here.
Thank you for catching up with our 1.45 release, and we hope you love building with Deno!