Home/Journal/Debugging a Broken JavaScript Build: Fixing Dependency Issues, Parcel, and Aliases

February 14, 2025

Debugging a Broken JavaScript Build: Fixing Dependency Issues, Parcel, and Aliases

A deep dive into resolving outdated dependencies, broken aliases, and Parcel v2 migration issues while debugging a JavaScript build failure.

So, I was trying to update ABTestBase, a project that had been quietly accumulating dependency debt for a while, and I ran npm run build and got hit with basically everything at once.

  • A caniuse-lite warning about outdated browserslist data
  • An ESLint peer dependency conflict
  • Two Parcel CLI flags that no longer exist in v2
  • Aliases that Parcel v2 just... didn't understand

Classic cascade. Updating one thing broke another, which required updating another thing, which broke two more things. I dunno, it's a rite of passage at this point.

Updating outdated dependencies

First thing I tried was the obvious stuff:

npm update caniuse-lite browserslist

That cleared the browserslist warning but didn't help with the rest. So I ran:

npm audit fix --force

That resolved a few security vulnerabilities that had been hanging around, including an arbitrary file overwrite in tar, a regex DoS in semver, and prototype pollution in tough-cookie. Good to get those cleaned up either way.

Fixing the Parcel issues

The project was on Parcel v1, and the build script was using flags that v2 dropped entirely.

Old command (v1):

parcel build v1.js -d build --experimental-scope-hoisting --cache-dir ./.cache

New command (v2):

parcel build v1.js --dist-dir ./build --cache-dir ./.cache

The -d flag is gone, --experimental-scope-hoisting doesn't exist in v2, and --dist-dir is the replacement. I also had to remove @babel/preset-env from babel.config.json, since Parcel v2 handles transpilation on its own. I didn't know that going in, so I'm writing it down here in case it saves someone else a trip down the same rabbit hole.

Fixing broken aliases

ABTestBase used angle-bracket aliases like <UTILS> and <LIBRARY> for imports. Parcel v2 doesn't support that syntax.

I updated package.json to use standard aliases:

"alias": {
  "UTILS": "./Library/Utils",
  "LIBRARY": "./Library"
}

And updated every import:

// Before (didn't work)
import addInternalCSS from '<UTILS>/addInternalCSS';
 
// After (works)
import addInternalCSS from 'UTILS/addInternalCSS';

Even after that, Parcel still wasn't resolving them until I added the explicit resolver config to .parcelrc:

{
  "extends": "@parcel/config-default",
  "resolvers": ["@parcel/resolver-default"]
}

I'm honestly not sure why the package.json config alone wasn't enough, but it wasn't. Both places needed it.

Making builds work from any test directory

The original build script only worked from the project root. I wanted to run npm run build from inside any test directory:

cd Accounts/TheGood/Tests/SoftwareUpdateTest
npm run build

The fix was using $PWD in the build script so the output paths are always relative to wherever you're running from:

"build": "parcel build ./v1.js --dist-dir $PWD/build --cache-dir $PWD/.cache"

Simple, works everywhere.

What I took away from this

Old projects are fine until they're not. Everything looks stable until you try to update something, and then you find out you've been one npm update away from chaos for two years. Parcel v2 in particular is a meaningful upgrade from v1, but it's not a drop-in replacement. The CLI flags changed, the alias syntax changed, and the Babel config assumptions changed.

If your aliases aren't resolving in Parcel v2, check both package.json and .parcelrc. Might need both.

Anyway, the build works now. Worth it.