|
1 | 1 | # enhanced-resolve |
2 | 2 |
|
| 3 | +## 5.21.0 |
| 4 | + |
| 5 | +### Minor Changes |
| 6 | + |
| 7 | +- Added promise API. (by [@alexander-akait](https://github.com/alexander-akait) in [#520](https://github.com/webpack/enhanced-resolve/pull/520)) |
| 8 | + |
| 9 | +### Patch Changes |
| 10 | + |
| 11 | +- fix: prevent fallback to parent node_modules when exports field target file is not found (by [@xiaoxiaojx](https://github.com/xiaoxiaojx) in [#495](https://github.com/webpack/enhanced-resolve/pull/495)) |
| 12 | + |
| 13 | + When a package has an `exports` field that maps a request to a target file, |
| 14 | + but that target file does not exist on disk, enhanced-resolve was incorrectly |
| 15 | + falling back to search parent `node_modules` directories. This violated the |
| 16 | + Node.js ESM resolution spec, which requires resolution to fail with an error |
| 17 | + rather than continue searching up the directory tree. |
| 18 | + |
| 19 | + This manifested in monorepos where the same package exists at multiple levels |
| 20 | + (e.g. `workspace/node_modules/pkg` and `root/node_modules/pkg`): if the |
| 21 | + workspace version's exports-mapped target was missing, the resolver would |
| 22 | + silently resolve to the root version instead. |
| 23 | + |
| 24 | + Root cause: `ExportsFieldPlugin` was returning `null` on failure, which |
| 25 | + `Resolver.doResolve` converted to `undefined`, causing |
| 26 | + `ModulesInHierarchicalDirectoriesPlugin` to treat the lookup as "not found, |
| 27 | + try next directory" rather than a hard stop. |
| 28 | + |
| 29 | + Fix: when the `exports` field is present and a match is found but no valid |
| 30 | + target file can be resolved, return an explicit error to stop directory |
| 31 | + traversal. Closes #399. |
| 32 | + |
| 33 | +- Imports field spec deviation: non-relative targets (e.g. `"#a": "#b"`) no longer re-enter imports resolution, aligning with the Node.js ESM spec where `PACKAGE_IMPORTS_RESOLVE` does not recursively resolve `#` specifiers. (by [@xiaoxiaojx](https://github.com/xiaoxiaojx) in [#503](https://github.com/webpack/enhanced-resolve/pull/503)) |
| 34 | + |
| 35 | + Previously `{ "#a": "#b", "#b": "./the.js" }` would chain-resolve `#a` to `./the.js`; now it correctly fails, matching Node.js behavior. |
| 36 | + |
| 37 | +- Move `cachedJoin`/`cachedDirname`/`createCachedBasename` caches from module-level globals to per-Resolver instances. (by [@xiaoxiaojx](https://github.com/xiaoxiaojx) in [#507](https://github.com/webpack/enhanced-resolve/pull/507)) |
| 38 | + This prevents unbounded memory growth in long-running processes — when a Resolver is garbage collected, its join/dirname/basename caches are released with it. |
| 39 | + |
| 40 | + Also export `createCachedJoin`, `createCachedDirname` and `createCachedBasename` factory functions from `util/path` for creating independent cache instances. |
| 41 | + |
| 42 | +- Fixed when `tsconfig: true` is used (default config file) and no `tsconfig.json` exists. (by [@xiaoxiaojx](https://github.com/xiaoxiaojx) in [#502](https://github.com/webpack/enhanced-resolve/pull/502)) |
| 43 | + |
| 44 | +- Improved performance of the alias plugin. (by [@alexander-akait](https://github.com/alexander-akait) in [#529](https://github.com/webpack/enhanced-resolve/pull/529)) |
| 45 | + |
| 46 | +- Replace the `Set<string>`-based resolver stack with a singly-linked `StackEntry` class that exposes a Set-compatible API (`has`, iteration, `size`, `toString`). (by [@xiaoxiaojx](https://github.com/xiaoxiaojx) in [#526](https://github.com/webpack/enhanced-resolve/pull/526)) |
| 47 | + |
| 48 | + Each `doResolve` call now prepends a single linked-list node instead of cloning the entire Set, making stack push O(1) in time and memory. Recursion detection walks the linked list (O(n)), but because the stack is typically shallow this is much cheaper than cloning a Set per call. |
| 49 | + |
| 50 | + Because `StackEntry` implements the Set methods consumers use in practice, `resolveContext.stack` remains a drop-in replacement — existing callers that iterate or call `.has()` keep working. |
| 51 | + |
| 52 | + Benchmarks on the local suite: |
| 53 | + - `pathological-deep-stack` (50-level alias chain): +57% |
| 54 | + - `realistic-midsize` warm cache: +16% |
| 55 | + |
| 56 | +- Cache the result of `stripJsonComments` + `JSON.parse` in `readJson` using a `WeakMap` keyed by the raw file buffer. (by [@xiaoxiaojx](https://github.com/xiaoxiaojx) in [#524](https://github.com/webpack/enhanced-resolve/pull/524)) |
| 57 | + When `CachedInputFileSystem` serves the same buffer, the parsed result is reused; when the buffer is purged and garbage collected, the cache entry is automatically released. |
| 58 | + |
| 59 | + This avoids redundant comment-stripping and JSON parsing on every resolve call that reads tsconfig.json files (via `stripComments: true`), improving TsconfigPathsPlugin warm performance by ~20-35% depending on the depth of the `extends` chain. |
| 60 | + |
| 61 | +- Avoid OOM in CachedInputFileSystem when duration is Infinity. (by [@alexander-akait](https://github.com/alexander-akait) in [#527](https://github.com/webpack/enhanced-resolve/pull/527)) |
| 62 | + |
3 | 63 | ## 5.20.1 |
4 | 64 |
|
5 | 65 | ### Patch Changes |
|
0 commit comments