O Deno usa módulos ECMAScript como seu principal sistema de módulos, o padrão oficial para JavaScript. Você compartilha código entre arquivos com declarações import e export padrão e o executa diretamente, sem bundler nem etapa de build. Este é o mesmo sistema de módulos usado pelos navegadores, então o código que você escreve permanece portável entre ambientes.

O Deno também executa os módulos CommonJS mais antigos do Node (require e module.exports), então pacotes npm e projetos Node existentes funcionam sem conversão. Os dois sistemas têm suporte completo e interoperam, mas módulos ECMAScript são recomendados para código novo; use CommonJS principalmente quando uma dependência ou projeto existente ainda o utiliza.

O Deno decide qual sistema um arquivo usa pela extensão e pelo package.json mais próximo: um arquivo .mjs é sempre um ES module, um arquivo .cjs é sempre CommonJS, e arquivos .js e .ts são tratados como ES modules a menos que um package.json defina "type": "commonjs". O restante desta página cobre ES modules; veja suporte a CommonJS para saber como require, resolução de módulos e interoperabilidade ESM/CommonJS funcionam.

Importando módulos

Neste exemplo, a função add é importada de um módulo local calc.ts.

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

Você pode executar este exemplo chamando deno run main.ts no diretório que contém tanto main.ts quanto calc.ts.

Com módulos ECMAScript, especificadores de import local devem sempre incluir a extensão completa do arquivo. Ela não pode ser omitida.

// WRONG: missing file extension
import { add } from "./calc";

// CORRECT: includes file extension
import { add } from "./calc.ts";

Imports dinâmicos

As declarações import acima são estáticas: o especificador é uma string literal e o módulo carrega antes do seu código rodar. Você também pode carregar um módulo sob demanda com a função import(), que retorna uma promise para o namespace do módulo:

const { add } = await import("./calc.ts");

console.log(add(1, 2)); // 3

Como import() roda em tempo de execução, o especificador pode ser calculado e o módulo só é buscado e avaliado quando a chamada executa. Isso é útil para carregar código condicionalmente ou manter um recurso raramente usado fora do caminho de inicialização:

if (Deno.args.includes("--greet")) {
  const { greet } = await import("./greet.ts");
  greet();
}

Um import() dinâmico cujo especificador é uma string literal faz parte do grafo estático de módulos e carrega sem permissões extras. Um cujo especificador é calculado em tempo de execução é verificado contra o sistema de permissões quando roda: caminhos locais precisam de --allow-read e URLs remotas precisam de --allow-import. Veja Segurança para detalhes.

Metadados de import

Dentro de qualquer módulo, import.meta expõe informações sobre o módulo atual e helpers para resolver caminhos relativos a ele:

console.log(import.meta.url); // file:///path/to/main.ts
console.log(import.meta.main); // true if this is the entry module
console.log(import.meta.filename); // /path/to/main.ts (local modules only)
console.log(import.meta.dirname); // /path/to (local modules only)
console.log(import.meta.resolve("./data.json")); // resolves a specifier to a URL

import.meta.main é uma forma comum de fazer um arquivo funcionar tanto como entry point quanto como biblioteca importável:

export function createServer() {/* ... */}

// Only start the server when run directly, not when imported.
if (import.meta.main) {
  createServer();
}

filename e dirname são undefined para módulos remotos. Para um passo a passo completo, veja o tutorial de metadados de módulo.

Import attributes

O Deno oferece suporte à sintaxe de import attribute with { type: "json" } para importar arquivos JSON:

import data from "./data.json" with { type: "json" };

console.log(data.property); // Access JSON data as an object

A partir do Deno 2.4, também é possível importar módulos text e bytes.

import text from "./log.txt" with { type: "text" };

console.log(typeof text === "string");
// true
console.log(text);
// Hello from a text file

:::info imports text

Estável no Deno 2.8 e não exige mais uma flag.

:::

import bytes from "./image.png" with { type: "bytes" };

console.log(bytes instanceof Uint8Array);
// true
console.log(bytes);
// Uint8Array(12) [
//    72, 101, 108, 108, 111,
//    44,  32,  68, 101, 110,
//   111,  33
// ]

:::info imports bytes

Ainda experimental. Ative com a flag de CLI --unstable-raw-imports ou a opção unstable.raw-import em deno.json.

:::

Avaliação adiada de módulos

A partir do Deno 2.8, a proposta TC39 Deferred Module Evaluation tem suporte. A sintaxe import defer carrega um módulo (incluindo suas dependências), mas não executa seu código de nível superior até você ler pela primeira vez uma propriedade do namespace:

import defer * as expensive from "./expensive.ts";

console.log("startup is fast: expensive.ts has not run yet");

// Touching any property triggers synchronous evaluation
console.log(expensive.value);

Use isso para adiar o custo de módulos que só são necessários condicionalmente; por exemplo, código de caminhos de erro em uma ferramenta CLI ou feature flags cuja implementação é pesada para inicializar.

