deno2node
deno2node copied to clipboard
Compile your Deno project to run on Node.js.
deno2node
Compile your Deno project to run on Node.js.
Quick Setup
Run npx deno2node --init in an empty directory.
CLI Usage From Deno
No installation needed. Simply cd into the directory of your project, and run:
deno run --no-prompt --allow-read=. --allow-write=. \
https://deno.land/x/deno2node/src/cli.ts --project <tsConfigFilePath>
You need to substitute <tsConfigFilePath> by a path to your tsconfig.json
file. deno2node passes it on to the shipped tsc for compiling your code.
If you don't already have a package.json, you may find dnt easier to use.
CLI Usage From Node.js
As a by-product of end-to-end testing, a Node.js build is also available:
# New features or TypeScript upgrades
# may alter output or diagnostics across minor versions.
npm install --save-dev --save-prefix='~' deno2node
Now add a script to package.json so you can run it with npm run prepare:
// @filename: package.json
{
// yada yada ...
"scripts": {
"prepare": "deno2node --project <tsConfigFilePath>"
}
}
How It Works
There are three main steps to this.
- Transform the code base in-memory, by rewriting all import statements.
- Typecheck the code.
- Emit
.jsand.d.tsfiles. These files can directly be run by Node or published on npm.
deno2node uses ts-morph under the hood, which in turn builds on top of the
TypeScript compiler tsc. Hence, you get the same behaviour as if you had
developed your code directly for Node.
deno2node can perform more powerful transpilation steps that make it flexible
enough for most needs.
Shimming
Some things are global in Deno, but not in Node.js. One example for this is
fetch. Consequently, you need to shim them, i.e. provide code that
supplements the missing globals.
Note that
deno2nodedoes not actually touch global definitions. Instead, it only injects import statements in the respective modules.
Install the packages providing the shims.
Now, create a file that exports the globals you need:
// @filename: src/shim.node.ts
export { Blob } from "buffer";
export { webcrypto as crypto } from "crypto";
export { fetch, File, FormData, Headers, Request, Response } from "undici";
export { Deno } from "@deno/shim-deno";
export { alert, confirm, prompt } from "@deno/shim-prompts";
Lastly, you need to register your shims in tsconfig.json so deno2node can
inject them for you:
// @filename: tsconfig.json
{
"deno2node": {
"shim": "src/shim.node.ts" // path to shim file, relative to tsconfig
}
}
Runtime-specific code
In same cases you may want to have two different implementations, depending on
whether you're running on Deno or on Node. When shimming is not enough, you can
provide a Node-specific <anything>.node.ts and a Deno-specific
<anything>.deno.ts version of any file. They need to reside next to each other
in the same directory.
deno2node will ignore the Deno version and rewrite imports to use the Node.js
version instead. Thus, the Deno-specific file will not be part of the build
output.
For example, provide greet.deno.ts for Deno:
// @filename: src/greet.deno.ts
export function greet() {
console.log("Hello Deno!");
// access Deno-specific APIs here
}
Now, provide greet.node.ts for Node:
// @filename: src/greet.node.ts
export function greet() {
console.log("Hello Node!");
// access Node-specific APIs here
}
Finally, use it in foo.ts:
import { greet } from "./platform.deno.ts";
// Prints "Hello Deno!" on Deno,
// and "Hello Node!" on Node:
greet();
This technique has many uses. deno2node itself uses it to import from
https://deno.land/std. The Telegram bot framework grammY uses it to abstract
away platform-specific APIs.
Vendoring
To import a module which has no npm equivalent, first set up vendorDir.
// @filename: tsconfig.json
{
"deno2node": {
"vendorDir": "src/vendor/" // path within `rootDir`
}
}
Then, populate it: deno vendor src/deps.vendor.ts --output src/vendor/.
Vendoring is still experimental, so be welcome to open an issue if you encounter a problem with it!
Also, consider recommending pnpm to users of your library. It might be able
to deduplicate vendored files across packages.
API
Confer the automatically generated API Reference if you want to use
deno2node from code.