Configuring Module Resolution On Typescript and Jest
At some point we have all come across insanely long import statements like import { stuff } from ‘../../../../../../../some/deep/component’
in our projects. If you’re just like me and looking for a way to get around this, then you are at the right place. The primary objective of this guide is to explain handling Module Aliases on Typescript and Jest.
TL;DR — Here is a boilerplate project that reflects what is explained
Lets gradually build up our configuration, understanding and resolving issues as they surface.
Important: You can clone this Github project as you walk through the guide.
Step 1: Setting up a basic service.
Within the project folder run git checkout step-1
. We have a simple web server with the following directory structure.
.
+-- src
| +-- lib
| | +-- database
| | | +-- index.ts
| +-- repositories
| | +-- person
| | | +-- index.ts
| +-- services
| | +-- person
| | | +-- index.ts
| +-- index.ts
+-- test
| +-- lib
| | +-- database
| | | +-- index.spec.ts
| +-- repositories
| | +-- person
| | | +-- index.spec.ts
| +-- services
| | +-- person
| | | +-- index.spec.ts
Importing the repository
module requires the service to traverse up their directory and into the the repositories
directory leading the following: import * from ../../repositories/person
. This becomes quite verbose as your project grows in size.
Step 2: Configuring path resolution on tsconfig.js
Run git checkout step-2
. Setting the baseUrl
and paths
specifies to the Typescript transpiler how to resolve modules.
baseUrl: Specifies where to find modules for all imports, all non-relative imports are considered to be relative this.
paths: Configures how to resolve specific names, relative to the baseUrl.
Step 3: Configuring the NodeJS runtime to handle module resolution
Within the project folder run git checkout step-3
. Everything runs well on using ts-node
and when you compile it using tsc
.
However trying to run the transpiled code fails throwing an error like.
Error: Cannot find module '@/services/person'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:580:15)
at Function.Module._load (internal/modules/cjs/loader.js:506:25)
...
The NodeJS runtime does not know how to resolve our aliases. This is because the tsconfig
is only applicable to the Typescript compiler and is not picked up Nodejs runtime. More on this is detailed in this issue on the Typescript repository.
It would be great if the compiler also handled rewriting the generated files.
Unfortunately it doesn’t and we need to specify to the NodeJS runtime how to resolve modules.
We can use a npm module called module-alias
to do just that. It has other great features that allow you to define aliases on the fly.
Add the following to the beginning of the src/index.ts
to register the module.
require('module-alias/register')
We append a _moduleAliases
to our package.json
.
Running the newly transpiled code from the dist
folder, runs now without any issues.
Step 4: Configuring the Jest runtime to resolve modules.
Within the project folder run git checkout step-4
. If you tried running the tests any time until now, it probably failed to run and threw an error:
Cannot find module '@/repositories/person' from 'index.spec.ts'> 1 | import * as personRepository from '@/repositories/person'
| ^
2 | import { findById, getAllPersons } from '@/services/person'
3 |
4 | describe('findById', () => {
By now I am pretty sure, you definitely get the idea. Jest needs a way to figure out how to resolve the modules. We use the moduleNameMapper
property to specify module resolution using regular expressions and how to resolve matching paths.
Running npm test
should go ahead without any issues any compilation issues.
To be able to use aliases for module paths requires that we configure the
- Typescript compiler
- NodeJS runtime
- Jest runtime
This might seem like lot of work, but the not having to get lost in deep nested import statements is definitely worth it. Provided that it is done just once.
Text editors or IDEs that come with IntelliSense that handle the module imports for us. But given the nature of Javascript this sometimes is a hit and a miss.
Hope you enjoyed reading this as much as I enjoyed writing it.
If you think this will be of help to someone? Please do share. If you liked it, tap the clap below so other people will see this here on Medium. Don’t forget to show some love by following the blog!