Skip to content

Parse resolve.alias from webpack.config.js (parity with Vite plugin) #273

@michaljuris

Description

@michaljuris

Summary

The built-in Webpack plugin currently extracts entry points and loader/plugin imports from webpack.config.js, but it does not parse resolve.alias. As a result, every aliased import in a project that relies on webpack aliases (without mirroring them in tsconfig.json paths) is reported as unresolved-import, and any file only reachable via an alias is reported as unused-file / unused-export.

This is asymmetric with the Vite, Nuxt, and SvelteKit plugins, which already extract resolve.alias, alias, and kit.alias respectively. See the "Plugins with deep config parsing" table in https://docs.fallow.tools/frameworks/built-in.

Current behavior

Given:

// webpack.config.js
const path = require('path');

module.exports = {
  resolve: {
    alias: {
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils'),
    },
  },
};
// src/app.ts
import { Button } from '@components/Button';
import { format } from '@utils/format';

fallow dead-code reports:

  • unresolved-import on both import lines
  • unused-file for src/components/Button.ts and src/utils/format.ts (cascading, since they're not reachable from any entry point fallow knows about)

No error in the actual webpack build; no error in the editor (TS picks up paths separately); just fallow.

Expected behavior

When the Webpack plugin is active, resolve.alias entries from webpack.config.{js,ts,cjs,mjs} should be folded into the resolver, the same way Vite's resolve.alias already is. Static object-literal entries with path.resolve(__dirname, '...') / path.join(__dirname, '...') / plain string values are the common cases and would cover the majority of real configs.

Workaround (current)

Mirror the aliases into tsconfig.json compilerOptions.paths — fallow's TypeScript plugin reads them. This works but creates a second source of truth and isn't always possible (e.g. JS-only projects, or repos where the webpack alias map is generated dynamically and the tsconfig is hand-written).

Other workarounds (dynamicallyLoaded, suppressing unresolved-imports globally, per-file // fallow-ignore-next-line) all suppress the symptom rather than fix resolution, so other findings (unused-file cascades, unused-export cascades) need separate suppression.

Proposed scope

Start small, mirror what already works for Vite:

  1. Static string aliases: '@x': './src/x'
  2. path.resolve(__dirname, 'src/x') and path.join(__dirname, 'src/x') — these are the canonical forms in the webpack docs and in practice the overwhelming majority of configs
  3. Object-literal resolve.alias only (no spread, no factory functions)
  4. Both module.exports = { ... } and ESM export default { ... }

Non-goals for v1 (can be follow-ups): array aliases, $ exact-match suffix, function configs, defineConfig()-style wrappers from third-party tools, tsconfig field (would just defer to the existing TypeScript plugin), Rspack (separate plugin but identical config shape — could be a copy).

Anything outside the supported AST shapes would gracefully no-op, consistent with the existing "Config parsing ceiling" behavior documented at https://docs.fallow.tools/configuration/overview.

Why this matters

Webpack is still the bundler for a long tail of production codebases — CRA-derived apps, custom SSR setups, internal tooling, monorepos that haven't migrated to Vite/Rspack. For all of them, fallow currently produces a wall of false positives on first run, which is the exact moment a tool needs to earn trust. The Vite plugin already proves the parsing approach is tractable; bringing the Webpack plugin to parity should be mostly mechanical.

Happy to put together a repro repo if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions