Repository navigation
Huge dependency size and tree #283
Copy link
Copy link
Open
Description
This simple library to talk to an API is somehow 113 dependencies long, 10 MiB in size with 5395 files.
└─┬ webflow-api@3.2.0
├─┬ crypto-browserify@3.12.1
│ ├─┬ browserify-cipher@1.0.1
│ │ ├─┬ browserify-aes@1.2.0
│ │ │ ├── buffer-xor@1.0.3
│ │ │ ├── cipher-base@1.0.6 deduped
│ │ │ ├── create-hash@1.2.0 deduped
│ │ │ ├── evp_bytestokey@1.0.3 deduped
│ │ │ ├── inherits@2.0.4 deduped
│ │ │ └── safe-buffer@5.2.1 deduped
│ │ ├─┬ browserify-des@1.0.2
│ │ │ ├── cipher-base@1.0.6 deduped
│ │ │ ├─┬ des.js@1.1.0
│ │ │ │ ├── inherits@2.0.4 deduped
│ │ │ │ └── minimalistic-assert@1.0.1 deduped
│ │ │ ├── inherits@2.0.4 deduped
│ │ │ └── safe-buffer@5.2.1 deduped
│ │ └─┬ evp_bytestokey@1.0.3
│ │ ├── md5.js@1.3.5 deduped
│ │ └── safe-buffer@5.2.1 deduped
│ ├─┬ browserify-sign@4.2.3
│ │ ├── bn.js@5.2.2
│ │ ├─┬ browserify-rsa@4.1.1
│ │ │ ├── bn.js@5.2.2 deduped
│ │ │ ├── randombytes@2.1.0 deduped
│ │ │ └── safe-buffer@5.2.1 deduped
│ │ ├── create-hash@1.2.0 deduped
│ │ ├── create-hmac@1.1.7 deduped
│ │ ├─┬ elliptic@6.6.1
│ │ │ ├── bn.js@4.12.2
│ │ │ ├── brorand@1.1.0
│ │ │ ├─┬ hash.js@1.1.7
│ │ │ │ ├── inherits@2.0.4 deduped
│ │ │ │ └── minimalistic-assert@1.0.1 deduped
│ │ │ ├─┬ hmac-drbg@1.0.1
│ │ │ │ ├── hash.js@1.1.7 deduped
│ │ │ │ ├── minimalistic-assert@1.0.1 deduped
│ │ │ │ └── minimalistic-crypto-utils@1.0.1 deduped
│ │ │ ├── inherits@2.0.4 deduped
│ │ │ ├── minimalistic-assert@1.0.1
│ │ │ └── minimalistic-crypto-utils@1.0.1
│ │ ├── hash-base@3.0.5 deduped
│ │ ├── inherits@2.0.4 deduped
│ │ ├─┬ parse-asn1@5.1.7
│ │ │ ├─┬ asn1.js@4.10.1
│ │ │ │ ├── bn.js@4.12.2
│ │ │ │ ├── inherits@2.0.4 deduped
│ │ │ │ └── minimalistic-assert@1.0.1 deduped
│ │ │ ├── browserify-aes@1.2.0 deduped
│ │ │ ├── evp_bytestokey@1.0.3 deduped
│ │ │ ├── hash-base@3.0.5 deduped
│ │ │ ├── pbkdf2@3.1.3 deduped
│ │ │ └── safe-buffer@5.2.1 deduped
│ │ ├─┬ readable-stream@2.3.8
│ │ │ ├── core-util-is@1.0.3
│ │ │ ├── inherits@2.0.4 deduped
│ │ │ ├── isarray@1.0.0
│ │ │ ├── process-nextick-args@2.0.1
│ │ │ ├── safe-buffer@5.1.2
│ │ │ ├─┬ string_decoder@1.1.1
│ │ │ │ └── safe-buffer@5.1.2
│ │ │ └── util-deprecate@1.0.2
│ │ └── safe-buffer@5.2.1
│ ├─┬ create-ecdh@4.0.4
│ │ ├── bn.js@4.12.2
│ │ └── elliptic@6.6.1 deduped
│ ├─┬ create-hash@1.2.0
│ │ ├─┬ cipher-base@1.0.6
│ │ │ ├── inherits@2.0.4 deduped
│ │ │ └── safe-buffer@5.2.1 deduped
│ │ ├── inherits@2.0.4 deduped
│ │ ├─┬ md5.js@1.3.5
│ │ │ ├── hash-base@3.0.5 deduped
│ │ │ ├── inherits@2.0.4 deduped
│ │ │ └── safe-buffer@5.2.1 deduped
│ │ ├─┬ ripemd160@2.0.2
│ │ │ ├── hash-base@3.0.5 deduped
│ │ │ └── inherits@2.0.4 deduped
│ │ └─┬ sha.js@2.4.12
│ │ ├── inherits@2.0.4 deduped
│ │ ├── safe-buffer@5.2.1 deduped
│ │ └── to-buffer@1.2.1 deduped
│ ├─┬ create-hmac@1.1.7
│ │ ├── cipher-base@1.0.6 deduped
│ │ ├── create-hash@1.2.0 deduped
│ │ ├── inherits@2.0.4 deduped
│ │ ├── ripemd160@2.0.2 deduped
│ │ ├── safe-buffer@5.2.1 deduped
│ │ └── sha.js@2.4.12 deduped
│ ├─┬ diffie-hellman@5.0.3
│ │ ├── bn.js@4.12.2
│ │ ├─┬ miller-rabin@4.0.1
│ │ │ ├── bn.js@4.12.2
│ │ │ └── brorand@1.1.0 deduped
│ │ └── randombytes@2.1.0 deduped
│ ├─┬ hash-base@3.0.5
│ │ ├── inherits@2.0.4 deduped
│ │ └── safe-buffer@5.2.1 deduped
│ ├── inherits@2.0.4
│ ├─┬ pbkdf2@3.1.3
│ │ ├─┬ create-hash@1.1.3
│ │ │ ├── cipher-base@1.0.6 deduped
│ │ │ ├── inherits@2.0.4 deduped
│ │ │ ├── ripemd160@2.0.1 deduped
│ │ │ └── sha.js@2.4.12 deduped
│ │ ├── create-hmac@1.1.7 deduped
│ │ ├─┬ ripemd160@2.0.1
│ │ │ ├─┬ hash-base@2.0.2
│ │ │ │ └── inherits@2.0.4 deduped
│ │ │ └── inherits@2.0.4 deduped
│ │ ├── safe-buffer@5.2.1 deduped
│ │ ├── sha.js@2.4.12 deduped
│ │ └─┬ to-buffer@1.2.1
│ │ ├── isarray@2.0.5
│ │ ├── safe-buffer@5.2.1 deduped
│ │ └─┬ typed-array-buffer@1.0.3
│ │ ├── call-bound@1.0.4 deduped
│ │ ├── es-errors@1.3.0 deduped
│ │ └─┬ is-typed-array@1.1.15
│ │ └─┬ which-typed-array@1.1.19
│ │ ├─┬ available-typed-arrays@1.0.7
│ │ │ └── possible-typed-array-names@1.1.0
│ │ ├─┬ call-bind@1.0.8
│ │ │ ├── call-bind-apply-helpers@1.0.2 deduped
│ │ │ ├── es-define-property@1.0.1 deduped
│ │ │ ├── get-intrinsic@1.3.0 deduped
│ │ │ └─┬ set-function-length@1.2.2
│ │ │ ├─┬ define-data-property@1.1.4
│ │ │ │ ├── es-define-property@1.0.1 deduped
│ │ │ │ ├── es-errors@1.3.0 deduped
│ │ │ │ └── gopd@1.2.0 deduped
│ │ │ ├── es-errors@1.3.0 deduped
│ │ │ ├── function-bind@1.1.2 deduped
│ │ │ ├── get-intrinsic@1.3.0 deduped
│ │ │ ├── gopd@1.2.0 deduped
│ │ │ └─┬ has-property-descriptors@1.0.2
│ │ │ └── es-define-property@1.0.1 deduped
│ │ ├── call-bound@1.0.4 deduped
│ │ ├─┬ for-each@0.3.5
│ │ │ └── is-callable@1.2.7
│ │ ├── get-proto@1.0.1 deduped
│ │ ├── gopd@1.2.0 deduped
│ │ └── has-tostringtag@1.0.2 deduped
│ ├─┬ public-encrypt@4.0.3
│ │ ├── bn.js@4.12.2
│ │ ├── browserify-rsa@4.1.1 deduped
│ │ ├── create-hash@1.2.0 deduped
│ │ ├── parse-asn1@5.1.7 deduped
│ │ ├── randombytes@2.1.0 deduped
│ │ └── safe-buffer@5.2.1 deduped
│ ├─┬ randombytes@2.1.0
│ │ └── safe-buffer@5.2.1 deduped
│ └─┬ randomfill@1.0.4
│ ├── randombytes@2.1.0 deduped
│ └── safe-buffer@5.2.1 deduped
├─┬ form-data@4.0.4
│ ├── asynckit@0.4.0
│ ├─┬ combined-stream@1.0.8
│ │ └── delayed-stream@1.0.0
│ ├─┬ es-set-tostringtag@2.1.0
│ │ ├── es-errors@1.3.0
│ │ ├─┬ get-intrinsic@1.3.0
│ │ │ ├─┬ call-bind-apply-helpers@1.0.2
│ │ │ │ ├── es-errors@1.3.0 deduped
│ │ │ │ └── function-bind@1.1.2 deduped
│ │ │ ├── es-define-property@1.0.1
│ │ │ ├── es-errors@1.3.0 deduped
│ │ │ ├─┬ es-object-atoms@1.1.1
│ │ │ │ └── es-errors@1.3.0 deduped
│ │ │ ├── function-bind@1.1.2 deduped
│ │ │ ├─┬ get-proto@1.0.1
│ │ │ │ ├─┬ dunder-proto@1.0.1
│ │ │ │ │ ├── call-bind-apply-helpers@1.0.2 deduped
│ │ │ │ │ ├── es-errors@1.3.0 deduped
│ │ │ │ │ └── gopd@1.2.0 deduped
│ │ │ │ └── es-object-atoms@1.1.1 deduped
│ │ │ ├── gopd@1.2.0
│ │ │ ├── has-symbols@1.1.0
│ │ │ ├── hasown@2.0.2 deduped
│ │ │ └── math-intrinsics@1.1.0
│ │ ├─┬ has-tostringtag@1.0.2
│ │ │ └── has-symbols@1.1.0 deduped
│ │ └── hasown@2.0.2 deduped
│ ├─┬ hasown@2.0.2
│ │ └── function-bind@1.1.2
│ └─┬ mime-types@2.1.35
│ └── mime-db@1.52.0
├── formdata-node@6.0.3
├── js-base64@3.7.7
├─┬ node-fetch@2.7.0
│ ├── UNMET OPTIONAL DEPENDENCY encoding@^0.1.0
│ └─┬ whatwg-url@5.0.0
│ ├── tr46@0.0.3
│ └── webidl-conversions@3.0.1
├─┬ qs@6.14.0
│ └─┬ side-channel@1.1.0
│ ├── es-errors@1.3.0 deduped
│ ├── object-inspect@1.13.4
│ ├─┬ side-channel-list@1.0.0
│ │ ├── es-errors@1.3.0 deduped
│ │ └── object-inspect@1.13.4 deduped
│ ├─┬ side-channel-map@1.0.1
│ │ ├─┬ call-bound@1.0.4
│ │ │ ├── call-bind-apply-helpers@1.0.2 deduped
│ │ │ └── get-intrinsic@1.3.0 deduped
│ │ ├── es-errors@1.3.0 deduped
│ │ ├── get-intrinsic@1.3.0 deduped
│ │ └── object-inspect@1.13.4 deduped
│ └─┬ side-channel-weakmap@1.0.2
│ ├── call-bound@1.0.4 deduped
│ ├── es-errors@1.3.0 deduped
│ ├── get-intrinsic@1.3.0 deduped
│ ├── object-inspect@1.13.4 deduped
│ └── side-channel-map@1.0.1 deduped
├─┬ readable-stream@4.7.0
│ ├─┬ abort-controller@3.0.0
│ │ └── event-target-shim@5.0.1
│ ├─┬ buffer@6.0.3
│ │ ├── base64-js@1.5.1
│ │ └── ieee754@1.2.1
│ ├── events@3.3.0
│ ├── process@0.11.10
│ └─┬ string_decoder@1.3.0
│ └── safe-buffer@5.2.1 deduped
└── url-join@4.0.1
I've made attempts at this for some open source projects, and what usually happens is some accept PR that replace dependencies for lighter ones, others will often frown or fight every change depending on their internal culture.
So my question is, is there a way to clean this up? And is this repo friendly towards such attempts or not?
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
zplata commentedon Aug 29, 2025
Hey @TheThing, thanks a ton for raising this. Some context to set:
crypto-browserifyis brought in, for webhook verification as an example)We'll look to see how much of the size we can cut here, but if you have ideas or quick wins you see that you'd like to contribute, feel free to open a PR! Changes to
package.jsonwill ultimately come from an internal source-of-truth repo that generates the next SDK output, but we can make those modifications based on any changes you make through an accepted PRTheThing commentedon Sep 4, 2025
Thank you for the reply.
Yeah the tree is big but that's mostly because of some strange imports by sup-dependency most often.
For example the package
form-datais pullinges-set-tostringtagwhich is pulling a tree of total 23 packages (some deduped but still a too large tree just to doset Symbol.toStringTag, when possible.).The biggest problem is replacing these (and why I was asking about the culture of the company), is because the only two ways of "fixing" this is:
a) For me to replace a known, big, popular dependency (like
form-data) with less-known, less-popular lighter alternatives.b) For me to take a large dependency (that does a simple job or is only being used for a simple thing) and rewrite it without the unnecessary sub-dependencies and submitting PR using that.
I've done this before where I replaced a large dependency tree coming from a singular package with an alternative that I worked hard to make and ensure was 100% api compatible, only for it to eventually reach someone higher up in the decision chain reject the whole thing due to fear of supply-chain attack (resulting in a ton of thrown work).
Hence this inquiry and question, I'm just trying to avoid having to go through the motions again and have entire work thrown out due to supply-chain attack fears :)
This is a great example of something that could be cleaned up.
If
crypto-browserifyis being pulled in for, say hypothetically only to verify HMAC signature, then instead of pulling the entire crypto library that also has a ton of other features (like AES encryption, ellyptic curve and etc), it is possible to replace it with a smaller dependency that only has the exposed functions needed by the SDK and only pull in sub-dependency needed for those.This is just an example of what can be done to cleanup the tree and something I would happily be willing to do and tackle. I myself have strong fear of supply-chain attacks and so I have big distrust towards large dependency tree, they all look like attack vectors to me.
Unfortunately, in the example I mentioned above, it would probably require me building a new dependency which from a perspective of a developer like yourself, would replace one larger potential supply-chain problem with smaller but focused potential supply-chain problems :)
Hence this inquiry. It would not be hard for me to, for example if I was trying to trim
crypto-browserify, figure out what is needed, and then make a dependency that does that to trim of all the other fat. However I would like to avoid doing that work if the fear of supply-chain is stronger than wanting smaller dependency tree and wasting work :)zplata commentedon Sep 4, 2025
Hey @TheThing - really appreciate your line of thinking on this, and totally understand where you're coming from in investing time.
Thankfully in these two cases,
form-dataandcrypto-browserify, these are "additive" libraries in that it is not crucial to the generative function for the SDK (and only brought in for some of those extra helper methods I mentioned). In general, we'll first lean on more well-known and maintained dependencies as they are more likely to be patched for security vulnerabilities, bug fixes, all that. In the case ofform-data, I think there's definitely room to nix using this. It's only used in this helper method here.With
crypto-browserify, our team didn't find an immediate alternative but we'd be open to moving off this for a slimmer alternative. We'll often run security scanners on our dependency trees to look for vulnerabilities so dependencies may change/evolve. With that in mind, that could mean moving off crypto-browserify (and any other dependency we currently use), leaning in more for future functions we need, etc., and in those cases, wouldn't want your efforts to be thrown away! For more custom control there, feel free to fork the repo and deploy (and simply remove the helper functions if you don't need them, thus removing both dependencies). But would love to still consider any contributions or ideas you might have if you find alternatives.