12k
All articles

Presentamos Nub, un kit de herramientas todo en uno para Node.js

Nub es una herramienta Rust para Node.js que ejecuta TypeScript, scripts, instalaciones y versiones de Node sobre Node nativo, con lockfiles y seguridad.

OpenReplay Team
OpenReplay Team
Presentamos Nub, un kit de herramientas todo en uno para Node.js

Nub es un kit de herramientas de línea de comandos escrito en Rust para Node.js. Transpila TypeScript, ejecuta los scripts de package.json, instala dependencias y aprovisiona versiones de Node, para luego delegar la ejecución al binario node estándar que tu proyecto ya tiene fijado. Complementa a Node en lugar de reemplazarlo, y ahí radica toda la diferencia entre él y Bun o Deno.

La mayoría de los equipos que evaluaron Bun y Deno frente a Node nunca superaron la primera pregunta: no se cambia el runtime de un servicio en producción porque la experiencia de desarrollo sea más agradable. Nub toma el otro camino y se define a sí mismo como un kit de herramientas en Rust que deja tu Node, tu lockfile y tu gestor de paquetes donde están. Esto es lo que ganas con ello y lo que cuesta probarlo.

Puntos clave

  • Nub es una CLI escrita en Rust que añade ejecución de TypeScript, despacho de scripts, instalación de paquetes y gestión de versiones de Node sobre el binario node estándar, de modo que no hay un nuevo runtime que homologar.
  • El soporte de TypeScript nativo de Node solo elimina anotaciones y rechaza todo lo que requiera generar código, como enums, propiedades de parámetro o un namespace que contenga código en tiempo de ejecución; el loader de Nub, en cambio, compila esas formas.
  • El instalador de Nub tiene la forma de pnpm y lee y escribe in situ los lockfiles existentes de npm, pnpm y bun, mientras que los de yarn son de solo lectura.
  • Las defensas de instalación no requieren configuración: los scripts de build de las dependencias permanecen bloqueados hasta que los apruebes, cada resolución nueva se contrasta con OSV y una barrera de antigüedad de 24 horas mantiene fuera las versiones recién publicadas.
  • No hay APIs específicas de Nub ni un lockfile propio, y nub.jsonc es opcional, así que eliminar Nub devuelve el proyecto a Node puro.

¿Qué es Nub y qué no es?

Nub no es un cuarto runtime. Es un único binario que se sitúa delante de Node, hace el trabajo que hoy requiere tsx, nvm, npx y un gestor de paquetes, y luego ejecuta (exec) Node real. Su página principal expone el mecanismo con claridad: oxc compila tus archivos en memoria desde dentro de un addon nativo, y el binario node estándar ejecuta el resultado. No hay un runtime separado por debajo, y el ejecutor de archivos acepta los mismos flags que node.

Nada cambia en tu entorno de despliegue. La versión de V8, la ABI de C++ contra la que se compilaron tus módulos nativos, la superficie de process a la que se enganchan tus instrumentaciones: todo eso sigue siendo el Node que ya estabas desplegando. La ruta aumentada requiere Node 18.19 o posterior (Node 18 LTS), en macOS, Linux y Windows, cada uno en x64 y arm64.

El proyecto es reciente. El paquete de npm @nubjs/nub está publicado bajo licencia MIT y aún es pre-1.0, en la línea 0.9.x en su versión más reciente, con nuevas versiones apareciendo con frecuencia.

¿Cómo ejecuta Nub TypeScript sin paso de build?

El soporte de TypeScript nativo de Node elimina los tipos en lugar de compilarlos. Las anotaciones se sustituyen por espacios en blanco, y todo aquello que requiera generar JavaScript se rechaza. La documentación de Node enumera los casos: los enums, los namespaces que contienen código en tiempo de ejecución, las propiedades de parámetro y los alias import = lanzan ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX; los decoradores no se pueden parsear; y como Node nunca abre tsconfig.json, los alias de paths no se aplican. Antes existía un modo de transformación más completo tras el flag --experimental-transform-types, pero Node lo eliminó en la versión 26, así que la eliminación de tipos es ahora la única vía integrada.

Esas exclusiones son exactamente la sintaxis con la que está construida una base de código de NestJS o TypeORM. Tomemos un archivo que usa un enum, una propiedad de parámetro y una importación relativa sin extensión:

// invoice.ts
import { Model } from "./base"

