Import ES6 Modules with absolute paths in NodeJS without using Babel/Webpack

Viewed 9177

I have a plain old NodeJS project (with Typescript) and I'm really struggling to find out how to do ES6 imports for local files without having to do "../../../foo/bar" all the time.

There are loads of similar questions but all seem to revolve around babel/Webpack which I'm not using.

If I do the following:

import foo from `/bar`

it looks for it in the root folder of my PC (e.g. c:/bar) and fails.

I have tried using a .env file with NODE_PATH set to various hings ("/", "." etc) but no luck. I have also tried setting "type: 'module'" in my package.json and my tsconfig.json file has {"baseUrl": "."}

So I think I've tried every answer I can find. Am I just doing them in the wrong combination or is the solution something different?

3 Answers

Here are two tricks I've used for this, with Node.js and native ES modules.

  1. file: dependencies

If you want to access <project root>/bar from a sub package two levels down, adding this to the package.json dependencies:

    "@local/bar": "file:../../bar",

..makes bar available to the said subpackage as @local/bar.

While relative paths are still present, they are now all in the package.json files and the sources never need to know..

  1. Use dynamic import()

Pick the root folder's path to a constant and do this:

const foo = await import(`${rootPath}/bar`);

This was pretty simple to implement for me, using Typescript in VSCode.

In tsconfig.json, I added "baseUrl": "./", under compilerOptions

After a restart of VSCode, VSCode will automatically import using psuedo-absolute paths.

The paths won't begin with /, that still points to your drive root.

If the is below the current file, it will still use a ./relative/path, but no more ../../tree/traversing

Then I set dev in packages.json to

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "dev": "cross-env NODE_PATH=./ nodemon ./app.ts"
},

I use cross-env. You may use the set command and nodemon for automatic reloading of change files.

Setting NODE_PATH to ./ tells NodeJS to do what Visual Studio and TypeScript are doing.

I do have package.json in my src directory. You may not, and may need to change some pathing to adjust.

I am not sure what you mean by "local files without having to do "../../../foo/bar""

Let me explain how Javascript handles imports. Here is a basic folder structure.

C:Users/NAME/Project/randomfolder/bar.js


- Project
  - Random Folder
    - foo.js
  - bar.js

Option 1 Probably the better option for 95% of what you will do

Let's say you are trying to import foo.js into bar.js we first get our current location of bar.js using a . so it would be

import foo from "./Random Folder/foo.js"

If you are going the other way the . is still used to say get current location in the folder structure but you pass a second . so .. then a / to go up a folder. So to import bar.js into foo.js it would look like this:

import bar from "../bar.js"

We are going up a folder then looking for the file we want.

Option 2

But if you know you folder structure is going to be very big and you will be importing always a few folders up or down why not make a variable and then use string literals.

  let folder = `../../../`
  import test from `${folder}foo`

You have a few options of how to handle what you want to do.

Option 3 For NodeJS and modules

If you are using NodeJS and want to get the path not relative, the use the app-root-path module. It lets you always get your project root and then dig into to files accordingly. This can be accomplished with string literals.

var appRoot = require('app-root-path');
import foo from appRoot + "/foo/bar/folders/bla/bla"

or

import foo from `${appRoot}/foo/bar/folders/bla/bla` <-- string literals

Related