refactor(webpack-cli): replace fastest-levenshtein with in-tree implementation#4762
Conversation
…mentation Drop the fastest-levenshtein dependency in favor of a small in-tree Levenshtein distance (Myers' bit-parallel algorithm, inspired by fastest-levenshtein) used for command/option "did you mean" suggestions, and add unit tests for it. https://claude.ai/code/session_013zogbY9uURTCwhdRrifVBt
🦋 Changeset detectedLatest commit: c4200f2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4762 +/- ##
==========================================
+ Coverage 92.58% 92.82% +0.24%
==========================================
Files 14 15 +1
Lines 4911 5076 +165
Branches 731 752 +21
==========================================
+ Hits 4547 4712 +165
Misses 362 362
Partials 2 2
Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR removes the fastest-levenshtein dependency from webpack-cli and replaces it with an in-tree Levenshtein distance implementation (Myers’ bit-parallel algorithm) to power “did you mean” suggestions, along with unit tests.
Changes:
- Added an in-tree
distance()implementation inpackages/webpack-cli/src/levenshtein.tsand switchedwebpack-clito use it. - Removed
fastest-levenshteinfrompackages/webpack-cli/package.jsonandpackage-lock.json. - Added Jest tests for the new distance implementation and a changeset entry documenting the patch change.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
test/api/levenshtein.test.js |
Adds unit tests for the new in-tree Levenshtein distance() helper. |
packages/webpack-cli/src/webpack-cli.ts |
Replaces the external fastest-levenshtein import with the new in-tree implementation. |
packages/webpack-cli/src/levenshtein.ts |
Introduces the Myers bit-parallel Levenshtein implementation used for suggestions. |
packages/webpack-cli/package.json |
Drops fastest-levenshtein from dependencies. |
package-lock.json |
Removes the fastest-levenshtein lockfile entries. |
.changeset/replace-fastest-levenshtein.md |
Records the dependency replacement as a patch-level change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| peq[b.charCodeAt(k)] |= 1 << k; | ||
| } | ||
|
|
||
| for (let i = 0; i < n; i++) { | ||
| const eq = peq[a.charCodeAt(i)]; | ||
| const pb = (phc[(i / 32) | 0] >>> i) & 1; | ||
| const mb = (mhc[(i / 32) | 0] >>> i) & 1; | ||
| const xv = eq | mv; | ||
| const xh = ((((eq | mb) & pv) + pv) ^ pv) | eq | mb; | ||
| let ph = mv | ~(xh | pv); | ||
| let mh = pv & xh; | ||
|
|
||
| if ((ph >>> 31) ^ pb) { | ||
| phc[(i / 32) | 0] ^= 1 << i; | ||
| } | ||
|
|
||
| if ((mh >>> 31) ^ mb) { | ||
| mhc[(i / 32) | 0] ^= 1 << i; |
There was a problem hiding this comment.
Leaving this as-is: the word is already selected explicitly via (i / 32) | 0, and the within-word shift intentionally relies on JS's standard mask-to-31 semantics. This is a faithful port of the upstream fastest-levenshtein algorithm, verified bit-for-bit against it (including a 2000-case fuzz across the myersX paths), so I'd prefer not to rewrite the hot loop and risk diverging from the reference. The parameter-naming and long-vs-short-test suggestions are addressed in c4200f2.
Generated by Claude Code
| function myersX(b: string, a: string): number { | ||
| const n = a.length; | ||
| const m = b.length; | ||
| const mhc: number[] = []; | ||
| const phc: number[] = []; | ||
| const horizontalSize = Math.ceil(n / 32); | ||
| const verticalSize = Math.ceil(m / 32); |
| const b = `${"a".repeat(39)}b`; | ||
|
|
||
| expect(distance(a, b)).toBe(1); | ||
| expect(distance("a".repeat(40), "b".repeat(40))).toBe(40); |
…case Address review feedback: rename the swapped myersX parameters to longer/shorter so they match their internal usage, and add a regression test for a long string against a much shorter one (40 vs 5). https://claude.ai/code/session_013zogbY9uURTCwhdRrifVBt
Drop the fastest-levenshtein dependency in favor of a small in-tree
Levenshtein distance (Myers' bit-parallel algorithm, inspired by
fastest-levenshtein) used for command/option "did you mean" suggestions,
and add unit tests for it.
https://claude.ai/code/session_013zogbY9uURTCwhdRrifVBt