Guide To Porting Node Modules To Deno
Tags: deno, node, typescript, javascript
Published: 2020-07-02 15:06:00

If you've been using Deno lately one of the biggest drawbacks is the lack of support for several third party modules that you are used to using in Node.

This is a quick guide to share how you can get started porting existing Node modules so they are compatible with Deno.

# Quick Wins

## 1. Public Source Code + ESM + No Node APIs

If an existing project is already written with ES Modules (and preferably TypeScript) and has no dependencies on Node APIs, either in it's code or it's sub-dependencies, then you're in luck! You should be able to import these modules directly by using any URL that returns the raw code.

For example, if the code is hosted on GitHub you can use:

- The a raw GitHub content URL (click the `Raw` button on a file in GitHub)
- A GitHub CDN such as [GitHack](raw.githack.com)
- The Deno Land Third Party module registry using the experimental GitHub syntax (e.g. <https://deno.land/x/gh:asos-craigmorten:superdeno/mod.ts>)

## 2. NPM Module + CommonJS + No Node APIs

If the desired module is written using Node's CommonJS then you won't be able to import the module directly using ESM `import` syntax.

To get around this you can use one of the growing numbers of CDNs that will automatically convert CommonJS code to ESM for you! For example, check out one of these CDNs:

- [JSPM](https://jspm.org/) - provides a module CDN allowing any package from NPM to be directly loaded in Deno (and the browser) using ESM syntax.
- [Pika](https://www.pika.dev/cdn) - Every NPM package can be loaded from Pika CDN as a modern ESM import. If the package wasn't written as ESM, they'll do the work to convert it for you.

# Now we need to do some work

By this point we start to run out of options for being able to get a module ported for free, and will likely have to do some coding!

## Denoify

As a first port of call, check out [Denoify](https://github.com/garronej/denoify):

> A build tool that takes as input a TypeScript codebase that was meant to target node and/or the web and spits out a modified version of the source files ready to be deployed as a Deno module.

Denoify is a handy tool for taking an existing Node module and creating Deno compatible assets, meaning you can support both JavaScript runtimes at the same time, using the same code! It does have some [limitations](https://github.com/garronej/denoify#limitations) however:

1. If your module is vanilla JS it needs to be ported to TypeScript first. This may sound scary, but generally a case of adding `.ts` extensions and adding simple types to your code where needed. If in doubt it is perfectly reasonable to start of with using `any`!
2. Not all of the Node APIs are supported yet - Denoify can only transform APIs that have been ported in [Deno's standard library for Node](https://deno.land/std/node#supported-builtins). If you're looking to port a module then one of the outstanding Node APIs would be super-useful for the community!
3. You will need to fork and Denoify manually each of your module's dependencies as well!
4. For the dependencies that can't easily be Denoified you will need to write a partial Deno port of the bits your module needs.
5. `require()` is not yet supported.
6. You can't (yet) `fs.readFile()` files that are part of the module.

If this sounds like a possibility for your target module, then check out [the example Denoified module](https://github.com/garronej/my_dummy_npm_and_deno_module) for a guide on how to get started!

## Node Compatibility Layer

If Denoify doesn't work for your target module, you may need to start manually porting code, say by forking the project and updating file by file.

As mentioned in the previous section, if you come across any code that uses a Node API you may be in luck! The Deno community have been writing a [Node compatibility layer](https://github.com/denoland/deno/tree/master/std/node/#commonjs-module-loading) to allow Deno code to use a form of `require()` and ported APIs. For example:

```ts
import { createRequire } from "https://deno.land/x/std@0.65.0/node/module.ts";

const require = createRequire(import.meta.url);

// Loads native module polyfill.
const path = require("path");

// Loads extensionless module.
const cjsModule = require("./my_mod");

// Visits node_modules.
const leftPad = require("left-pad");
```

This may save you some time by allowing you to not only import polyfills for Node APIs, but also modules from a `node_modules`.

## Hard Graft

If none of the options above work for you, or you fancy more of a challenge(!), then you might just have to write the code!

I recommend forking the original module's repo and pulling it down locally. You can then work through the code file by file to replace out any code which is incompatible with Deno over to Deno APIs.

To help with your conversion, the following resources can be really useful:

- [DenoLand Docs](https://doc.deno.land/) - Deno has it's [full API docs](https://doc.deno.land/https/github.com/denoland/deno/releases/latest/download/lib.deno.d.ts) available on their website, complete with types to make it easier to get started. You can also use the site to provide documentation on any [standard or third party module](https://deno.land/x/) that DenoLand has registered.
- [Deno Standard Library](https://deno.land/std) - Deno has a fast growing standard library that supports the majority of core functionalities. Often these have similar names to core Node APIs, and if you're familiar with GoLang, they are actually a [loose port](https://deno.land/std/#contributing) of Go's standard library!
- [Deno Third Party Modules](https://deno.land/x) - The Deno community is rapidly developing modules all the time. Use the third party modules registry to find any sub-dependencies you might need for your project. You might find someone has already started a port which you can contribute to!

---

That's it peeps! Hopefully some of these resources are useful when you get started with writing or porting your own modules!

If you have any good tips, resources or tools for helping port modules then I would love to hear it - drop comments below!