Skip to main content
Deno 2 is finally here 🎉️
Learn more
Deno 1.29 Custom npm registry support

Deno 1.29: Custom npm registry support

Deno 1.29 has been tagged and released with the following new features and changes:

If you already have Deno installed, you can upgrade to 1.29 by running:

deno upgrade

If you are installing Deno for the first time:

# MacOS and Linux
curl -fsSL https://deno.land/x/install/install.sh | sh

# Windows
iwr https://deno.land/x/install/install.ps1 -useb | iex

Click here for more installation options.

npm compatibility improvements

This release features several npm compatibility improvements and 30+ bug fixes since 1.28.0.

Custom registry support via environment variable

Deno now respects the NPM_CONFIG_REGISTRY environment variable, which allows for specifying a custom npm registry.

# change this to a custom registry
> NPM_CONFIG_REGISTRY=https://registry.npmjs.org deno run main.ts

In a future release, there will be support for using different registries per package scope and the ability to set credentials. Follow issue #16105 for updates.

deno install support

npm specifiers can now be used with deno install.

> deno install -A npm:[email protected]
✅ Successfully installed cowsay
C:\Users\david\.deno\bin\cowsay.cmd
C:\Users\david\.deno\bin\cowsay (shell)
> cowsay Hello from deno!
 __________________
< Hello from deno! >
 ------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

This will additionally create a lockfile for the command on first run to ensure each subsequent run uses the same npm dependency versions.

REPL changes

REPLs are very valuable tools in a developer’s toolchain; allowing for quick experimentation. This release brings a plethora of updates to the REPL:

npm support

You can now use npm packages directly from the REPL. As for other Deno subcommands no prior installation step is required - just import a package and you’re good to go.

$ deno
> import cowsay from "npm:cowsay"
undefined
> console.log(cowsay.say({ text: "hello world" }))
 _____________
< hello world >
 -------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
undefined
>

deno repl runs with no permissions by default

Before this release both the deno and deno repl commands started the REPL with full permissions granted. While it’s useful for quick experimentation to not have to answer permission prompts, it’s somewhat conflicting with Deno’s “secure by default” story.

This release changes the deno repl subcommand to have no permissions granted by default. Permissions can be specified using --allow-* flags or you can defer to permission prompt when calling an API that requires a permission:

$ deno repl --allow-write
Deno 1.29.0
Run repl.help() to see help
exit using ctrl+d, ctrl+c, or close()
> Deno.writeTextFileSync("hello.txt", "hello world");
undefined
$ deno repl
Deno 1.29.0
Run repl.help() to see help
exit using ctrl+d, ctrl+c, or close()
> Deno.writeTextFileSync("hello.txt", "hello world");
⚠️  ┌ Deno requests write access to "hello.txt".
   ├ Requested by `Deno.writeFileSync()` API
   ├ Run again with --allow-write to bypass this prompt.
   └ Allow? [y/n] (y = yes, allow; n = no, deny) > y
✅ Granted write access to "hello.txt".
undefined

To make it clearer, running deno will show a banner informing that all permissions are allowed and suggesting to use deno repl subcommand if you want to limit permissions:

$ deno
Deno 1.29.0
Run repl.help() to see help
exit using ctrl+d, ctrl+c, or close()
REPL is running with all permissions allowed.
To specify permissions, run `deno repl` with allow flags.
>

More reliable history handling

The REPL will now update history file on each executed statement, making history less spotty.

--quiet flag is now respected

If you use the REPL with --eval or --eval-file to build a custom REPL, it might be undesirable to see the default banner printed on startup. The REPL now respects the --quiet flag which will hide any auxiliary output from the REPL:

$ deno --quiet
>

Quality of life improvements

deno init improvements

The deno init subcommand was added in the v1.25 release to allow users to quickly scaffold a new project (though it’s completely optional). While this subcommand is very handy, it was also very minimalistic - generating only main.ts and main_test.ts files.

To make it more useful and allow IDEs to discover that we just initialized a new Deno project, deno init will also generate a deno.jsonc file as well as a main_bench.ts file.

The output of the subcommand was also refreshed.

deno init in Deno v1.28:

$ deno init ./my_deno_project
✅ Project initialized
Run these commands to get started
  cd ./my_deno_project
  deno run main.ts
  deno test

deno init in Deno v1.29:

$ deno init ./my_deno_project
✅ Project initialized

Run these commands to get started

  cd ./my_deno_project

  // Run the program
  deno run main.ts

  // Run the program and watch for file changes
  deno task dev

  // Run the tests
  deno test

  // Run the benchmarks
  deno bench

Thanks to sigmaSd for contributing these improvements.

Config file improvements

Starting with this release you can specify file inclusions and exclusions for deno bench, as well as the lockfile inside your configuration file.

