DEV Community

Cover image for Webpack Aliases Keep My Code Sane
Michael McShinsky
Michael McShinsky

Posted on • Edited on • Originally published at magitek.dev

Webpack Aliases Keep My Code Sane

Reference Mistakes Make Developers Look Bad

If you were to ask me what are one of the most common mistakes are in basic web development, I would, without hesitation, tell you that reference errors are one of the big ones out there. Whether they are with variables or package imports, I encounter and solve many problems for myself and other developers that make these mistakes constantly. Specifically with package imports, I wanted to stop dealing with changing all the path locations every time files were refactoring and improved.

You are probably familiar with what this looks like with some example code below.

    import { UserCard } from '../../../components';

    import { apiServices, themeServices } from '../../../services';

    import { objUtil, dateUtil } from '../../../utils';
Enter fullscreen mode Exit fullscreen mode

What happens when you move a file or do a mass update to references throughout your app? You have to painfully go through and make sure every reference is correct. Next, you have to confirm that none of your pages crash or fail if your compiler is misconfigured and doesn’t recognize it as an error. Even worse… you reference another file on accident that has the same named export. As a result, you don’t notice until the code is pushed to production and now customers are calling in complaining that features have stopped working.

Aliases to the Rescue

Webpack solves these problems by giving us the ability to use “aliases”. Aliases are a way to let webpack know where to find our code by providing a word or character that represents a partial reference to where the code is located. Once webpack knows this, the code can be properly resolved while it is compiling during development or building the final package. This is especially useful, not only for JavaScript and TypeScript, but CSS as well.

In order to add this feature to our code, we need to start out by setting up our “resolve” inside webpack. Your code will probably end up looking similar to this:

    const path = require('path');

    module.exports = {
      //...
      resolve: {
        extensions: ['js', 'jsx', 'ts', 'tsx'],
        alias: {
          '@': path.resolve(__dirname, 'src'),
          '@components': path.resolve(__dirname, 'src/components'),
          '@utilities': path.resolve(__dirname, 'src/utilities')
        }
      }
      //...
    };
Enter fullscreen mode Exit fullscreen mode

You can use as many aliases as you want if you have the use case for it. Overall, it ends up being pretty easy to use only the ‘@’ character as an alias to reference the ‘src’ folder, but you can create any name for any alias path that you want to. Once we have modified our webpack configuration to be similar to the code above, our old imports could now look something similar to this:

    import { UserCard } from '@/components';

    import { apiServices, themeServices } from '@/services';

    import { objUtil, dateUtil } from '@/utils';
Enter fullscreen mode Exit fullscreen mode

So much better! Now when you refactor your code, you will always be referencing the same packages you intended to.

Working with Babel and TypeScript

If you are resolving your code in combination with webpack inside of Babel, ESLint or TypeScript, you may need to update their config files. These file could be: .babelrc, .babel.json, .eslintrc.js, tsconfig.json, config.ts, etc… depending on how you are set up. There are a couple differences in these files you may have to make as opposed to the webpack.config.js when resolving aliases.

Babel

    // .babelrc
    // npm i babel-plugin-module-resolver -D

    {
      "plugins": [
        "module-resolver",
        {
          root: ["./src"],
          alias: {
            @: "./src",
          }
        }
      ]
    }
Enter fullscreen mode Exit fullscreen mode

ESLint

    // .eslintrc.js
    // npm i eslint-import-resolver-alias -D

    const path = require('path');

    module.exports = {
      //...
      settings: {
        'import/resolver': {
          alias: {
            map: [['@', path.resolve(__dirname, 'src')]],
          },
        },
      },
    };
Enter fullscreen mode Exit fullscreen mode

TypeScript

    // tsconfig.json
    // npm i tsconfig-paths-webpack-plugin -D

    {
      //...
      "compilerOptions": {
        "baseUrl": "src",
        "paths": {
          "@/*": ["*"]
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode

If you don’t want to resolve your aliases in TypeScript and webpack, here is an example where we use both our TypeScript and webpack configuration files, but using a package install, keep the aliases inside our tsconfig.json. Also note that you usually only need the alias configuration in your .babelrc file if you are using webpack and defining the aliases in webpack. The overall goal for moving our aliases into TypeScript and Babel config files in the following example can be to keep our code DRY and in one location. This can avoid updating one configuration and then accidentally forgetting to update it elsewhere.

    // webpack.config.js
    // npm i tsconfig-paths-webpack-plugin -D

    const path = require('path');
    const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')

    module.exports = { 
      //... 
      resolve: {
        extensions: ['js', 'jsx', 'ts', 'tsx'],
        plugins: [
          new TsconfigPathsPlugin()
        ],
      } 
    };
Enter fullscreen mode Exit fullscreen mode

Combine this code with your TypeScript “tsconfig.json” alias setup are you are up and ready to go!

Summary

Aliases taking over major references for variable and file imports make developers look good. Your code will look cleaner and you are much less likely to make mistakes with file refactoring and consolidation. After I had discovered aliases, I make sure that they exist in just about every project I work on. I would encourage you as well to consider if aliases are the right answer for you in saving yourself from one more bug to take care of or worry about before shipping code anywhere.


If you found this helpful or useful, please share a 💓, 🦄, or 🔖. Thanks!

Top comments (8)

Collapse
 
bitttttten profile image
bitten • Edited

Hi! Just to let you know, if you move a file around in vscode it'll ask to update all the relative imports for you! After finding this out I have been using relative imports since it also makes other tools simpler :)

Collapse
 
mmcshinsky profile image
Michael McShinsky • Edited

That's one of my favorite features that they introduced. I love accepting the prompt with components that are localized to each other with shallow references. Thanks for mentioning that!

Collapse
 
dweng0 profile image
Jay

Most editors update the references for you nowadays

Collapse
 
mmcshinsky profile image
Michael McShinsky

Thanks for the feedback @jay ! @bitttttten brought up the same point. Usually, when using a modern IDE, I too get prompted for the reference updates which is really nice. I ran into this issue where a modern IDE prompted for an update one moment and then it didn't the next moment when working with a junior developer the other day during a refactor session.

I find that having this in place is going to make life so much more convenient and less prone to error when in deeply nested ../../../../../ by using an alias like @. Makes the code cleaner and easier to understand where I am getting all my dependencies from.

Collapse
 
nickytonline profile image
Nick Taylor

We use these at DEV. Only difference for us is we’re not using TypeScript, so we have a jsconfig.json instead.

Collapse
 
aderchox profile image
aderchox

Hi Michael. Thank you for this useful tutorial. But I think there's a typo in the snippets, the extensions listed in the resolve.extensions array must have dots at their beginnings, so extensions: ['js', 'jsx', 'ts', 'tsx'], should become extensions: ['.js', '.jsx', '.ts', '.tsx'], or otherwise it won't work.

Collapse
 
c_v_ya profile image
Constantine

That's deadly! Thanks for the tip 🦄

Collapse
 
mmcshinsky profile image
Michael McShinsky

Thanks, I appreciate it! As of right now, I haven't used it in combination with these tools personally.