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:
-
Typescript-transpile with
tsc
, then babel-transpile withbabel-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.
-
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)
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...
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.