So you probably saw something like this in your code base somewhere:

import { Button } from '../../../../../ui-lib'

Or something like this maybe?

import { Card } from 'components/card'

What came up on your mind when you saw that ? Confusion, anger, despair ?

I hear you yo!

Let’s talk about the 1st one:

import { Button } from '../../../../../ui-lib'

if you’re using typed javascript ( I prefer Typescript for various sane reasons ), there are no issues at all -> you get intellisense for import path and available module exports with all that type safety included

it may start repeating across your app codebase, if you have some reusable code, which is not related to your project in particular and you know, that it should be published to npm sooner or later ( If you work in that kind of company, which forbids you to publish stuff to open world, I guess I’m sorry for you :( )

What’s the problem ?

The Morse code ( long relative paths … )

Editors may have problem with auto importing that complicated relative paths ( at least VSCode does ). Importing manually from various modules in your project will start to feel very cumbersome ( trust me, I experienced that with my team at work… )

Refactoring

Let’s say you wanna move some file to different folder etc and you don’t use super smart nuclear powerplant IDE’s like WebStorm for instance -> if you do that once, no biggie, but if that start’s to repeat, updating that path manually, well there are much better things in life that you can spent your time :)

Bad architecture

If you’re using such long import paths, it is a sign that you’re doing something very wrong — most of the time bad architecture/structure of your project. Of course if you are importing a reusable modules, that’s ok

What about the 2nd one?

import { Card } from 'components/button'

there are only problems with this one

You have absolutely no idea, what black magic this is and where this module import comes from

this is and where this module import comes from Typescript errors if you are in strict mode — strict , TS have absolutely no idea where was this imported from

So we can wrap up the 2nd example with one sentence basically,

Don’t Do that ! ever! Period. Future you and mostly your coworkers at team will thank you for that eventually

So what’s the goal of this article ?

Transforming import { Button } from '../../../../../ui-lib' to import { Button } from 'custom_modules/ui-lib'

to Have correct type checking and intellisense features

Make test work ( we are using Jest )

Have ready reusable code for extraction and npm publish when the right time comes

Our project structure looks like something like this:

src/

--| app/

--| index.ts --| custom_modules/

--| ui-lib/

Step 1 — Webpack config

Webpack is very powerful module bundler and provides module path aliasing as well. There are 2 options how to turn it on:

resolve.alias — preferred and explicit one

resolve.modules — don’t do this, it’s not explicit and may take hours till someone at your team will notice this during debugging some strange issue. Also webpack docs provide a very unfortunate example of usage which may be tempting to copy paste and start world war 3 in your app modules: [path.resolve(__dirname, "src"), "node_modules"] Please don’t do this!

This is how it looks like with alias property ( yup simple as that ):

resolve: {

extensions: ['.js', '.ts', '.tsx'],

alias: {

// CUSTOM PACKAGES:

'custom_modules': path.resolve(__dirname, 'src/custom_modules/'),

}

},

Step 2 — Jest config

If you’ll run tests, they will start to fail, because jest can’t find your new module path alias import { Button } from 'custom_modules/ui-lib'

Have no fear ! Jest configuration is here !

Because Jest is highly customisable and configurable ( you can even use custom environment if you don’t like jsdom ! ) there is a config for aliasing like webpack provides

"moduleNameMapper": {

"^custom_modules/(.*)": "<rootDir>/src/custom_modules/$1"

},

Tests are green again! yummy

Step 3 — Typescript config

We may have bundlers/test configured to resolve our custom_modules, but what you notice immediately, are Typescript errors and missing type safety as well. All of this because TS just doesn’t know about webpack magical aliasing powers.

So how can we solve this? Check this out, yo:

TS is super flexible and provides as well path mapping and customisable module resolution algorithms for us!

tsconfig.json :

{

"compilerOptions": {

"module": "es2015",

"target": "es5",

"strict": true,

"outDir": "ts-output",

"sourceMap": true,

"moduleResolution": "node",

"baseUrl": ".",

"paths": {

"custom_modules/*": [

"src/custom_modules/*"

]

}

},

"include": [

"src"

]

}

This tells the compiler, that for any module import, that matches the pattern "custom_modules/*" (i.e. custom_modules/ui-lib), to look in following location:

<moduleName> => <baseUrl>/src/custom_modules/<moduleName>

so in our case

custom_modules/ui-lib => ./src/custom_modules/ui-lib

this will automatically resolve all our code written in Typescript and we get what we wanted -> aliases and all that DX with type safety!

Typescript and es2015 type safe path aliasing

Step 4 — npm publish

This is just a bonus step and it’s up to you if you wanna publish things publicly to npm ( you can publish also privately if you wanna spend your money on private npm registry )

Because our custom module “ui-lib” is completely written in TS, we can just extract that folder to separate directory, add “declaration: true” to tsconfig, so consumers will get all that definitions and tweak few more things ( like adding package.json etc, creating separate repo ). then just npm publish -> now you can install your library via npm/yarn and can remove that aliasing config we’ve created

That’s it! Happy type-safe reusable modules aliasing in your projects!