enum Status { Draft, Sent, Paid }

export class Invoice extends Model {
  constructor(public status: Status = Status.Draft) {
    super()
  }
}

Con un simple node invoice.ts, el enum y la propiedad de parámetro no son eliminables y la importación no tiene extensión. Con nub invoice.ts, el mismo archivo se ejecuta sin tocar nada. Nub entrega cada archivo a su addon nativo para compilarlo, y por eso el enum, la propiedad de parámetro y la importación sin extensión funcionan. Además recorre tu tsconfig.json y cualquier configuración de la que ese archivo haga extends, y luego pasa los alias de paths al propio resolver de Node mediante un resolve hook de module.registerHooks().

Los decoradores están soportados en una sola variante. El artículo de lanzamiento cubre los decoradores legacy de experimentalDecorators, la forma para la que están escritos NestJS, TypeORM y Angular, junto con emitDecoratorMetadata. Los decoradores de Stage 3, que TypeScript 5 usa por defecto, se rechazan, porque esa transformación sigue siendo una carencia abierta en oxc. El ejecutor sí emite source maps en línea, de modo que las trazas de pila apuntan a tu código fuente y no a la salida generada. Ese último detalle no es cosmético: el TypeScript transpilado que pierde sus source maps produce trazas contra código que nadie escribió, una fuente recurrente de tiempo perdido en el triaje.

¿Qué comandos reemplaza Nub?

El binario único de Nub abarca trabajo que hoy está repartido entre un estante lleno de herramientas. La correspondencia de reemplazos documentada es directa:

Comando de NubSustituye a
nub <file>node, tsx, ts-node, dotenv-cli
nub run <script>npm run, pnpm run, yarn run
nubxnpx, pnpm dlx, pnpm exec, yarn dlx
nub installnpm, pnpm, yarn
nub watchnodemon, node --watch, tsx watch
nub nodenvm, fnm, n, volta
nub pmcorepack

Esa tabla no cubre toda la superficie. El README también incluye nubr, un único comando que ejecuta un archivo, un script de package.json o un binario de node_modules/.bin, probándolos en ese orden. Se distribuye de forma independiente como @nubjs/runner para proyectos que no pueden instalar un binario.

La propiedad importante es que son independientes entre sí. Adoptar el ejecutor de archivos no te obliga a adoptar el instalador, y cambiar un script dev de tsx watch src/server.ts a nub watch src/server.ts deja package.json como un manifiesto normal y compatible con npm. El proyecto publica sus propios benchmarks para respaldar las afirmaciones de velocidad: 24× más rápido que pnpm run en el despacho de scripts, 19× más rápido que npx en la ejecución de binarios y 18× más rápido que pnpm install. Los tiempos comparados del README sitúan el despacho de scripts en 14,7 ms frente a 329,9 ms de npm, y una instalación en caliente con lockfile congelado en 171 ms frente a 3193 ms de pnpm, ambas medidas en macOS. Un segundo benchmark de instalación, ejecutado con hyperfine en un runner ubuntu-latest sobre un árbol de 1.168 paquetes, arroja 346 ms para Nub y 3453 ms para pnpm.

El gestor de paquetes: con forma de pnpm y respetuoso con el lockfile

El instalador de Nub no introduce un formato de lockfile. Determina qué gestor de paquetes usa ya el proyecto, ya sea a partir de package.json#packageManager o del lockfile que encuentre, y luego se ejecuta en modo de compatibilidad respetando los archivos de configuración y las variables de entorno de esa herramienta. La propia CLI tiene forma de pnpm, de modo que nub install, nub add -E -D react, nub remove, nub update y nub ci se comportan como espera la memoria muscular.

En cuanto a los lockfiles en concreto: los de npm, pnpm y bun se leen y escriben in situ, y los de yarn son de solo lectura. No se convierte nada, y no aparece un segundo lockfile en el diff. Para un equipo que usa pnpm, esa es la pregunta que decide si esto es siquiera evaluable.

Resolución de versiones de Node sin nvm

nub node resuelve la versión de Node que espera un proyecto y la aprovisiona bajo demanda. La versión se obtiene de .node-version, .nvmrc o package.json#engines, y una versión ausente se descarga y cachea automáticamente; también hay verbos explícitos disponibles: nub node install 26, nub node ls, nub node pin 26 y nub node uninstall 22. Lo hace sin hooks de shell y sin reescribir tu PATH, que es justo la parte de nvm que tiende a romperse en CI y en shells no interactivos.

