Deno 2.4 acaba de ser liberado, y debo admitir que me ha sorprendido gratamente. No solo por la cantidad de características nuevas, sino por una en particular que muchos creíamos que no volvería: deno bundle
ha regresado. Y esta vez, para quedarse.
Esta versión viene cargada de mejoras que van desde importar archivos de texto directamente hasta observabilidad estable con OpenTelemetry. Vamos a ver qué nos trae esta release.
El regreso triunfal de deno bundle
Para los que llevamos tiempo con Deno, deno bundle
fue una de esas características que usábamos constantemente hasta que fue deprecada en 2021. El equipo de Deno admitió que el bundling es un problema complejo y que no podían hacerlo bien.
¿Qué cambió? Ahora deno bundle
usa esbuild por debajo, lo que significa que tenemos toda la potencia y madurez de una herramienta probada en batalla.
Uso básico
# Bundle con minificación
deno bundle --minify main.ts
# Especificar plataforma (browser en lugar de deno)
deno bundle --platform browser --output bundle.js app.jsx
# Generar sourcemap externo
deno bundle --platform browser --output bundle.js --sourcemap=external app.jsx
Lo que más me gusta
- Funciona con npm y JSR - Sin configuración adicional
- Tree shaking automático - Solo incluye lo que necesitas
- Minificación incluida - Via esbuild
- Soporte para browser y server - Un comando, múltiples targets
Ejemplo práctico
// main.ts
import { serve } from "jsr:@std/http/serve";
import cowsay from "npm:cowsay";
serve((req) => {
const url = new URL(req.url);
const message = url.searchParams.get("message") || "Hello from Deno!";
return new Response(cowsay.say({ text: message }), {
headers: { "content-type": "text/plain" }
});
});
# Bundle para browser con dependencias npm
deno bundle --platform browser --minify --output server-bundle.js main.ts
# El resultado es un único archivo con todo incluido
ls -la server-bundle.js
# -rw-r--r-- 1 user user 45K server-bundle.js
Import directo de texto y bytes
Esta es una característica que llevaba tiempo esperando. Ahora puedes importar archivos de texto, binarios, JSON y WASM directamente en tu código JavaScript.
Import attributes en acción
// Importar como texto
import readme from "./README.md" with { type: "text" };
import config from "./config.json" with { type: "json" };
// Importar como bytes
import imageData from "./logo.png" with { type: "bytes" };
import iconData from "./favicon.ico" with { type: "bytes" };
// Usar en un servidor
Deno.serve((req) => {
const url = new URL(req.url);
if (url.pathname === "/readme") {
return new Response(readme, {
headers: { "content-type": "text/markdown" }
});
}
if (url.pathname === "/logo") {
return new Response(imageData, {
headers: {
"content-type": "image/png",
"content-length": imageData.byteLength.toString()
}
});
}
return new Response("Not found", { status: 404 });
});
Funciona con bundle y compile
Lo mejor de todo es que esto funciona tanto con deno bundle
como con deno compile
:
# Incluir assets en el bundle
deno bundle --unstable-raw-imports --output app.js main.ts
# O compilar a binario con assets embebidos
deno compile --unstable-raw-imports --output app main.ts
Ejemplo con diccionario embebido
// spellcheck.ts
import dictData from "./dictionary.txt" with { type: "text" };
const WORDS = new Set(dictData.split("\n").map(w => w.trim().toLowerCase()));
function checkWord(word: string): boolean {
return WORDS.has(word.toLowerCase());
}
// CLI simple
while (true) {
const input = prompt("> Enter word to check: ");
if (!input) continue;
console.log(
checkWord(input)
? `✅ "${input}" is a valid word`
: `❌ "${input}" is not in dictionary`
);
}
# Compilar con diccionario embebido
deno compile --unstable-raw-imports spellcheck.ts
# El binario resultante incluye todo el diccionario
./spellcheck
> Enter word to check: javascript
✅ "javascript" is a valid word
OpenTelemetry estable
Una de las características más potentes de Deno 2.4 es que OpenTelemetry ya es estable. Esto significa observabilidad automática sin configuración.
Activación simple
# Ya no necesitas --unstable-otel
OTEL_DENO=1 deno --allow-net server.ts
Lo que obtienes automáticamente
- Logs asociados a requests HTTP
- Trazas automáticas de operaciones asíncronas
- Métricas de rendimiento
- Integración con herramientas estándar (Jaeger, Zipkin, etc.)
Ejemplo con servidor web
// server.ts
import { serve } from "jsr:@std/http/serve";
serve(async (req) => {
console.log(`Processing ${req.method} ${req.url}`);
// Simular trabajo asíncrono
await new Promise(resolve => setTimeout(resolve, 100));
// Operación que podría fallar
if (Math.random() > 0.8) {
console.error("Random error occurred!");
return new Response("Internal Error", { status: 500 });
}
console.log("Request completed successfully");
return new Response("Hello World!");
});
# Ejecutar con observabilidad
OTEL_DENO=1 deno --allow-net server.ts
# Los logs se asocian automáticamente con traces
# Puedes enviar los datos a tu sistema de observabilidad favorito
Modificar el entorno con –preload
El nuevo flag --preload
permite ejecutar código antes de tu script principal. Esto abre posibilidades interesantes para configurar el entorno.
Casos de uso
// setup.ts - Modificar globals
globalThis.DEBUG = true;
globalThis.API_BASE = "https://api.example.com";
// Polyfills personalizados
if (!globalThis.fetch) {
globalThis.fetch = async () => new Response("Mock response");
}
// Eliminar APIs (para sandboxing)
delete Deno.writeFile;
delete Deno.remove;
console.log("Environment configured!");
// main.ts
console.log("DEBUG mode:", globalThis.DEBUG);
console.log("API Base:", globalThis.API_BASE);
// Esto fallará si se ejecutó setup.ts
try {
await Deno.writeFile("test.txt", new TextEncoder().encode("test"));
} catch (error) {
console.log("Write blocked by preload script");
}
# Ejecutar con preload
deno --preload setup.ts main.ts
# Output:
# Environment configured!
# DEBUG mode: true
# API Base: https://api.example.com
# Write blocked by preload script
Para frameworks y plataformas
Esto es especialmente útil si estás construyendo tu propia plataforma o framework:
// platform-setup.ts
// Configurar base de datos
globalThis.db = await connect("sqlite:///app.db");
// Cargar configuración
globalThis.config = JSON.parse(await Deno.readTextFile("config.json"));
// APIs personalizadas
globalThis.sendEmail = async (to: string, subject: string, body: string) => {
// Implementación de email
};
globalThis.logEvent = (event: string, data: any) => {
// Implementación de logging
};
Gestión de dependencias simplificada
deno update
es el nuevo comando para actualizar dependencias:
# Actualizar a versiones semver compatibles
deno update
# Ignorar restricciones semver
deno update --latest
# Filtrar por patrón
deno update "@std/*"
# Incluir workspaces
deno update --recursive
En la práctica
// deno.json antes
{
"imports": {
"@std/http": "jsr:@std/http@0.220.0",
"@std/assert": "jsr:@std/assert@0.218.0",
"express": "npm:express@4.18.0"
}
}
deno update --latest
// deno.json después
{
"imports": {
"@std/http": "jsr:@std/http@0.224.0",
"@std/assert": "jsr:@std/assert@0.220.0",
"express": "npm:express@4.19.2"
}
}
Coverage con deno run
Ahora puedes recopilar coverage directamente desde deno run
:
// math.ts
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
export function multiply(a: number, b: number): number {
if (a === 0 || b === 0) {
return 0;
}
return a * b;
}
// app.ts
import { add, multiply } from "./math.ts";
const result1 = add(5, 3);
const result2 = multiply(4, 2);
console.log({ result1, result2 });
# Ejecutar con coverage
deno run --coverage app.ts
# Ver reporte
deno coverage
# | File | Branch % | Line % |
# | ------- | -------- | ------ |
# | math.ts | 50.0 | 85.7 |
# | app.ts | 100.0 | 100.0 |
DENO_COMPAT=1: Compatibilidad simplificada
Para proyectos Node.js existentes, ahora tienes una variable de entorno que activa múltiples flags de compatibilidad:
# Antes (verbose)
deno --unstable-detect-cjs --unstable-node-globals --unstable-bare-node-builtins --unstable-sloppy-imports app.js
# Ahora (simple)
DENO_COMPAT=1 deno app.js
Qué activa DENO_COMPAT=1
--unstable-detect-cjs
- Detectar módulos CommonJS--unstable-node-globals
- Globals de Node.js disponibles--unstable-bare-node-builtins
- Import sinnode:
prefix--unstable-sloppy-imports
- Imports sin extensiones
Ejemplo con proyecto Node.js
// legacy-app.js (código Node.js existente)
const fs = require('fs');
const path = require('path');
const { Buffer } = require('buffer');
// Import sin extensión (sloppy imports)
const utils = require('./utils');
// Global de Node.js
process.env.NODE_ENV = 'development';
console.log('App running on Node.js APIs');
# Funciona directamente con DENO_COMPAT=1
DENO_COMPAT=1 deno legacy-app.js
Mejoras en permisos
Wildcards para subdominios
# Permitir todos los subdominios de example.com
deno --allow-net=*.example.com server.ts
// Esto funcionará
await fetch("https://api.example.com/data");
await fetch("https://cdn.example.com/assets");
// Esto pedirá permiso
await fetch("https://other.com/api");
Rangos CIDR
# Permitir rango de IPs local
deno --allow-net=192.168.1.0/24 app.ts
Deny imports
# Bloquear CDNs específicos
deno --deny-import=cdn.jsdelivr.net,unpkg.com app.ts
Exports condicionales
Soporte para exports condicionales de npm packages:
// Funciona con React Server Components
import React from "npm:react@19.1.0";
console.log(React.Component);
# Comportamiento normal
deno app.jsx
# Component: [Function: Component]
# Con condición react-server
deno --conditions=react-server app.jsx
# undefined
Bare specifiers en deno run
Ahora puedes usar bare specifiers directamente:
// deno.json
{
"imports": {
"file-server": "jsr:@std/http/file-server",
"cowsay": "npm:cowsay",
"lume/": "https://deno.land/x/lume@v3.0.4/"
}
}
# Todos estos funcionan ahora
deno run file-server
deno run cowsay "Hello World!"
deno run lume/cli.ts
# Incluso puedes omitir 'run'
deno cowsay "Hello World!"
Formateo de XML/SVG
deno fmt
ahora formatea archivos XML y SVG:
<!-- Antes: mal formateado -->
<svg width="100" height="100"><circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /></svg>
deno fmt
<!-- Después: bien formateado -->
<svg width="100" height="100">
<circle
cx="50"
cy="50"
r="40"
stroke="green"
stroke-width="4"
fill="yellow"
/>
</svg>
Node.js globals simplificados
Deno ahora expone globales de Node.js a código de usuario sin flags adicionales:
Buffer
- Ya no necesitas--unstable-node-globals
global
- Alias paraglobalThis
setImmediate
/clearImmediate
- Para compatibilidad
// Esto funciona sin flags adicionales
const buffer = Buffer.from("Hello World");
console.log(buffer.toString('base64'));
global.myGlobal = "accessible everywhere";
setImmediate(() => console.log("Next tick"));
Mejoras significativas en Node.js APIs
Deno 2.4 mejora considerablemente la compatibilidad con Node.js:
Nuevas APIs disponibles
// fs.glob - Búsqueda de archivos con patrones
import { glob } from "node:fs/promises";
for await (const file of glob("**/*.ts")) {
console.log(file);
}
// crypto.Certificate - Manejo de certificados
import { Certificate } from "node:crypto";
const cert = new Certificate();
// dgram multicast - UDP multicast
import { createSocket } from "node:dgram";
const socket = createSocket('udp4');
socket.setBroadcast(true);
socket.setMulticastTTL(1);
Compatibilidad mejorada
node:buffer
- >95% compatibilidadnode:events
- >95% compatibilidadnode:querystring
- >95% compatibilidadnode:crypto
- Mejoras significativasnode:http
- Soporte para Unix sockets
Otras mejoras interesantes
fetch sobre Unix sockets
const client = Deno.createHttpClient({
proxy: {
transport: "unix",
path: "/var/run/docker.sock",
}
});
const response = await fetch("http://localhost/containers/json", { client });
deno serve con callbacks
export default {
fetch(req) {
return new Response("Hello world!");
},
onListen(addr) {
console.log(`🚀 Server running on ${addr.hostname}:${addr.port}`);
},
} satisfies Deno.ServeDefaultExport;
Jupyter mejorado
# Nombre personalizado para el kernel
deno jupyter --install --name="deno_2_4"
# Display name personalizado
deno jupyter --install --display="Deno 2.4 Development Kernel"
Tablas compatibles con Markdown
# deno coverage y deno bench ahora generan tablas Markdown
deno coverage
# | File | Branch % | Line % |
# | --------- | -------- | ------ |
# | parser.ts | 87.1 | 86.4 |
# | All files | 86.1 | 87.4 |
Mi experiencia con 2.4
He estado probando Deno 2.4 durante las últimas semanas, y estas son mis impresiones:
Lo que más me gusta
- deno bundle funciona de verdad - Finalmente puedo generar bundles confiables para producción
- Import de assets es game-changing - Especialmente útil para aplicaciones que manejan muchos recursos estáticos
- OpenTelemetry estable - Observabilidad sin esfuerzo es increíble para debugging
- DENO_COMPAT=1 - Migrar proyectos Node.js es mucho más simple
Casos de uso donde brilla
- Single-page applications - Con deno bundle y asset imports
- CLI tools - Con deno compile y assets embebidos
- Microservicios - Con OpenTelemetry automático
- Migración gradual - Con DENO_COMPAT=1
Pequeñas molestias
- Import attributes requieren
--unstable-raw-imports
(por ahora) - Algunos flags todavía son verbosos
- La documentación de algunas características nuevas aún está en desarrollo
Conclusión
Deno 2.4 se siente como una versión de madurez. El regreso de deno bundle
con esbuild, los import attributes, y OpenTelemetry estable son características que cambian las reglas del juego.
Para desarrolladores que venían de Node.js, DENO_COMPAT=1
hace que la transición sea prácticamente transparente. Y para los que ya usábamos Deno, las nuevas características abren posibilidades que antes requerían herramientas externas.
¿Deberías actualizar? Definitivamente. Esta versión trae mejoras sustanciales sin breaking changes significativos.
# Actualizar a Deno 2.4
deno upgrade
# Verificar la versión
deno --version
# deno 2.4.0
Recursos adicionales
- Deno 2.4 Release Notes - Notas oficiales de la release
- deno bundle Documentation - Documentación del bundling
- Import Attributes Guide - Guía de import attributes
- OpenTelemetry in Deno - Documentación de observabilidad
- Node.js Compatibility - Estado de compatibilidad con Node.js
¿Te ha resultado útil este artículo? Compártelo con otros desarrolladores JavaScript/TypeScript que puedan estar interesados en las novedades de Deno 2.4. Y si tienes alguna pregunta o experiencia que compartir, no dudes en contactarme.
Comentarios