Migrating Next.js (+Jest/Storybook/Cypress) to use module path aliases, instead of relative paths
🦅

Migrating Next.js (+Jest/Storybook/Cypress) to use module path aliases, instead of relative paths

Most projects use the well-known relative paths to resolve files and modules. For instance: import '../config.ts';
Sometimes, especially on big projects, it becomes this:import { NRN_DEFAULT_THEME } from '../../common/constants';
But, did you know you can convert to something like this:import { NRN_DEFAULT_THEME } from '@/common/constants';
This is a “module path alias”, and Next.js has support for those since 9.4 (May, 2020).
The main advantages to module path aliases are:
  • Avoids “../” spaghetti code
  • Avoid breaking your imports when moving them around (sure, IDE help but when you move a lot of files around, they get confused too)
  • Avoid changing the file content when moving the file around, which in turn leads to avoiding Git conflicts! (this is, by far, the main advantage, IMHO)
  • You can copy/paste your code, and it’ll work as-it, even if you pasted it into a file on another folder level (and that’s great)
  • You can still use relative imports if you want, it’s not a lock-in!
  • Do you see more?
So, why not using them? Why don’t we see that more often?
  • It requires configuration in several tools (babel, webpack)
  • It affects all tools in your project, because all file loaders must be compatible, and configured properly (this is, by far, the main reason why it’s so complicated to get right)
If you use a very simple Next.js project, you can simply change your tsconfig.json to something like this:
{ "compilerOptions": { "baseUrl": ".", "paths": { "@/common/*": [ "src/common/*" ], "@/components/*": [ "src/common/components/*" ], "@/utils/*": [ "src/common/utils/*" ], "@/modules/*": [ "src/modules/*" ] },
The above mapping is what we’re thinking of using in Next Right Now, but you probably want something specific to your app and way of doing things!
Using a mapping as described above will be enough for Next.js to use import { NRN_DEFAULT_THEME } from '@/common/constants'; properly!

How to convert all relative paths to module paths?

The relative-to-alias NPM module helped us a ton.
It was hard to configure properly because it wasn’t doing anything at first (due to a misconfiguration on our side), but we eventually managed to make it work.
npx relative-to-alias --src 'src' --alias '@/common' --alias-path './src/common' --extensions 'ts,tsx' --language 'typescript'
Running this replaced all relative paths to @/common/.... We eventually wrote a script for this.
This is a quick and easy way to migrate an existing project.
Make sure to commit all changes before running the codemod:module-path-aliases! It'll change lots of files and should be done in a dedicated commit/PR.

Is that it?

Depending on how big your project is, it might… But:
  • If you’re using Jest (you probably are)
  • If you’re using Cypress
  • If you’re using Storybook
Then, you’ll need to configure them, so they understand what @/ means!
And, unfortunately, that’s the most complicated part to figure out by yourself… Because they all use a different way of doing so. They don’t all rely on tsconfig.json, and you don't necessarily only use TypeScript, but JavaScript as well!
Here is an overview of where you’ll need to make changes:
The jsconfig.json are necessary for WebStorm to resolve the aliases used within .js files. (Otherwise, it compiles but WebStorm displays "Module not found")
Jest has a particular way to understand aliases. Storybook doesn’t have tsconfig.json and it must be configured via their Webpack override.
In conclusion, it can be quite complicated (with a lot of duplicated configuration) to make Module aliases work on a whole project. It takes some time to understand how each 3rd party needs to be configured.
At this time, I don’t have any feedback experience regarding actual usage. This is still under peer-review, and it might take a while until we get familiar with those new paths.
Also, it is concerning how complicated the configuration is. It’s being duplicated in no less than 7 different files! And one might be concerned about what’d happen if it ever breaks.
But, in such event, I guess we can solve it with another codemod that converts all module aliases back to relative paths!