Fixing VSCode "go to definition" in webpack projects (and why import path aliases are bad)

Cause of the issue

This issue stems from path aliases instead of absolute paths for module imports. Path aliases can seem a little bit neater than having paths like .../../../components/app/scanConfigs/scanConfigTableActions.js throughout your codebase but they add a needless and compounding amount of complexity to your project. As you layer tooling like a bunder, TypeScript, or monorepo tooling you need to add configuration for your path aliases for each of these or your IDE won't work as expected.

Path aliases are a pointless abstraction, but if you're stuck with them, here's how to fix them in webpack.

Fixing the issue with webpack

If you press Command and left-click an import statement in VSCode, it opens the file of the defined import in a new tab. This is a handy feature when traversing components and dependencies across a big React project, but for some reason, it didn't work with the codebase I maintain at Rapid7.

The issue was caused by VSCode not being able to resolve the path provided in the import statement, because my import paths were actually wrong. For example, in a file called scanConfigTableActions.js, an object is imported like this:

import * as historyUtils from 'utils/historyUtils';

The code runs fine, but historyUtils.js is in a completely different part of the project's file tree, so utils/historyUtils is not the correct relative import between the two files.

src/
ā””ā”€ā”€ js/
    ā”œā”€ā”€ components/
    ā”‚   ā””ā”€ā”€ app/
    ā”‚       ā””ā”€ā”€ scanConfigs/
    ā”‚           ā””ā”€ā”€ scanConfigTableActions.js
    ā””ā”€ā”€ utils/
        ā””ā”€ā”€ historyUtils.js

webpack and jsconfig.json

The reason this non-relative import path resolves OK is because the project's webpackconfig.js defines src/js as a path to resolve module from, meaning that import statements don't need to specify this part of the path. This means that utils/historyUtils.js is the same as writing src/js/utils/historyUtils.js.

This is what was included in the project's webpack.config.js:

{
"resolve": {
    "modules": [path.resolve(__dirname, "src/js"), "node_modules"],
}

VSCode's tooling isn't aware of these paths though. You have to let VSCode know about this by adding the module resolution paths to a jsconfig.json or tsconfig.json file at the root of your project.

In my case, I added src/js as a path:

// jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".", // This must be specified if "paths" is.
    "paths": {
      "utils/*": ["src/js/*"],
    }
  },
  "include": ["src/**/*"]
}