Valores por defecto para la cadena de suministro, y la ausencia de lock-in

Las defensas de Nub en tiempo de instalación están activas sin ninguna configuración. Hay cuatro documentadas. Los scripts de build de una dependencia no se ejecutan hasta que apruebas ese paquete. Cada resolución nueva se contrasta con OSV en busca de versiones maliciosas conocidas. Una versión que haya perdido la evidencia de confianza de publicación que tenía una versión anterior se rechaza sin más. Y minimumReleaseAge tiene un valor por defecto de 24 horas, la misma ventana que usa pnpm, así que una versión publicada hace unos minutos no puede llegar a tu árbol de dependencias. El artículo de lanzamiento añade que una dependencia transitiva que se resuelva a una URL git+, file: o a un tarball directo se rechaza en lugar de descargarse de forma silenciosa. Si ya has trabajado en una postura defensiva frente a los ataques a la cadena de suministro de npm, esto es esa misma checklist pero como comportamiento por defecto, en lugar de un .npmrc que tienes que mantener.

La afirmación sobre la reversibilidad es la otra mitad. Nub no añade APIs que importar, no escribe ningún lockfile propio y trata nub.jsonc como configuración opcional y no como un requisito. Desinstala el binario y el proyecto se ejecuta con Node puro y el tooling que tenía antes, porque el código fuente nunca hizo referencia a Nub.

¿Quién debería probar Nub y quién no?

Pruébalo si estás ejecutando TypeScript a través de tsx o ts-node, mantienes nvm para fijar versiones y preferirías no dedicar un trimestre a homologar un nuevo runtime para salir de ahí. Empieza por el ejecutor de archivos en un solo servicio, deja el instalador en paz y comprueba si desaparece esa fricción del paso de build de la clase «enums y decoradores». Descártalo, por ahora, si necesitas una cadena de herramientas fija y aburrida para un proceso de release regulado, porque un proyecto pre-1.0 que publica versiones con días de diferencia todavía no es eso. El coste de averiguarlo es npm install -g @nubjs/nub y un comando contra un archivo que ya tienes.

Preguntas frecuentes

¿Cómo ejecuto un archivo a través de Nub sin ninguna funcionalidad añadida?

Usa el modo de compatibilidad: pasa --node para una única invocación, o establece NODE_COMPAT en 1, true o yes para cubrir todo el árbol de procesos. En ese modo Nub no aplica absolutamente nada, así que no hay load hook, ni preload, ni inyección de flags, ni carga de .env. Aun así determina qué Node tiene fijado el proyecto y lo instala si hace falta, de modo que tu código se ejecuta en modo vanilla sobre la versión correcta. Eso lo hace útil para distinguir un bug de Nub de uno de Node.

¿Qué plataformas y versiones de Node soporta Nub?

Nub distribuye binarios de Rust precompilados para Linux, macOS y Windows, en x64 y arm64, y descarga el addon N-API correspondiente a tu plataforma en el momento de la instalación. Los modos aumentados necesitan Node 18.19 o posterior, porque ahí es donde aparece por primera vez la API de loader hooks que sustenta la transpilación en la importación. En cualquier versión anterior, un comando aumentado se detiene con un error que indica la versión mínima y te remite al modo de compatibilidad.

¿Por qué falla una instalación con ERR_NUB_ALLOW_BUILDS_RENAMED?

Nub 0.9.0 renombró la lista de permitidos de builds de nivel superior en package.json de allowBuilds a allowScripts, para coincidir con la clave que lee npm 12. Un proyecto que todavía conserve un mapa allowBuilds en la raíz se rechaza con ese error en lugar de recibir una advertencia, así que la solución es renombrar la clave. El allowBuilds de pnpm es una configuración distinta y no se toca, tanto si vive en pnpm-workspace.yaml como bajo package.json#pnpm.

¿Puedo usar nubx sin cambiar de gestor de paquetes?

Sí. nubx encuentra una CLI instalada localmente en node_modules/.bin sea cual sea la herramienta que la puso ahí, así que funciona en un proyecto instalado con npm, pnpm, yarn o bun sin migrar nada. Acepta los flags de pnpm exec con los mismos nombres, y nub dlx replica pnpm dlx hasta en el modo shell, de modo que las líneas de comando que ya tienes siguen funcionando.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.