loading...
Cover image for Webpack Aliases Keep My Code Sane

Webpack Aliases Keep My Code Sane

mmcshinsky profile image Michael McShinsky ・4 min read

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';

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')
        }
      }
      //...
    };

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';

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",
          }
        }
      ]
    }

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')]],
          },
        },
      },
    };

TypeScript

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

    {
      //...
      "compilerOptions": {
        "baseUrl": "src",
        "paths": {
          "@/*": ["*"]
        }
      }
    }

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()
        ],
      } 
    };

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!

Posted on by:

mmcshinsky profile

Michael McShinsky

@mmcshinsky

Father, Techie, and Fullstack Software Engineer with a passion for continuous learning and React of course! http://linkedin.com/in/michaelmcshinsky

Discussion

markdown guide
 

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 :)

 

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!

 

Most editors update the references for you nowadays

 

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.

 

some people don't invest in IDEA> for whatever reason.,

 

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

 

That's deadly! Thanks for the tip 🦄

 

Thank you. That is awesome. I have been wondering how to do this for some time now. By any chance do you know if this works with browserify and mangle? TYIA.

 

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