TypeScript e Deno: módulos e permissões
1. Introdução ao Deno como Runtime TypeScript Nativo
Deno, criado por Ryan Dahl (mesmo criador do Node.js), surgiu como uma resposta às limitações arquiteturais do Node.js. Diferentemente do Node, que trata TypeScript como um "cidadão de segunda classe" exigindo transpilação prévia com tsc ou ts-node, o Deno possui suporte nativo a TypeScript. Isso significa que arquivos .ts são executados diretamente sem configuração adicional de compiladores.
Enquanto o Node.js gerencia dependências via node_modules e package.json, o Deno adota um modelo descentralizado baseado em URLs. Não há mais a necessidade de um diretório gigantesco de módulos — as dependências são baixadas e cacheadas sob demanda. Para projetos TypeScript modernos que buscam simplicidade e segurança, Deno representa uma alternativa atraente, eliminando a complexidade de configurações de build e oferecendo um runtime verdadeiramente seguro por padrão.
2. Sistema de Módulos no Deno: Importando com URLs
No Deno, a importação de módulos é feita diretamente via URLs. Isso elimina a necessidade de um registro centralizado como o npm:
// Importando um módulo de URL absoluta
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
// Importando módulo local relativo
import { minhaFuncao } from "./utils/helpers.ts";
// Importando com alias para versionamento
import { concat } from "https://deno.land/x/concatenate@v1.0.0/mod.ts";
O versionamento é embutido na própria URL, garantindo reprodutibilidade. O Deno gerencia um cache global e um arquivo deno.lock que registra as versões exatas das dependências:
# Atualizar cache e lock file
deno cache --lock=deno.lock --lock-write src/main.ts
3. Módulos Padrão do Deno e Compatibilidade com npm
O Deno oferece uma biblioteca padrão (std/) rica e auditada, que cobre operações comuns:
import { copySync, ensureDir } from "https://deno.land/std@0.224.0/fs/mod.ts";
import { join, extname } from "https://deno.land/std@0.224.0/path/mod.ts";
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
// Exemplo prático
const caminho = join("data", "arquivo.txt");
console.log(`Extensão: ${extname(caminho)}`);
ensureDir("./logs");
copySync("origem.txt", "destino.txt");
Além disso, o Deno suporta pacotes npm diretamente via npm: specifier:
// Importando pacote npm
import express from "npm:express@4.18.2";
import { v4 as uuidv4 } from "npm:uuid@9.0.0";
// Usando módulos built-in do Node para compatibilidade
import { readFileSync } from "node:fs";
import { createServer } from "node:http";
Isso permite uma transição gradual de projetos Node.js para Deno, mantendo dependências existentes.
4. O Sistema de Permissões do Deno
Deno adota uma abordagem de segurança radical: por padrão, nenhum acesso a arquivos, rede ou ambiente é permitido. O desenvolvedor deve explicitamente conceder permissões via flags:
// Este código falhará sem permissões adequadas
const conteudo = await Deno.readTextFile("./dados.json");
console.log(conteudo);
Para executar, é necessário:
# Conceder permissão de leitura
deno run --allow-read ./app.ts
# Múltiplas permissões
deno run --allow-read --allow-write --allow-net --allow-env ./app.ts
# Permissão granular para diretórios específicos
deno run --allow-read=/home/user/data --allow-write=/tmp ./app.ts
# Negar permissões específicas (útil em scripts com muitas permissões)
deno run --allow-read --allow-write --deny-net ./app.ts
As principais flags de permissão são:
- --allow-read: leitura de arquivos
- --allow-write: escrita de arquivos
- --allow-net: acesso à rede
- --allow-env: acesso a variáveis de ambiente
- --allow-run: execução de subprocessos
- --allow-sys: acesso a informações do sistema
5. Configuração de Permissões com deno.json e deno.jsonc
Para projetos maiores, gerenciar permissões via linha de comando torna-se impraticável. O Deno permite declarar permissões no arquivo de configuração:
// deno.jsonc
{
"compilerOptions": {
"strict": true,
"noUnusedLocals": true
},
"tasks": {
"start": "deno run --allow-net --allow-read=./public main.ts",
"test": "deno test --allow-read --allow-net"
},
"lint": {
"rules": {
"exclude": ["no-explicit-any"]
}
}
}
Para permissões estáticas, use deno.json com a seção permissions:
{
"permissions": {
"read": true,
"write": false,
"net": ["deno.land", "api.exemplo.com"],
"env": ["DB_HOST", "DB_PORT"]
}
}
O import_map.json permite gerenciar módulos e permissões conjuntamente:
{
"imports": {
"std/": "https://deno.land/std@0.224.0/",
"lodash": "npm:lodash@4.17.21"
}
}
6. Exemplos Práticos: Aplicação TypeScript com Módulos e Permissões
Servidor HTTP com permissão de rede
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
const handler = (req: Request): Response => {
const url = new URL(req.url);
if (url.pathname === "/api/status") {
return new Response(JSON.stringify({ status: "ok" }), {
headers: { "Content-Type": "application/json" },
});
}
return new Response("Hello TypeScript + Deno!", {
headers: { "Content-Type": "text/plain" },
});
};
console.log("Servidor rodando em http://localhost:3000");
await serve(handler, { port: 3000 });
deno run --allow-net server.ts
Leitura de arquivos com tratamento de erros
import { join } from "https://deno.land/std@0.224.0/path/mod.ts";
async function lerArquivoSeguro(caminho: string): Promise<string | null> {
try {
const dados = await Deno.readTextFile(caminho);
return dados;
} catch (erro) {
if (erro instanceof Deno.errors.PermissionDenied) {
console.error("Permissão negada para ler o arquivo");
return null;
}
if (erro instanceof Deno.errors.NotFound) {
console.error("Arquivo não encontrado");
return null;
}
throw erro;
}
}
const dados = await lerArquivoSeguro(join("data", "config.json"));
console.log(dados);
deno run --allow-read=./data leitor.ts
Combinação de módulos npm e Deno
import express from "npm:express@4.18.2";
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
import { v4 as uuid } from "npm:uuid@9.0.0";
const app = express();
app.get("/api/items", (_req, res) => {
res.json([
{ id: uuid(), name: "Item 1" },
{ id: uuid(), name: "Item 2" },
]);
});
// Adaptando Express para o runtime Deno
const handler = (req: Request) => {
// Lógica de adaptação...
return new Response("Funcionando!");
};
await serve(handler, { port: 8080 });
7. Debugging e Testes com Deno e TypeScript
Deno oferece suporte nativo a testes com tipagem TypeScript:
// math.ts
export function soma(a: number, b: number): number {
return a + b;
}
// math_test.ts
import { assertEquals } from "https://deno.land/std@0.224.0/testing/asserts.ts";
import { soma } from "./math.ts";
Deno.test("soma de dois números positivos", () => {
assertEquals(soma(2, 3), 5);
});
Deno.test("soma com zero", () => {
assertEquals(soma(0, 5), 5);
});
# Executar testes com permissões específicas
deno test --allow-read --allow-net
# Testes com cobertura
deno test --coverage=coverage
Para debugging no VS Code, configure o launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Deno",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "deno",
"runtimeArgs": ["run", "--inspect-wait", "--allow-all"],
"args": ["${file}"],
"attachSimplePort": 9229
}
]
}
Ferramentas nativas de lint e formatação:
# Formatação automática
deno fmt
# Linting
deno lint
# Verificação de tipos sem executar
deno check src/
8. Migração de Node.js para Deno: Desafios e Estratégias
Migrar projetos Node.js para Deno requer atenção a alguns pontos:
Adaptando imports CommonJS para ESM
// Antes (Node.js CommonJS)
const fs = require("fs");
const path = require("path");
// Depois (Deno ESM)
import * as fs from "node:fs";
import * as path from "node:path";
Gerenciando permissões ao migrar
// Script legado que precisa de várias permissões
async function migrarScript() {
// Usar permissões granulares
const config = JSON.parse(
await Deno.readTextFile("./config.json")
);
// Substituir acesso direto a variáveis de ambiente
const dbUrl = Deno.env.get("DATABASE_URL");
// Usar fetch nativo em vez de axios
const response = await fetch("https://api.exemplo.com");
}
deno run --allow-read=./config.json --allow-env=DATABASE_URL --allow-net=api.exemplo.com migrar.ts
Ferramentas de compatibilidade
# Compilar para executável único
deno compile --allow-read --allow-net app.ts
# Verificar compatibilidade com Node.js
deno check --compat app.ts
Para projetos complexos, considere usar o nodeCompat em deno.json:
{
"compilerOptions": {
"lib": ["deno.window"]
},
"unstable": ["node-compat"]
}
Referências
- Documentação Oficial do Deno - Módulos — Guia completo sobre o sistema de módulos baseado em URLs do Deno
- Deno Standard Library — Catálogo oficial da biblioteca padrão com exemplos de uso
- Deno Permissions Documentation — Documentação detalhada sobre o sistema de permissões
- Deno and npm: A Match Made in Heaven — Artigo oficial sobre compatibilidade com pacotes npm
- Migrating from Node.js to Deno — Guia oficial de migração com exemplos práticos
- Deno Configuration File Reference — Referência completa do arquivo deno.json e suas opções
- TypeScript in Deno: A Complete Guide — Artigo aprofundado sobre como o Deno lida com TypeScript nativamente