Using absolute path import with  Typescript + babel + nodemon in Your Node Project
Tags: typescript, babel, node, javascript
Published: 2019-10-28 04:46:10

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.

2. 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](https://github.com/dotorimook/ts-babel-abs-path)