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:
- Static string aliases:
'@x': './src/x'
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
- Object-literal
resolve.alias only (no spread, no factory functions)
- 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.
Summary
The built-in Webpack plugin currently extracts entry points and loader/plugin imports from
webpack.config.js, but it does not parseresolve.alias. As a result, every aliased import in a project that relies on webpack aliases (without mirroring them intsconfig.jsonpaths) is reported asunresolved-import, and any file only reachable via an alias is reported asunused-file/unused-export.This is asymmetric with the Vite, Nuxt, and SvelteKit plugins, which already extract
resolve.alias,alias, andkit.aliasrespectively. See the "Plugins with deep config parsing" table in https://docs.fallow.tools/frameworks/built-in.Current behavior
Given:
fallow dead-codereports:unresolved-importon both import linesunused-fileforsrc/components/Button.tsandsrc/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.aliasentries fromwebpack.config.{js,ts,cjs,mjs}should be folded into the resolver, the same way Vite'sresolve.aliasalready is. Static object-literal entries withpath.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.jsoncompilerOptions.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, suppressingunresolved-importsglobally, 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:
'@x': './src/x'path.resolve(__dirname, 'src/x')andpath.join(__dirname, 'src/x')— these are the canonical forms in the webpack docs and in practice the overwhelming majority of configsresolve.aliasonly (no spread, no factory functions)module.exports = { ... }and ESMexport default { ... }Non-goals for v1 (can be follow-ups): array aliases,
$exact-match suffix, function configs,defineConfig()-style wrappers from third-party tools,tsconfigfield (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.