Node.js: using non-local dependencies inside native ES modules
Tags: javascript, node, todayilearned
Published: 2021-06-04 22:54:06

---
title: Node.js: using non-local dependencies inside native ES modules 
published: true
description: grab that awesome library that is located somewhere else in your filesystem
tags: #javascript #node #todayilearned
//cover_image: https://direct_url_to_image.jpg
---

If you were using only CommonJS syntax you would "solve" this by using the `NODE_PATH` environment variable, but that won't work with native ES modules. The good news is that we can still achieve our goal. The somewhat bad news is that you won't be using the beloved `import` keyword. What I will show you now is how you can make your own `require` function that works with native ES modules.

Before I begin let me just say I do not encourage using non-local dependencies in "production apps", or libraries or really anything that is meant to be deployed on a remote server. The use case **I** have for this is focused more on creating scripts for personal use.

With that out the way, lets go back to what's important.

## The module "module"

Did you know there is a module called `module`? Yeah, and it has everything we need. 

Node has a very convenient function called [createRequire](https://nodejs.org/api/module.html#module_module_createrequire_filename), it takes a path as an argument and it gives you back a function that acts in the same way `require` does in CommonJS.

The docs will show you how you can make a "polyfill" of `require` like this.

```js
import { createRequire } from 'module';
const require = createRequire(import.meta.url);

// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module');
```

With this new knowledge nothing can stop us from doing something like this.

```js
import { createRequire } from 'module';
const use = createRequire(GLOBAL_MODULES_PATH);

const fetch = use('node-fetch');
```

Why not call it `require`? Well, because in this case `use` doesn't really act like the standard `require`. All the algorithms and shenanigans it does will be relative to `GLOBAL_MODULES_PATH`. So doing `use('./sibling-module');` will not get you a `sibling-module` relative to your script, it'll search in the path you gave to `createRequire`.

Okay, that's it, really. Is how you would get global modules in a relatively safe way inside ES modules.

## Usage

How do I use this little piece of knowledge? Funny story...  somewhere in my filesystem I have a `awesome-js` folder with my favorite `js` libraries installed and ready to go. I also have a script called `cli.mjs`.

```js
#! /usr/bin/env node

import { resolve } from 'path';
import { createRequire } from 'module';

// Setup function to require "global" modules
global['use'] = createRequire(import.meta.url);

// Get absolute path
const script = resolve(process.argv[2]);

if(script) {
  // Run the script
  await import(script);
} else {
  console.error('Must provide a valid path to a script');
  process.exit(1);
}
```

The idea is to allow `cli.mjs` to execute others script that can be anywhere in the filesystem and allow then to use the `node_modules` of `awesome-js`. So in the command line I want something like this.

```sh
jsm /path/to/script.mjs --some argument
```

How did I do that? In the `package.json` of that folder I added a `bin` property.

```diff
  {
    "name": "awesome-js",
    "version": "1.0.0",
+   "bin": {
+     "jsm": "./cli.mjs"
+   },
    "main": "",
    "license": "MIT",
    "dependencies": {
      ...
    }
  } 
```

And ran the command `npm link`. On linux this will create a `jsm` symlink which leads to `cli.mjs`, and put that in your `PATH`. On windows it should do the equivalent of that. If I'm not mistaken the executable will be in the `prefix` you have configured for `npm`. If you want to know what is that location use:

```sh
npm get prefix
```

I actually do some other hacky stuff inside `awesome-js` but let's not get into that. If you are curious you can find the code [here](https://github.com/VonHeikemen/jsenv).

---

Thank you for your time. If you find this article useful and want to support my efforts, [buy me a coffee ☕](https://www.buymeacoffee.com/vonheikemen).

[![buy me a coffee](https://res.cloudinary.com/vonheikemen/image/upload/v1618466522/buy-me-coffee_ah0uzh.png)](https://www.buymeacoffee.com/vonheikemen)