DEV Community

dotorimook
dotorimook

Posted on

Using absolute path import with Typescript + babel + nodemon in Your Node Project

Developing with Node Project, it could be difficult to read your code, and it really feel irritating while importing somany .. in your import code. When you see the path like ../../../../../../models/post, could you even guess where the file is?

Recently I found various ways to import with absolute path rather than relative path in Typescript Node project, and I finally found simplest configuration for my own.

You can do this with just babel-plugin-module-resolver, and tsconfig.json or @babel/preset-typescript.

There are 2 ways to use absolute path in your import. Each of them has pros and cons:

  1. Typescript-transpile with tsc, then babel-transpile with babel-plugin-module-resolver

    Pros

  • You do not worry about support of latest grammar because it is official way to transpile typescript.
  • You do not need @babel/preset-typescript. tsc will transpile your typescript code.

    Cons

  • You have to transpile twice.

  • You have to write path alias information twice, in tsconfig.json and .babelrc. It is redundant.

  1. Configure .bablerc with @babel/preset-typescript and module-resolver, then transpile your typescript with babel.

    Pros

  • Simple configuration. You can write path alias information in .babelrc only.
  • You can transpile only one time with babel.
  • Furthermore, you can use babel plugin you want (like @babel/plugin-proposal-optional-chaining. This is one of the features that I want to use with typescript but I can't.)

    Cons

  • Some of Typescript Grammar is not supported.( like !, null assertion operator)

From now, I will show you how to configure each of them. I will suppose to use nodemon in both ways.

0. File Structure

I will suppose the file structure like following:

├─src
│├─hello
││ └─abc.ts
│└─index.ts
├─.babelrc
├─nodemon.json
├─package.json
└─tsconfig.json

abc.ts and index.ts will be like this:

src/index.ts

import abc from 'hello/abc';
console.log(`Hello, ${abc}`);

src/hello/abc.ts

export default 'abc';

package.json

Some of the packages is not needed by the way you use. You don't need @babel/preset-typescript if you transpile with tsc like so forth.

{
  "name": "path_hell",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.6.4",
    "@babel/node": "^7.6.3",
    "@babel/preset-env": "^7.6.3",
    "@babel/preset-typescript": "^7.6.0",
    "@types/jasmine": "^3.4.4",
    "babel-loader": "^8.0.6",
    "babel-plugin-module-resolver": "^3.2.0",
    "jasmine-core": "^3.5.0",
    "karma": "^4.4.1",
    "karma-chrome-launcher": "^3.1.0",
    "karma-jasmine": "^2.0.1",
    "karma-webpack": "^4.0.2",
    "nodemon": "^1.19.4",
    "ts-loader": "^6.2.0",
    "ts-node": "^8.4.1",
    "typescript": "^3.6.4",
    "webpack": "^4.41.2",
    "webpack-dev-server": "^3.8.2"
  }
}

1. Typescript-transpile with tsc, then babel-transpile with babel-plugin-module-resolver

First, you can write start script like follow in your package.json.

package.json

...
"scripts": {
    "start": "tsc && nodemon"
  },
...

and other configuration file like follow:

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",      
    "module": "commonjs",   
    "outDir": "./dist",     
    "strict": true,              
    "moduleResolution": "node",   
    "baseUrl": "src",              
    "paths": {
      "hello": ["./hello"]
    },                         
    "esModuleInterop": true     
  },
}

.babelrc

{
  "presets": [
    "@babel/preset-env"
  ],
  "plugins": [
    ["module-resolver", {
      "root": ["."],
      "alias": {
        "hello": "./dist/hello",
      }
    }]
  ],
}

nodemon.json

{
  "ignore": [
    "node_modules"
  ],
  "watch": [
    "src",
    "yarn.lock"
  ],
  "exec": "babel-node dist/index.js",
  "ext": "ts"
}

If you npm start or yarn start, then tsc will transpile your typescript the way you wrote in tsconfig.json. the baseUrl and paths will help resolve your absolute path while tsc transpile. Then, your transpiled code will be run by babel-node. Wile transpiling with babel, you have to specify given absolute path into what babel can understand, so you have to specify the alias in the .babelrc with babel-plugin-module-resolver.

2. Configure .bablerc with @babel/preset-typescript and module-resolver, then transpile your typescript with babel.

You can make similar effect while configuring only .babelrc with @babel/preset-typescript and babel-plugin-module-resolver. You can write path alias information only 1 time in your .babelrc, which is very simple.

First of all, the start script in package.json will be different from the first.

package.json

...
"scripts": {
    "start": "nodemon"
  },
...

Then, you have to configure .babelrc and nodemon.json like follow:

.babelrc

{
  "presets": [
    "@babel/preset-typescript",
    "@babel/preset-env"
  ],
  "plugins": [
    ["module-resolver", {
      "root": ["."],
      "alias": {
        "hello": "./src/hello",
      }
    }]
  ],
}

nodemon.json

{
  "ignore": [
    "node_modules"
  ],
  "watch": [
    "src",
    "yarn.lock"
  ],
  "exec": "babel-node --extensions \".ts\" src/index.ts",
  "ext": "ts"
}

Done!

In the code, please focus that alias field in module-resolver: from dist to src. This way only transpiles your code 1 time, so you don't have to reference tsc-transpiled code path, and you don't have to define paths in your tsconfig.json. Please be careful with nodemon option --extensions ".ts" that let you transpile your .ts code right a way. If you write '.ts' rather than typing \".ts\" , then the error comes. Be careful.

I prefer the second one because it is simple and capable of using other babel plugins like @babel/plugin-proposal-optional-chaining.

Let's simplify your path with either way!

You can check the working example in my github repository: https://github.com/dotorimook/ts-babel-abs-path

Top comments (2)

Collapse
 
roblav96 profile image
Robert Laverty

With all due respect, I highly recommend against these two implementations, both overly complicate your pipeline.

Use github.com/ilearnio/module-alias and a working example github.com/roblav96/futon-media/bl...

Collapse
 
dotorimook profile image
dotorimook

Thank you for the introduction with good example.
I have heard of module-alias, and I did not want to use it because I am not familiar with it.
I think I have to try it.