Introduction
Many challenges are encountered as a product scales. One of the most important is to ensure code maintainability. The depth of imports increases as the codebase grows. They becoming to look really messy.
Sometimes your imports become looks like this, especially in complex projects. Just look at those imports, it’s really difficult to understand.
In this article, I will not touch architecture and dependency injection, but will simply talk about how imports can be made more readable.
In a Unix-style file system, a period "." refers to the current directory, a double period ".." refers to the directory up a level. By default, if we need to import something from up level directory we need to write it as relative path. Like, from current directory we need to jump to 3 levels up and find here specific file: "../../../service/{filename}".
But what if I say, that we can actually write imports as non-relative paths like "app/service/{filename}".
First solution – Path mapping via JS/TS compilers
My friend notices the same problem with messy imports and remembered “aliases for imports”, but he got some difficulties.
So let’s try to beautify our imports with first solution.
JavaScript and TypeScript compilers resolving our relative imports, but we can change configuration to make it resolve non-relative imports too. So we just need to add «baseUrl» and «paths» directives in jsconfig.json or tsconfig.json file.
Setting «baseUrl» informs the compiler where to find modules. All module imports with non-relative names are assumed to be relative to the «baseUrl».
And via «paths» we setting up compiler to use a mapping configuration to map module names to files at run-time.
Imagine we have file structure like this, so we can apply config like this:
So we can apply config like this:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@features*": [
"./features/*"
],
"@core*": [
"./core/*"
],
"@auth*": [
"./auth/*"
],
"@store*": [
"./store/*"
],
},
}
}
Please notice that «paths» are resolved relative to «baseUrl».
This configuration will give us readable imports like this. We got rid of periods and slashes.
If you noticed, we still can use relative paths as well as non-relative. My personal preference is to use non-relative paths for deep imports to another logic parts of project, and use relative paths for 1–2 level imports within a current logical part.
Looking good! But we have to manually configurate it.
And next solution will work automatically. So comment your «paths» directive and move forward!
Second solution – ESLint import resolver
ESLint is super powerful tool! And it's also has plugin for linting import/export syntax. And there is also imports resolver than can help us with to upgrade our imports.
We need to install eslint and import plugin:
yarn add --dev eslint eslint-plugin-import
And add this configuration in ESLint config file:
{
"settings": {
"import/resolver": {
"typescript": {
"project": "./tsconfig.json"
},
"node": {
"paths": [ "." ]
}
}
}
}
And that's all!
After this we getting automatically resolved non-relative imports!
Thanks for reading this article! By following it, you’ll develop more maintalable code with readable imports. I hope it will help you to develop greatest projects!
I’m progressive software engineer with over 5 years of development experience and I love to share it! So follow me and stay tuned!
Incoming topics:
- WebAssembly (Wasm): when and how to use it
- Client API code-gen, from OpenAPI Specs to TypeScript
- Getting frames from video files on frontend
References:
Top comments (1)
🙂 Feel free to ask if you have any questions or concerns about applying this to your project!