Configure deno bench:

{
  "bench": {
    "files": {
      "include": ["./benches/"],
      "exclude": ["./benches/testdata/"]
    }
  }
}

If you want to use a non-default filename for the lockfile, you can do it like so…

{
  "lock": "./lock.file"
}

…or you can disable using lockfile altogether:

{
  "lock": false
}

Your IDE should be able to suggest these new options automatically.

Thanks to Geert-Jan Zwiers and @roj1512 for contributing these improvements.

Warning when --allow-* flags used incorrectly

Deno allows you to opt-in into the sandbox by providing various --allow-* flags. When executing deno run, these flags need to come before the script name, otherwise they are passed to the script itself in Deno.args.

// example.js
console.log("cli args", Deno.args);
await Deno.writeTextFile("./hello.txt", "hello world");

Before Deno v1.29 you would get such output:

$ deno run example.js --allow-write
cli args [ "--allow-write" ]
⚠️  ┌ Deno requests write access to "./hello.txt".
   ├ Requested by `Deno.writeFile()` API
   ├ Run again with --allow-write to bypass this prompt.
   └ Allow? [y/n] (y = yes, allow; n = no, deny) >

We found that it’s a common mistake to place permission flags after the script name, so starting with this release, Deno will warn you that this might have been unintentional and suggest how to run the program properly:

$ deno run example.js --allow-write
Permission flags have likely been incorrectly set after the script argument.
To grant permissions, set them before the script argument. For example:
    deno run --allow-read=. main.js
cli args [ "--allow-write" ]
⚠️  ┌ Deno requests write access to "./hello.txt".
   ├ Requested by `Deno.writeFile()` API
   ├ Run again with --allow-write to bypass this prompt.
   └ Allow? [y/n] (y = yes, allow; n = no, deny) >

Thanks to Asher Gomez for contributing this improvement.

Show unresolved promise in top-level await

Deno supports top-level await which allows you to use the await keyword without having to wrap your code in an async IIFE. This is very useful and ergonomic, but in some situations the awaited promise never resolves - most often than not, due to a bug in the code:

// example.js
await new Promise((resolve) => {}); // this promise will never resolve!
console.log("hello world");

In such cases, an error was returned and program terminated execution:

deno run example.js
error: Module evaluation is still pending but there are no pending ops or dynamic imports. This situation is often caused by unresolved promises.

While the error suggests what might be the problem, it was up to the user to read through the code and try to spot where the problem lies. Thanks to a new v8 API a much nicer error is now shown along with stack trace of the unresolved promise:

