On this page
Using modules in Deno
Deno uses ECMAScript modules as its default module system to align with modern JavaScript standards and to promote a more efficient and consistent development experience. It's the official standard for JavaScript modules, allows for better tree-shaking, improved tooling integration, and native support across different environments.
By adopting ECMAScript modules, Deno ensures compatibility with the ever-evolving JavaScript ecosystem. For developers, this means a streamlined and predictable module system that avoids the complexities associated with legacy module formats like CommonJS.
Importing modules Jump to heading
In this example the add
function is imported from a local calc.ts
module.
export function add(a: number, b: number): number {
return a + b;
}
// imports the `calc.ts` module next to this file
import { add } from "./calc.ts";
console.log(add(1, 2)); // 3
You can run this example by calling deno run main.ts
in the directory that
contains both main.ts
and calc.ts
.
With ECMAScript modules, local import specifiers must always include the full file extension. It cannot be omitted.
// WRONG: missing file extension
import { add } from "./calc";
// CORRECT: includes file extension
import { add } from "./calc.ts";
Importing third party modules and libraries Jump to heading
When working with third-party modules in Deno, use the same import
syntax as
you do for local code. Third party modules are typically imported from a remote
registry and start with jsr:
, npm:
or https://
.
import { camelCase } from "jsr:@luca/cases@1.0.0";
import { say } from "npm:cowsay@1.6.0";
import { pascalCase } from "https://deno.land/x/case/mod.ts";
Deno recommends JSR, the modern JavaScript registry, for third party modules. There, you'll find plenty of well documented ES modules for your projects, including the Deno Standard Library.
You can read more about Deno's support for npm packages here.
Managing third party modules and libraries Jump to heading
Typing out the module name with the full version specifier can become tedious
when importing them in multiple files. You can centralize management of remote
modules with an imports
field in your deno.json
file. We call this imports
field the import map, which is based on the
Import Maps Standard.
{
"imports": {
"@luca/cases": "jsr:@luca/cases@^1.0.0",
"cowsay": "npm:cowsay@^1.6.0",
"cases": "https://deno.land/x/case/mod.ts"
}
}
With remapped specifiers, the code looks cleaner:
import { camelCase } from "@luca/cases";
import { say } from "cowsay";
import { pascalCase } from "cases";
The remapped name can be any valid specifier. It's a very powerful feature in Deno that can remap anything. Learn more about what the import map can do here.
Adding dependencies with deno add
Jump to heading
The installation process is made easy with the deno add
subcommand. It will
automatically add the latest version of the package you requested to the
imports
section in deno.json
.
# Add the latest version of the module to deno.json
$ deno add jsr:@luca/cases
Add @luca/cases - jsr:@luca/cases@1.0.0
{
"imports": {
"@luca/cases": "jsr:@luca/cases@^1.0.0"
}
}
You can also specify an exact version:
# Passing an exact version
$ deno add jsr:@luca/cases@1.0.0
Add @luca/cases - jsr:@luca/cases@1.0.0
Package Versions Jump to heading
It is possible to specify a version range for the package you are importing.
This is done using the @
symbol followed by a version range specifier, and
follows the semver versioning scheme.
For example:
@scopename/mypackage # highest version
@scopename/mypackage@16.1.0 # exact version
@scopename/mypackage@16 # highest 16.x version >= 16.0.0
@scopename/mypackage@^16.1.0 # highest 16.x version >= 16.1.0
@scopename/mypackage@~16.1.0 # highest 16.1.x version >= 16.1.0
Here is an overview of all the ways you can specify a version or a range:
Symbol | Description | Example |
---|---|---|
1.2.3 |
An exact version. Only this specific version will be used. | 1.2.3 |
^1.2.3 |
Compatible with version 1.2.3. Allows updates that do not change the leftmost non-zero digit. For example, 1.2.4 and 1.3.0 are allowed, but 2.0.0 is not. |
^1.2.3 |
~1.2.3 |
Approximately equivalent to version 1.2.3. Allows updates to the patch version. For example, 1.2.4 is allowed, but 1.3.0 is not. |
~1.2.3 |
>=1.2.3 |
Greater than or equal to version 1.2.3. Any version 1.2.3 or higher is allowed. |
>=1.2.3 |
<=1.2.3 |
Less than or equal to version 1.2.3. Any version 1.2.3 or lower is allowed. |
<=1.2.3 |
>1.2.3 |
Greater than version 1.2.3. Only versions higher than 1.2.3 are allowed. |
>1.2.3 |
<1.2.3 |
Less than version 1.2.3. Only versions lower than 1.2.3 are allowed. |
<1.2.3 |
1.2.x |
Any patch version within the minor version 1.2. For example, 1.2.0 , 1.2.1 , etc. |
1.2.x |
1.x |
Any minor and patch version within the major version 1. For example, 1.0.0 , 1.1.0 , 1.2.0 , etc. |
1.x |
* |
Any version is allowed. | * |
HTTPS imports Jump to heading
Deno also supports import statements that reference HTTP/HTTPS URLs, either directly:
import { Application } from "https://deno.land/x/oak/mod.ts";
or part of your deno.json
import map:
{
"imports": {
"oak": "https://deno.land/x/oak/mod.ts"
}
}
Supporting HTTPS imports enables us to support the following JavaScript CDNs, as they provide URL access to JavaScript modules:
HTTPS imports are useful if you have a small, often single file, Deno project
that doesn't require any other configuration. With HTTPS imports, you can avoid
having a deno.json
file at all. It is not advised to use this style of
import in larger applications however, as you may end up with version conflicts
(where different files use different version specifiers).
Use HTTPS imports with caution, and only from trusted sources. If the server is compromised, it could serve malicious code to your application. They can also cause versioning issues if you import different versions in different files. HTTPS imports remain supported, but we recommend using a package registry for the best experience.
Overriding HTTPS imports Jump to heading
The other situation where import maps can be very useful is to override HTTPS imports in specific modules.
Let's say you want to override a https://deno.land/x/my-library@1.0.0/mod.ts
specifier that is used inside files coming from https://deno.land/x/example/
to a local patched version. You can do this by using a scope in the import map
that looks something like this:
{
"imports": {
"example/": "https://deno.land/x/example/"
},
"scopes": {
"https://deno.land/x/example/": {
"https://deno.land/x/my-library@1.0.0/mod.ts": "./patched/mod.ts"
}
}
}
HTTPS imports have no notion of packages. Only the import map at the root of your project is used. Import maps used inside URL dependencies are ignored.
Publishing modules Jump to heading
Any Deno program that defines an export can be published as a module. This allows other developers to import and use your code in their own projects. Modules can be published to:
- JSR - recommended, supports TypeScript natively and auto-generates documentation for you
- npm - use dnt to create the npm package
- deno.land/x - for HTTPS imports, use JSR instead if possible
Reloading modules Jump to heading
By default, Deno uses a global cache directory (DENO_DIR
) for downloaded
dependencies. This cache is shared across all projects.
You can force deno to refetch and recompile modules into the cache using the
--reload
flag.
# Reload everything
deno run --reload my_module.ts
# Reload a specific module
deno run --reload=jsr:@std/fs my_module.ts
Using only cached modules Jump to heading
To force Deno to only use modules that have previously been cached, use the
--cached-only
flag:
deno run --cached-only mod.ts
This will fail if there are any dependencies in the dependency tree for mod.ts which are not yet cached.
Vendoring remote modules Jump to heading
If your project has external dependencies, you may want to store them locally to avoid downloading them from the internet every time you build your project. This is especially useful when building your project on a CI server or in a Docker container, or patching or otherwise modifying the remote dependencies.
Deno offers this functionality through a setting in your deno.json
file:
{
"vendor": true
}
Add the above snippet to your deno.json
file and Deno will cache all
dependencies locally in a vendor
directory when the project is run, or you can
optionally run the deno install --entrypoint
command to cache the dependencies
immediately:
deno install --entrypoint main.ts
You can then run the application as usual with deno run
:
deno run main.ts
After vendoring, you can run main.ts
without internet access by using the
--cached-only
flag, which forces Deno to use only locally available modules.
Integrity Checking and Lock Files Jump to heading
Imagine your module relies on a remote module located at https://some.url/a.ts.
When you compile your module for the first time, a.ts
is fetched, compiled,
and cached. This cached version will be used until you either run your module on
a different machine (such as in a production environment) or manually reload the
cache (using a command like deno install --reload
).
But what if the content at https://some.url/a.ts
changes? This could result in
your production module running with different dependency code than your local
module. To detect this, Deno uses integrity checking and lock files.
Deno uses a deno.lock
file to check external module integrity. To opt into a
lock file, either:
-
Create a
deno.json
file in the current or an ancestor directory, which will automatically create an additive lockfile atdeno.lock
.Note that this can be disabled by specifying the following in your deno.json:
deno.json{ "lock": false }
-
Use the
--lock
flag to enable and specify lock file checking.
Frozen lockfile Jump to heading
By default, Deno uses an additive lockfile, where new dependencies are added to the lockfile instead of erroring.
This might not be desired in certain scenarios (ex. CI pipelines or production
environments) where you'd rather have Deno error when it encounters a dependency
it's never seen before. To enable this, you can specify the --frozen
flag or
set the following in a deno.json file:
{
"lock": {
"frozen": true
}
}
When running a deno command with a frozen lockfile, 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.
If you wish to update the lockfile, specify --frozen=false
on the command line
to temporarily disable the frozen lockfile.
Changing lockfile path Jump to heading
The lockfile path can be configured by specifying --lock=deps.lock
or the
following in a Deno configuration file:
{
"lock": {
"path": "deps.lock"
}
}