Wildan M
WIIIN0DE

WIIIN0DE

The most reliable way to avoid relative imports in Node.js

Writing relative imports in Node.js is something I tend to avoid especially when it's growing larger in functionality. However, For something this basic yet it's so hard to get right. There are just many ways of doing that on the internet.

There are many ways to avoid relative imports in Node.js. One of them is either:

  1. Add NODE_PATH=./ env ( reference )
  2. Set "baseUrl" in (js|ts)config.json ( reference )
  3. Use require.main.require ( reference )
  4. Directly write into node_modules ( reference )
  5. Use NPM/Yarn workspaces ( reference )

There are many downsides to each approach.

  1. Adding an environment variable requires adding cross-env NODE_PATH=./ to all package.json scripts and every time you need to run the script yourself. This behavior is also somewhat unreliable during my testing, also VSCode Intellisense won't understand what you're trying to import.
  2. The baseUrl option from (js|ts)config.json seems to work out of the box, only for VSCode Intellisense. Node.JS won't understand them so I need a babel compiler to set up, it's explained here anyway but to me, this is way too complicated.
  3. Using require.main.require seems like a hack to me, it enforces me to use that in all the scripts rather than the usual require, which of course it's something that I don't like.
  4. Directly writing to node_modules is something against its purpose, also would you rather be willing to move your scripts to mode_modules? I wouldn't want it. It would become a nightmare to maintain.
  5. Using NPM/Yarn workspaces seems promising at first glance but it enforces me to thinking in the way it was designed for "monorepo". Monorepo is good if you have multiple projects that share code, but really it's just too much because I just work on one big node app. Note this was Yarn only feature, NPM add support too but my last experience using it was buggy.

I have found a less popular but way more reliable: symlink-dir. Let's me summarize their explanation on NPM:

Lets suppose you'd like to self-require your package. You can link it to its own node_modules: symlink-dir . node_modules/my-package

What is by mean to "link"? It's basically creating a directory shortcut. You can read it more here. NPM/Yarn workspaces internally also doing this way.

So to use symlink-dir, I just need to add these values in package.json:

{
  "scripts": {
    "postinstall": "symlink-dir src node_modules/src",
  }, 
  "dependencies": {
    "symlink-dir": "latest"
  }
}

This creates a symlink from src folder to node_modules in my project. After npm i I can use require('src/module.js') instead of require('../../../src/module.js'). Works with ESM imports too!

You can also add more symlinks by just appending the postinstall like "symlink-dir src node_modules/src && symlink-dir lib node_modules/src/libraries" and redoing npm i. Out of all solutions previously, this method works best to me. Hope you like it too!

 
Share this