In this second article of the Deno series, I want to talk about modules.
This article is a transcript of a Youtube video I made.
If you used Node before -- or any server-side language for that matter -- you will know the gist about modules.
Splitting your code into modules can help you keep your project organized or enable library makers to
share their work.
Browser-side, modules have been available for about four years now.
Before, developers would add their namespace to the global object hoping there wouldn't be any collision.
If you are more of a front-end-type-person, you probably started using modules through babel or webpack.
Deno implements the ES modules specifications, which means we're importing stuff, not requiring stuff.
Working with ES modules is great because it means that Deno code can be ported to modern browser -- it isn't the case with Node code.
Alright -- let's fire up our editor and write some modules.
In this demo I will cover how to export and import modules and, how to ensure the integrity of your dependencies.
During this session, I will assume that you have a basic understanding of JavaScript and it's ecosystem. If you have any
question, don't hesitate to write them in the comment section bellow.
Take this code where we export a string and function.
// users.js
export const targetURL = "https://randomuser.me/api/";
export function getUser () {
return fetch(`${targetURL}?inc=name,email,login,id`)
.then(response => response.json())
.then(({ results: [ user ] }) => user);
}
We can import it like this using the relative path.
// scratch.js
import * as users from "./users.js";
users.getUser()
.then(user => {
console.log(`Welcome to ${user.name.title}. ${user.name.last}`);
});
You can use the Deno run
subcommand with the appropriate flag.
deno run --allow-net=randomuser.me ./sratch.js
You can also use the remote URL to the file when using a third-party dependency.
// scratch.js
import * as users from "https://raw.githubusercontent.com/sebastienfilion/i-y/main/deno/02/users.js";
If the resource is on a private server, Deno has an environment variable DENO_AUTH_TOKENS
that can be used as bearer token when making the request to the server.
// scratch.js
import * as users from "https://raw.githubusercontent.com/sebastienfilion/i-y-private/main/02/users.js";
DENO_AUTH_TOKENS=████████████████████████████████████████@raw.githubusercontent.com deno run mod.js
In the Deno community, there are some conventions when naming files. The first one is to have a mod.js
file to export all public files -- it's a good practice when developing libraries.
// mod.js
export * from "./users.js";
The second one is to have a deps.js
file to import and expose third-party dependencies.
// deps.js
export * from "https://raw.githubusercontent.com/sebastienfilion/i-y/main/deno/02/mod.js";
Alternatively, you can create a JSON file to use as import-map for the dependencies. It's useful when you want to import dependencies by name instead of by URL.
{
"imports": {
"i-y": "https://raw.githubusercontent.com/sebastienfilion/i-y/main/02/mod.js"
}
}
// scratch.js
import * as users from "i-y";
deno run --allow-net=randomuser.me --import-map="map.json" ./scratch.js
Warning: import-map can't be composed; so if you are a library maker, try to avoid them.
When there's a new version of your dependencies available, you may need to refresh your cache.
// users.js
export const targetURL = "https://randomuser.me/api/";
export function getUser () {
return fetch(`${targetURL}?inc=name,email,login,id`)
.then(response => response.json())
.then(({ results: [ { name: { title: "title, last: surname } } ] }) => ({ title, surname }));"
}
// scratch.js
import * as users from "https://raw.githubusercontent.com/sebastienfilion/i-y/main/02/users.js";
users.getUser()
.then(user => {
console.log(`Welcome to ${user.title}. ${user.surname}`);
});
You can force the runtime to reload all dependencies with the --reload
flag.
deno run --allow-net=randomuser.me --reload ./sratch.js
Or you can be more granular and specify the dependencies you want to reload.
deno run --allow-net=randomuser.me --reload=https://raw.githubusercontent.com/sebastienfilion/i-y ./sratch.js
Also, you can use the cache
subcommand.
deno cache --reload
deno run --allow-net=randomuser.me ./sratch.js
Finally, you can ensure integrity of your dependencies by writing a lock file.
deno cache --lock=lock.json --lock-write ./scratch.js
If something happens to the dependencies, you'd get an error warning you of the changes...
deno run --lock=lock.json --allow-net=randomuser.me --reload ./scratch.js
[error]
Okay. Up until now, we worked with modules, importing local files and remote files.
We played with fire by not versioning our dependencies and relied on the integrity check to save us from ourselves.
The Deno community also maintains module registries, similar to NPM. These registries enforce SemVer on all modules -- this is an essential when dealing with multiple dependencies.
We are going to visit two registries, but there are more.
First up is deno/x -- the Deno core team maintains this registry. It is simple to use you can leverage Github Webhook to publish a module.
Next up is Nest -- Nest publishes modules to the permaweb, meaning that they are permanent and immutable.
They do this through blockchain technology. Unlike deno/x, Nest uses a CLI tool to publish modules.
Using a module from one of these registries, you will notice that the path includes a version number that ensures that your import code is immutable.
It also means that you can -- in theory -- import different versions from
the same source.
In this episode, we experimented with Deno modules.
We also did an overview of the many ways you can publish a module within the Deno-verse.
On the next episode, I want to cover the many tools that come out of the box when installing Deno.
You have a linter, a formatter, a test runner, a bundler, an installer and, so much more; I might have to make it a three-parter.
Top comments (2)
Great article, keep it up!
You really explored all the aspects of deno imports and explained them very well
Good stuff