A proposta ainda está no TC39 Stage 3, então a sintaxe é considerada experimental e pode mudar. import padrão continua sendo o default correto.

Módulos WebAssembly

O Deno oferece suporte à importação direta de módulos Wasm:

import { add } from "./add.wasm";

console.log(add(1, 2));

Para saber mais, visite a seção WebAssembly.

Imports de data URL

O Deno oferece suporte à importação de data URLs, o que permite importar conteúdo que não está em um arquivo separado. Isso é útil para testes, prototipagem ou quando você precisa gerar módulos programaticamente.

Você pode criar módulos em tempo real usando o esquema de URL data::

// Import a simple JavaScript module from a data URL
import * as module from "data:application/javascript;base64,ZXhwb3J0IGNvbnN0IG1lc3NhZ2UgPSAiSGVsbG8gZnJvbSBkYXRhIFVSTCI7";
console.log(module.message); // Outputs: Hello from data URL

// You can also use the non-base64 format
const plainModule = await import(
  "data:application/javascript,export function greet() { return 'Hi there!'; }"
);
console.log(plainModule.greet()); // Outputs: Hi there!

// A simpler example with text content
const textModule = await import(
  "data:text/plain,export default 'This is plain text'"
);
console.log(textModule.default); // Outputs: This is plain text

O formato de data URL segue este padrão:

data:[<media type>][;base64],<data>

Para módulos JavaScript, use application/javascript como media type. TypeScript também é aceito com application/typescript. Este recurso é especialmente útil para testar módulos isoladamente e criar módulos mock durante testes.

Importando módulos e bibliotecas de terceiros

Ao trabalhar com módulos de terceiros no Deno, use a mesma sintaxe import que você usa para código local. Módulos de terceiros normalmente são importados de um registry remoto e começam com jsr:, npm: ou https://.

import { camelCase } from "jsr:@luca/[email protected]";
import { say } from "npm:[email protected]";
import { pascalCase } from "https://deno.land/x/case/mod.ts";

O Deno recomenda JSR, o registry JavaScript moderno, para módulos de terceiros. Lá, você encontrará muitos ES modules bem documentados para seus projetos, incluindo a Biblioteca Padrão do Deno.

Você pode ler mais sobre o suporte do Deno a pacotes npm aqui.

Gerenciando módulos e bibliotecas de terceiros

Digitar o nome do módulo com o especificador completo de versão pode ficar tedioso ao importá-los em vários arquivos. Você pode centralizar o gerenciamento de módulos remotos com um campo imports no seu arquivo deno.json. Chamamos esse campo imports de import map, baseado no 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"
  }
}

Com especificadores remapeados, o código fica mais limpo:

import { camelCase } from "@luca/cases";
import { say } from "cowsay";
import { pascalCase } from "cases";

O nome remapeado pode ser qualquer especificador válido. É um recurso muito poderoso no Deno, capaz de remapear qualquer coisa. Saiba mais na seção de dependências de configuração.

Diferença entre imports ou importMap em deno.json e a opção --import-map

O Import Maps Standard exige duas entradas para cada módulo: uma para o especificador do módulo e outra para o especificador com uma / final. Isso acontece porque o padrão permite apenas uma entrada por especificador de módulo, e a / final indica que o especificador se refere a um diretório. Por exemplo, ao usar a opção --import-map import_map.json, o arquivo import_map.json deve incluir as duas entradas para cada módulo (observe o uso de jsr:/@std/async em vez de jsr:@std/async):

{
  "imports": {
    "@std/async": "jsr:@std/async@^1.0.0",
    "@std/async/": "jsr:/@std/async@^1.0.0/"
  }
}

Um arquivo import_map.json referenciado pelo campo importMap em deno.json se comporta exatamente igual ao uso da opção --import-map, com os mesmos requisitos de incluir as duas entradas para cada módulo, como mostrado acima.

Em contraste, deno.json estende o padrão de import maps. Quando você usa o campo imports em deno.json, só precisa especificar o especificador do módulo sem a / final:

{
  "imports": {
    "@std/async": "jsr:@std/async@^1.0.0"
  }
}

Gerenciando dependências

Agora que você entende como o Deno resolve e importa módulos, veja o guia de gerenciamento de dependências para as tarefas do dia a dia: adicionar e remover pacotes com deno add / deno remove, fixar versões, sobrescrever e vendorizar dependências, lockfiles e verificação de integridade, gestão de supply chain, publicar seus próprios módulos e usar registries privados.

Atualizando versões pela linha de comando

Você não precisa editar números de versão em deno.json manualmente. Para mover dependências para versões mais novas, execute deno outdated para ver o que está atrasado, depois deno outdated --update para atualizá-las:

deno outdated            # list dependencies with newer versions available
deno outdated --update   # update them in deno.json

Para incrementar o campo version do seu próprio pacote entre releases, use deno bump-version:

deno bump-version patch  # 1.4.6 -> 1.4.7 (also: minor, major, or a prerelease)