Measured: installing the full dep tree vs the tree without the deps below = 109 MB → 86 MB (−23 MB, −21%) and 328 → 312 packages. Every npx apify-cli and CI job downloads this fresh.
Droppable now — native replacement on Node 20+
| Dep |
Usage |
Replace with |
axios |
2 files |
native fetch |
es-toolkit |
only cloneDeep, 9 call sites |
structuredClone() — 17 MB for one function |
rimraf |
1 wrapper (rimrafPromised) |
fs.rm(p, { recursive: true, force: true }) (~5 MB incl. glob/lru-cache chain) |
strip-ansi |
1 file |
util.stripVTControlCharacters() |
is-ci |
1 file |
ci-info.isCI — already a dependency; is-ci is that one-liner republished |
@root/walk |
1 file (scrapy wrapper) |
fs.readdir(dir, { recursive: true }) |
computer-name |
1 file (login device label) |
os.hostname() — also kills an unmaintained native addon |
cors |
login's localhost auth server |
~3 lines of headers on the existing express handler |
Duplicates — consolidate
adm-zip + archiver are imported in the same file (src/lib/utils.ts) — two full zip implementations; either covers read + write.
js-levenshtein + @skyra/jaro-winkler both power the same "did you mean" check in useCommandSuggestions.ts (levenshtein <= 2 || jaroWinkler >= 0.975). One metric is plenty — or inline a 20-line levenshtein and drop both.
Blocked on raising the Node floor
escape-string-regexp (2 files) → RegExp.escape() needs Node 24; a one-liner worth inlining meanwhile.
tinyglobby (1 file) → fs.glob stable in Node 24.
chalk (46 files) → util.styleText on 22+; big mechanical diff, zero user-visible gain — last.
Keepers
semver, ajv, handlebars, express, open, mime, giget, execa, inquirer/apify packages. which is borderline but earns its keep on Windows PATHEXT handling.
Suggested order
- Bump
engines to >=22 (Node 20 is EOL since 2026-04).
- One mechanical PR per dep — all but chalk are single-import.
~10 deps removable today, 23 MB off every install, and ~16 fewer packages to audit next time something like the figlet incident happens.
Measured: installing the full dep tree vs the tree without the deps below = 109 MB → 86 MB (−23 MB, −21%) and 328 → 312 packages. Every
npx apify-cliand CI job downloads this fresh.Droppable now — native replacement on Node 20+
axiosfetches-toolkitcloneDeep, 9 call sitesstructuredClone()— 17 MB for one functionrimrafrimrafPromised)fs.rm(p, { recursive: true, force: true })(~5 MB incl. glob/lru-cache chain)strip-ansiutil.stripVTControlCharacters()is-cici-info.isCI— already a dependency;is-ciis that one-liner republished@root/walkfs.readdir(dir, { recursive: true })computer-nameos.hostname()— also kills an unmaintained native addoncorsDuplicates — consolidate
adm-zip+archiverare imported in the same file (src/lib/utils.ts) — two full zip implementations; either covers read + write.js-levenshtein+@skyra/jaro-winklerboth power the same "did you mean" check inuseCommandSuggestions.ts(levenshtein <= 2 || jaroWinkler >= 0.975). One metric is plenty — or inline a 20-line levenshtein and drop both.Blocked on raising the Node floor
escape-string-regexp(2 files) →RegExp.escape()needs Node 24; a one-liner worth inlining meanwhile.tinyglobby(1 file) →fs.globstable in Node 24.chalk(46 files) →util.styleTexton 22+; big mechanical diff, zero user-visible gain — last.Keepers
semver,ajv,handlebars,express,open,mime,giget,execa, inquirer/apify packages.whichis borderline but earns its keep on Windows PATHEXT handling.Suggested order
enginesto>=22(Node 20 is EOL since 2026-04).~10 deps removable today, 23 MB off every install, and ~16 fewer packages to audit next time something like the figlet incident happens.