$ deno run example.js
error: Top-level await promise never resolved
await new Promise((_resolve) => {});
^
    at <anonymous> (file:///Users/ib/dev/deno/example.js:1:1)

Ignore node_modules and .git folders by default

Starting with this release, all built-in Deno tooling will skip node_modules/ and .git/ directories by default. You can run tools like formatter (deno fmt) or linter (deno lint) without having to worry that they will start crawling through these big directories. You can still opt-in to search through these directories by specifying them explicitly (eg. deno fmt node_modules/).

deno check --remote flag renamed to --all

There was a discrepancy between the deno run and deno check commands where type checking all the code for deno run required doing deno run --check=all main.ts, but doing the same with deno check required using the --remote flag—deno check --remote main.ts.

This has now been resolved with --remote in deno check being renamed to --all, which also includes npm packages. The previous command will still work fine, but the new one is recommended.

New --inspect-wait CLI flag

Deno has an ability to connect to debuggers like Chrome DevTools, VSCode and others. You can do that if you specify either a --inspect or --inspect-brk flag. The former will enable the debugger and immediately run your code, while the latter will enable the debugger, wait for it to connect and stop execution on the first line of your code.

We received reports that there are situations, where it’s useful to wait for debugger to connect, but not necessarily break on the first line of user code. We’re happy to announce that we added a new --inspect-wait flag, which fills this requirement - it will wait for debugger session to be established before running user code, but it will not place a breakpoint on the first line of the code.

The --inspect-wait flag, similarly to --inspect and --inspect-brk allows you to provide an optional value with the network address for the debugger to connect to, like so: --inspect-wait=127.0.0.1:3000.

Inlay hints are enabled by default in VSCode extension

We added support for inlay hints in Deno v1.27. They were off by default, as we wanted to make sure that everything works fine. We are now confident that this feature is working as expected and we’re happy to announce that the latest release of VSCode extension for Deno now uses inlay hints by default (following default VSCode settings).

If you wish to disable them, you may do so by setting the following settings in your VSCode user settings:

{
  "deno.inlayHints.parameterNames.enabled": "none",
  "deno.inlayHints.parameterTypes.enabled": false,
  "deno.inlayHints.variableTypes.enabled": false,
  "deno.inlayHints.propertyDeclarationTypes.enabled": false,
  "deno.inlayHints.functionLikeReturnTypes.enabled": false,
  "deno.inlayHints.enumMemberValues.enabled": false
}

Alternatively, you could just disable inlay hints everywhere in VSCode by setting:

{
  "editor.inlayHints.enabled": "off"
}

Changes to Deno APIs

Stabilizations:

The following APIs have been stabilized in this release and no longer require the --unstable flag to be used:

  • Deno.TcpConn.setNoDelay()
  • Deno.TcpConn.setKeepAlive()

Removal of unstable Deno.spawn

The big change in this release is the removal of the unstable Deno.spawn(), Deno.spawnSync() and Deno.spawnChild() APIs. All of them have been replaced by a unified Deno.Command API. Subprocess APIs are hard to design to be both ergonomic for common tasks and flexible enough when you need low-level control of the subprocess you are spawning.

Let’s see examples on how to migrate your code:

const { stdout, stderr } = await Deno.spawn("echo", { args: ["foo"] });

// becomes...

const { stdout, stderr } = await new Deno.Command("echo", { args: ["foo"] })
  .output();
const { stdout, stderr } = Deno.spawnSync("echo", { args: ["foo"] });

// becomes...

const { stdout, stderr } = new Deno.Command("echo", { args: ["foo"] })
  .outputSync();
const child = Deno.spawnChild("cat", { stdin: "piped" });

// becomes...

const child = new Deno.Command("cat", { stdin: "piped" }).spawn();

Deno.Command#.spawn() is “inherit” by default for stdout and stderr

The unstable Deno.Command is now “inherit” by default for stdout and stderr when calling .spawn(). It remains as “piped” by default when calling .output() and .outputSync().

// outputs "1\n" to stdout and "2\n" to stderr
new Deno.Command("deno", {
  args: ["eval", "console.log(1); console.error(2);"],
}).spawn();

createNew option for Deno.writeFile and Deno.writeTextFile

This release adds a createNew option to Deno.writeFile, Deno.writeTextFile, and the corresponding synchronous APIs.

// ok
await Deno.writeTextFile("file.txt", "some text", { createNew: true });
// error: Deno.errors.AlreadyExists
await Deno.writeTextFile("file.txt", "some text", { createNew: true });

Previously, you would have needed to use the Deno.open API to get this functionality, which was overly verbose.

TypeScript 4.9

Deno v1.29 ships with the latest stable version of TypeScript. For more information on new features in TypeScript see TypeScript’s 4.9 blog post

Changes to the standard modules

Addition of testing/types module

In this release the testing/types module was added. It is a testing utility for checking the behaviors of complex types. The module is inspired by and ported from Conditional Type Checks by David Sherret.

import {
  assertType,
  IsExact,
  IsNullable,
} from "https://deno.land/[email protected]/testing/types.ts";

const result = "some result" as string | number;

// compile error if the type of `result` is not exactly `string | number`
assertType<IsExact<typeof result, string | number>>(true);

// causes a compile error that `true` is not assignable to `false`
assertType<IsNullable<string>>(true); // error: string is not nullable

Thanks Lino Le Van for contributing this feature.

Structure changes towards single-export pattern

In the standard modules, multiple APIs are implemented in a single file in some places. The examples of such modules are bytes, datetime, io, etc. On the other hand, some modules have the pattern that each exported item is implemented in a single file (We call this pattern single-export pattern here). The examples of such modules are collections, fs, etc. There wasn’t explicit policy about which pattern to use for organizing modules so far, but we decided to move to the latter (single-export) pattern wherever it’s appropriate.

In this release the following 4 modules, archive, bytes, io, and stream, are restructured into single-export pattern. As a result, the following paths are deprecated, and will be removed in later releases (See the deprecation messages in IDEs for the exact versions of removals). Please update your import paths if you import from these paths.

  • std/streams/buffer.ts
  • std/streams/conversion.ts
  • std/streams/delimiter.ts
  • std/io/buffer.ts (except Buffer class)
  • std/io/files.ts
  • std/io/readers.ts
  • std/io/util.ts
  • std/io/writers.ts
  • std/archive/tar.ts (Untar export is deprecated.)

You can find the updated paths in the search dialog of the homepage.

Thanks Asher Gomez for suggesting and contributing this change.

API renames

The following APIs are renamed in this release. Please update them if you use these APIs directly in your code.

  • std/dotenv/mod.ts
    • config -> load
    • configSync -> loadSync
    • Note: the parameters also renamed. Please see the type definition for details.
  • std/fmt/bytes.ts
    • prettyBytes -> format
  • std/fmt/duration.ts
    • prettyDuration -> format

Thanks Tim Reichen for suggesting and contributing these changes.




If you could see helping ship something in a future release, check out our job postings.