Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

always hilarious to see the dumb dependencies that these projects have

https://github.com/sindresorhus/arrify

https://github.com/JedWatson/classnames



While I can understand your skepticism on the first one, the second package is actually very useful. It doesn't just concatenate strings filtering out falsy values, it supports the whole old AngularJS class format. You can, for example, pass an object {string:booleanish} and it will add only the keys whose values are truthy.

Pretty neat, actually.


> You can, for example, pass an object {string:booleanish} and it will add only the keys whose values are truthy.

Why is this something you can't do on your own in a few lines of code?

  const classNames = (o, c=[]) => {
    for (let k in o) if (o[k]) c.push(k) // booleanish
    return c.join(" ")
  } 
This is not cool at all! Why import a trivial function over NPM to do this for you? Better to lower your dependency count and have a "util.js" file for helpers like this.


I am starting to follow Sindre's philosophy [1] more and more. I published this morning `fch` [2], a 10-15 LoC library (note: undocumented for now). I will test it well and make sure it works everywhere. In exchange, I removed basically the same code from 3 projects (so far), some of them with subtle bugs.

The cost of using it is the same as locally, and creation cost is lower per-project if you let npm manage your own libs. Plus, bug fixes are automatically propagated.

[1] https://github.com/sindresorhus/ama/issues/10#issuecomment-1...

[2] https://github.com/franciscop/fetch


Your little utility here doesn’t cover the whole 'classnames' API.

  export default function classNames(...args) {
    return args.filter(valid).map(single);
  }

  function valid(arg) {
    return arg && (!Array.isArray(arg) || arg.length);
  }

  function single(arg) {
    if (typeof arg === 'string') {
      return arg;
    } else if (Array.isArray(arg)) {
      return classNames(...arg);
    } else {
      return Object.keys(arg).filter(k => ………
This is getting similar…


> Your little utility here doesn’t cover the whole 'classnames' API.

Thats partly the point. The whole API doesn't really need to be covered in order to achieve the same utility. I think the library clearly over-engineered the simple task of string concatenation, and their API is redundant.

Their API caters to too many styles and that increases its complexity. It takes 2 seconds to grok the two lines in my utility, whereas it takes 5 minutes to make sure you know all 50 combinatorial ways to concatenate a string with the classNames library. There is too much indecision in the API, so they decided to support everything they could think of. Now I have to be ready for the possibility that the 5 people on my team can't agree on a convention simply because the library allows every possible convention one could desire.


You don't "agree" on a convention, they have different use cases. Sometimes you'll want to merge a bunch of class names so you use array-style. Other times you have class names that have prop dependencies so you use object style and finally you'll want to merge in some always-on defaults so you have string style. You'll often need to combine them, depending on use-case.

That's a whole lot of complaints for 50 lines of code.


> Sometimes you'll want to merge a bunch of class names so you use array-style

> Other times you have class names that have prop dependencies so you use object style

> merge in some always-on defaults so you have string style

There's some irony here -- all of this just to avoid dealing with string templates:

  `${class1} ${class2} ${class3}`
> You'll often need to combine them

Need is definitely not the right word here. The classNames utility is a completely unnecessary abstraction.


> There's some irony here -- all of this just to avoid dealing with string templates:

By that argument, we can just avoid everything by not making our apps dynamic!

> The classNames utility is a completely unnecessary abstraction.

The only necessary abstraction in programming is assembly since 0's and 1's are hard to reason about. The "need" in this case refers to needing to mix different primitives to make your app dynamic. Your arbitrary example above doesn't do anything because you still have conditional logic to determine what class1 class2 and class3 are, you are just doing a bunch of ugly crap in the render function rather than abstracting it. Or implementing your own version because you're smart.

It's a stupid hill to die on here... you want to argue about importing the entire lodash library because you like .chain, have at it. But I guess you can keep patting yourself on the back for avoiding importing a no-dependency 50-line library.


You want `undefined` and `null` to show up in class names? You'd at least need `${class1||''}` and your solution will grow in cases from there despite your one-liner.

Your solution doesn't do anything I'd expect someone wants that lib for, like merging in a map of classes.

I don't think this lib is the best pyre to die on to make a point I generally agree with.


Ok, no one is shoveling it down your throat. Just avoid it.

But be prepared because it is the de-facto standard way of building classNames in React, also because it was part of the standard utilities provided by React.


I get the "dependency hell" issue in Javascript world, but why would I rewrite this particular utility? It's well tested (both unit and field tested), includes edge cases and has zero dependencies. As a dependency, I will also get any future bug fixes or features if I wish to. An even better example is lodash. Would you really rewrite every util you need just for the sake of not having external dependencies?


Probably because the scope is so small that you _could_ actually rewrite it yourself. And then together with all these other toy libraries, stick it in one big one that covers everything you need in terms of convenience functions.


So much this. It's 30 lines of code. The remaining code is comment fluff and 15 lines dedicated to the various types of export utilities (AMD, NPM, window). Seriously this is such an odd package to start these arguments with.


Playing devil's advocate, why is lowering your dependency count a good thing?


Dependencies have cost. You have to monitor for updates, notify the maintainer(s) of any bugs, keep an eye out for security vulnerabilities, and sometimes (gasp) even step through them with a debugger.

Doing that for one dependency is bad enough, but for 100s it's a nightmare.

Personally, I prefer to just pull the pieces I need out of an open source library (unless it's very well maintained, or huge). It's like doing a code review at the same time, so you're aware of what's going on in your application.


Copy/pasting has a nearly identical cost (aside from notifying the maintainer), except you have to catch all of them, you removed an easy upgrade path when other people catch the issues and you're spending vastly more time "code reviewing" every dependency you have rather than actually producing the software you're trying to write.

Don't know about you, but we have actual goals and I'd never meet them if I tried your method.


I think he might be talking about trivial dependencies, like leftpad, classNames, arrify, etc. In the case of trivial dependencies, rolling your 30-line "util.js" file to remove 3 dependencies is definitely worth it. No one is suggesting you roll your own diffing algorithm.


I think I already responded to you elsewhere, but in case I'm incorrect: No it is not worth it. You are re-inventing the wheel so you can pat yourself on the back. Nobody hires a software engineer to write leftpad. Rolling your own diffing algorithm is exactly what actual engineering entails.


> You are re-inventing the wheel

Not quite, that would imply time and effort. I'm talking about reinventing the toothpick (2-3 lines of code), which is much different than reinventing a complex library for the sake of reinventing it.

> Rolling your own diffing algorithm is exactly what actual engineering entails

That's nice, but you're addressing a straw man. No one actually said rolling your own diffing algorithm is not what engineering entails. It was said that no one is suggesting you recreate a non-trivial dependency for the sake of reducing your dependency count.

I can use it in an example if it helps: If you are hired to write a basic website and you choose React, then you probably shouldn't reinvent React as your first step in writing the website. React is a fine dependency because it does a lot.

Humor me -- the next time you use classNames for a small project, try and see if you can't replace all of your calls to classNames with very basic template strings or other trivial code. It should not take any effort. For example I've seen things similar to "classNames({myClass: true})" in code before. Can you see what is wrong with this line? When people get comfortable with abstractions, they completely forget how to write the simplest code.


> Not quite, that would imply time and effort. I'm talking about reinventing the toothpick (2-3 lines of code), which is much different than reinventing a complex library for the sake of reinventing it.

Re-inventing 1000 versions of a tooth pick is reinventing the wheel.

> That's nice, but you're addressing a straw man.

You keep using that term and I'm not sure you understand what it means.

> It was said that no one is suggesting you recreate a non-trivial dependency for the sake of reducing your dependency count.

And my argument is that dependency count is one of the dumbest metrics to be interested in. Size, sure as that impacts bundle size, which in-turn, effects latency. But "count" is meaningless.

> Humor me -- the next time you use classNames for a small project

I use styled-components exclusively for React projects now.

> For example I've seen things similar to "classNames({myClass: true})" in code before.

So? Bad code is bad, that's not a library failure. Now that is a straw man.

> Can you see what is wrong with this line?

Of course I can, because I'm actually good at my job. I also wouldn't do that. Using your toaster in a bathtub is also REALLY stupid, but that doesn't mean we should ban toasters.

> When people get comfortable with abstractions, they completely forget how to write the simplest code.

No that's called laziness and it has nothing to do with dependencies. You have twice in this thread accused me of straw men inaccurately, and now wrote a paragraph dedicated to a huge straw man.


> Personally, I prefer to just pull the pieces I need out of an open source library (unless it's very well maintained, or huge). It's like doing a code review at the same time, so you're aware of what's going on in your application.

To quote yourself: Doing that for one dependency is bad enough, but for 100s it's a nightmare.

'Forking' a project's dependencies (which is effectively what this is...) is often fraught with as much trouble as just having the damn dependency in the first place.


I think the thing is that the one library you are forking contains 50 frivolous ones, so you’d really only be doing it twice.


Replace “dependency” with “code / function” in your post and you will see the problem.


What’s nice about it is that a lot of projects use the same util, meaning it is very easy to read or edit if you are familiar with the library. If everybody writes a custom function you will need to learn how to do classNames separately in every project you work on.


Yeah this argument falls flat in the case of classnames where we're comparing reading a two liner function with reading a page of docs for a library.

It's like everyone in JS land is allergic to writing a piece of code that isn't just playing lego with things written by a real developer.


Yeah, my React code is currently littered with conditions and `classNames.join(' ')`. I'm going to start using this library.


In ideal world all functions should be pure and be a dependency and all code should be just pure functions.


Second one is used only once in the code base to concat two strings together afaik


Curious what your issue with classnames is? Is it just because it’s a small and simple module (50 lines of code)?


I was curious so I checked out their Github page. I found this example:

  classNames('foo', 'bar'); // => 'foo bar'
  classNames('foo', { bar: true }); // => 'foo bar'
  classNames({ 'foo-bar': true }); // => 'foo-bar'
  classNames({ 'foo-bar': false }); // => ''
  classNames({ foo: true }, { bar: true }); // => 'foo bar'
  classNames({ foo: true, bar: true }); // => 'foo bar'
  ...
  var btnClass = classNames({
    btn: true,
    'btn-pressed': this.state.isPressed,
    'btn-over': !this.state.isPressed && this.state.isHovered
  });
Look like the button example is a good enough convention for me, I don't need any of the other conventions. Can anyone tell me why this is not sufficient?:

  const classNames = (pojo, c=[]) => {
    for (let k in pojo) if (pojo[k]) c.push(k);
    return !!c.length && c.join(" ");
  }
Do I need array-flattening support, multiple argument support? The size of this library probably comes from it supporting a handful of different coding styles. I would say the size (50 lines) is too large, if anything. Dependency worthy? I'm not convinced. I am very much so against requiring trivial utility functions from other people. Roll your own if it's trivial! It's better than depending on something over the network.


My usage is typically

  const cmpClass = classnames('componentsMainClass', props.className, { dynamicClassesHere });
I like that the component's main class stands out and is easy to find, that allowing the consumer to pass along their own class is trivial, and that the dynamic classes are grouped together.

So I'd argue multi argument and falsy thinning are welcome features. I've never used array flattening.

Does it really need to be a dependency? I guess that's a matter of opinion. I like that the various React projects I deal with on a regular basis all handle classes in a consistent manner. I also don't usually mind dependencies that don't pull the entire world in with them.


> const cmpClass = classnames('componentsMainClass', props.className, { dynamicClassesHere });

This is a matter of convention. Another, equally valid convention:

  classNames({
    mainClass: true, // first key is main class
    [props.className]: true, // derived
    otherClass: true, // rest
    yetAnotherClass: true // ...
  })
At the end of the day, everyone has to learn their codebase's convention, and that's all that really matters. One convention isn't better or worse than any other, but it's best to have one and stick with it. I don't need a library which supports 10 different conventions if my chosen convention only requires 2 lines of code. Your convention could be implemented in a few lines of code, as well, and then you'd be forced to stick with it, which is probably better. Using this library, 5 different developers could be using 5 different conventions. The helper function I suggest forces a single convention, which lowers complexity.


Readability is important, your example above would need to be

  classNames({
    mainClass: true,
    [props.className]: props.className,
    otherClass: somePredicate
  })
The props.className one in particular is pretty garish. Probably should do !!props.className to help explain intent a bit, which adds to the tedium. Once you've added falsy thinning and arbitrary arguments, you're 99% of the way to classnames.


Roll your own, untested function vs a small, widely used library with a well-understood, well-tested API that your devs have a good chance of being familiar with already. I too want to avoid bloated npm dependencies but this seems like a fair engineering trade-off..


This is a trivial function, your argument applies to complex utilities. It is piss easy to roll your own, and it's not worth introducing a potential attack vector via a new dependency. In this case, if your devs can't keep up with your classNames function, fire them, because it's trivial.

  it("should work with no classes", () => {
    expect(classNames({}).to.be.false
  })
  it("should work with a single class", () => {
    expect(classNames({a:true})).to.equal("a")
  })
  it("should work with booleans", () => {
    expect(classNames({a:true, b: false}).to.equal("a b")
  })
  it("should work with falsy and truthy values", () => {
    expect(classNames({a: 5, b: ""}).to.equal("a b")
  })

This isn't redux. It's a trivial utility function. Your kind of thinking is too often applied without thinking. Before depending on someone's code, ask yourself if you can roll your own in under 5 minutes. The answer could easily be yes. There is no good reason to depend on someone else's code if it's trivial. Having more dependencies than you need is a cardinal sin that way too many javascript developers commit.


If you write 100 ~50 line "trivial functions" you've now written 5000 lines of code that doesn't actually solve your underlying problem, it just makes it slightly easier.

And you can't write this particular utility function in 5 minutes and support all the uses-cases, particularly if you are going to write a bunch of unit tests.

The reason to depend on someone else's code in trivial use-cases is entirely because they are trivial. There's nothing you're doing aside from wasting time by writing it. lodash is literally a massive library of mostly "trival" functions that no one is every going to write themselves because WHY waste the time?

The only cardinal sin at play here is thinking that wasting your time reinventing the wheel is time well spent. This approach is what I'd expect from either a very young developer or a very self-centered one.


> If you write 100 ~50 line "trivial functions

No, we're talking about 2-3 line functions. Stop arguing with a straw man. You're taking what you know to be true about complex code, and then asserting that those things are also true with simple code. That doesn't work.

> And you can't write this particular utility function in 5 minutes and support all the uses-cases

The beauty is that because we are rolling our own function, we only need to support the single function signature we decide we're going to stick to in our codebase. Supporting every use-case becomes a moot point.

The author of the library has to support the 10^5 people who use his code, meaning he's more likely to merge silly pull requests like "should take objects", "should flatten the input array", "should allow multiple arguments", "should work with X", "should do Y" because people have different styles.

> The reason to depend on someone else's code in trivial use-cases is entirely because they are trivial. There's nothing you're doing aside from wasting time by writing it

It only takes time if you are not able to do basic programming. I find basic programming to be a much more enjoyable and productive approach than depending on potentially dozens of trivial libraries whose APIs are not the way I would design them, and could change at any time. Learning dozens of APIs takes just as much time as implementing them for a certain set of functions. That set is different for everyone, but it is not the null set for anyone.


> No, we're talking about 2-3 line functions. Stop arguing with a straw man. You're taking what you know to be true about complex code, and then asserting that those things are also true with simple code. That doesn't work.

The library in question is literally 50 lines of code. About 30 when you remove the exports and comments at the top. I find it somewhat ironic that you are so opposed to using utility libraries and you didn't even bother to look at the one in play here.

> The beauty is that because we are rolling our own function, we only need to support the single function signature we decide we're going to stick to in our codebase. Supporting every use-case becomes a moot point.

No it doesn't. All the use-cases are useful. You use strings when you need default classes and objects when you have conditional classes. You can't implement both of those in two lines of code.

> The author of the library has to support the 10^5 people who use his code, meaning he's more likely to merge silly pull requests like "should take objects",

It's always taken objects as properties and for good reason: it's a clean way to separate conditional classes from unconditional ones.

But sure dude, all those features are insane. Put his code up to a solid 30 lines. Really saving the bacon by rolling your own that doesn't do the same thing.

> Learning dozens of APIs takes just as much time as implementing them for a certain set of functions. That set is different for everyone, but it is not the null set for anyone.

Yeah, that's why everyone writes their own lodash. It's just a set of trivial functions.


Yeah and if you've analysed 100 sets of docs for 100 dependencies you've done a ton more work than writing 100 trivial functions.

Why does everyone in JS land treat dependencies like they're just some free magic, as if an intimate familiarity with the source code is injected into your entire team's brains the moment you type 'npm install'? I've worked with far too many people that blow out the package.json file for every little thing then wonder how every other dev on their team seems to be paralysed by the smallest task.

Every time someone on your team runs into a dependency boundary they need to stop reading code, pull up a browser, and start reading docs. You can't trace the path of execution through a black box dependency, which means if you use a bunch of them in one place your developers are going to have to hold a hell of a lot of information in their head while they analyse that code. If they drop something, it's back to the docs again. At the extreme, it's a context switching nightmare.

Dependencies cost time, effort and brain power. Make sure you're getting enough in return.


You're absolutely wrong. If reading the docs were harder/more work than writing the code we'd have 100s of implementations of lodash. Instead people use lodash.

Dependency boundary? If you can't figure out what a function called classNames does in the context of a React render function in which the output of classNames is put as a value in to "className" attributes, you need to find a new job.

Dependencies cost time, effort and brain power when they actually require those things. Be thoughtful when using something that actually requires investigation. But honestly, a trivial function should be self-documenting or it really isn't trivial, is it? If I had an npm library called array-flatten are you seriously going to read the docs, or assume it flattens nested arrays?


I never said anything about writing the dependency. I'm talking about reading and writing code that uses it.

Of course it's easier to read a trivial function than the docs for a trivial function. That's what makes it trivial, it's not abstracting away anything meaningful. And of course I can easily figure out what classNames outputs.

You know what I can't figure out trivially? What inputs it takes. It's a polymorphic function. Me and Bob load up a new codebase, I open a few files and see classNames used a couple of times with an object parameter. I can now be pretty confident what the API of the function is. Bob opens up a couple of different files and sees it used with an array parameter a few times. Now he's pretty confident he's got the hang of it too. We both go off to write new components. I've got a bunch of this everywhere:

{ foo: true, bar: true, baz: true }

And Bob has a bunch of this everywhere:

[foo ? 'bar' : null, baz ? 'boop' : null]

Bam, inconsistent code. Could have easily been solved by reading the docs before using something though. And hey, at least we'll both remember for next time...

...Unless our team imports 100 of these libraries and tries to enforce a consistent use of them across the project.

There's only so much API surface area one dev can keep at the front of their mind while coding. Good devs understand this and minimize it. Bad devs pile in trivial dependencies because they don't understand their cost.

Oh, and how do I know whether flattenArray is recursive or not? What if it is and I only want to flatten one level? Does it take a second integer parameter? Or an options object? Since we're talking about JS, does it flatten Array-like objects? Is that the same API or a different one? It's almost like your example proves my point...


> Bam, inconsistent code

Yeah, and in CR you should be told not to use the object form when you have "true" as the value, as it's pointless. That's what string form is for. The "docs" are literally 5 lines where you learn you can send object literals, arrays and strings.

> There's only so much API surface area one dev can keep at the front of their mind while coding. Good devs understand this and minimize it. Bad devs pile in trivial dependencies because they don't understand their cost.

Yes, because if you implement a similar feature the remaining members of your team all know it through osmosis. The surface area of these functions is tiny, but apparently your implementation requires no investigation by anyone.

> Oh, and how do I know whether flattenArray is recursive or not?

Holy cow, man. Do you literally hand-implement EVERY utility function you've ever needed?

> It's almost like your example proves my point...

Your point seems to be: you can't manage to infer ANYTHING from a function signature AND that apparently somehow your team members can ALWAYS infer everything needed from any function YOU write.

Use code inspection and you'll never open the docs for flattenArray or anything like it again. If this is the amount of effort required for flattenArray for you, how to you actually use large libraries with HUGE surface areas?


> Yeah, and in CR you should be told not to use the object form when you have "true" as the value, as it's pointless. That's what string form is for. The "docs" are literally 5 lines where you learn you can send object literals, arrays and strings.

What a dumbass argument. So instead of writing a one line abstraction, I should invest a bunch of CR time explaining to my whole team which of 3 different APIs they should be using in this dependency I've brought in?

> Holy cow, man. Do you literally hand-implement EVERY utility function you've ever needed?

No, just the trivial ones. Have you been actually reading this thread?

> Your point seems to be: you can't manage to infer ANYTHING from a function signature

No, my point is that you can't infer the full API of a function from the arguments passed to it's invokation when the function is polymorphic.

> AND that apparently somehow your team members can ALWAYS infer everything needed from any function YOU write.

If the function is trivial, which is exactly what we've been talking about this whole time, then No. Fucking. Shit. The function is trivial and they have the source code right there.


> So instead of writing a one line abstraction, I should invest a bunch of CR time explaining to my whole team which of 3 different APIs they should be using in this dependency I've brought in?

It's not a one-liner because it doesn't support the same feature set. It supports a basic use case that may be great for trivial apps, but in the real world it isn't.

> No, just the trivial ones. Have you been actually reading this thread?

And said trivial ones must still be "learned" by your co-workers. You've saved exactly zero time.

> No, my point is that you can't infer the full API of a function from the arguments passed to it's invokation when the function is polymorphic.

You can with a glorified text-editor these days with Code Inspection. I guess that's advanced stuff we shouldn't expect our co-workers to use?

> If the function is trivial, which is exactly what we've been talking about this whole time, then No. Fucking. Shit. The function is trivial and they have the source code right there.

Yet, if the function is trivial AND is imported via npm, it suddenly requires reading docs. On one hand you argue "triviality requires reading docs" and then suggest that you can implement a similar trivial function and everyone can just look at the source code.

And you don't see the irony here.


You're conflating a trivial function with a trivial API. The whole reason I jumped into this thread is because classnames is a great example of a trivial function with a non-trivial API. Yes, you do need to look up either the docs or the implementation to understand how to use it. I think my past examples made that clear.

What I'm saying is that it's EASIER to understand the source code than it is to understand the documentation for classnames. That's because it abstracts nothing away. It's replacing one simple API (template strings) with another one that's very similar and of roughly the same power. By turning it into a dependency you're adding an artificial barrier between it and your code base, that makes it harder for everyone to grok. Not much harder, but enough to be a pain in the ass in any codebase where that same tradeoff is made over and over.

If you can't understand that, then I don't know what to do for you. Keep writing shit JS code and making the web worse than it already is.


there is no need to support all use-cases.


There is if you use them all? We're talking about strings, arrays and objects as inputs. ~30 lines of actual code.


    it("should work with booleans", () => {
      expect(classNames({a:true, b: false}).to.equal("a b")
    })
Shouldnt this just equal "a" though? See, already a bug in your homegrown solution!


My test would fail, and I would fix it. + 2 seconds. Actually, the next test has the same problem. 2 more seconds. I'm gonna be in trouble with my boss!


No, your test wouldn't fail because you wrote your code already being confident it is correct and went on. The bug would surface later in your team, somebody else in your team not familiar with your code would spent hours figuring out what is wrong, will consult you about your intentions when you wrote the code etc.


No, the test would fail in this case because the code was written properly because it was a TRIVIAL 2-line function (see above).

A few people in this thread have regurgitated the same reasoning you have. The problem is that your reasoning is NOT an invariant under complexity. This reasoning does not apply nearly as much to trivial utility functions such as:

  const isObj = o => !!o && typeof o === "object"
and classNames, etc. as it does to larger, more complex code.

> will consult you about your intentions when you wrote the code etc.

No, no they won't. No one will consult me on what my 2-line classNames functions does. It's 2 non-obfuscated lines. These aren't undergrads in CS101.


We’ve been doing:

  const classNames = [
    “btn”,
    this.props.isSelected && “btn-selected”
  ].filter(Boolean).join(“ “)


  const arrify = a => a && (Array.isArray(a) ? a : [a])
vs. introducing a potential attack vector.


arrify(null) returns null, unlike the arrify library . I expect that a function which turns things into an array returns an empty array as fallback. This ist just an example for why one would favor well-tested utilities over re-inventing the wheel (not that I would use arrify as a dependency myself).


> This ist just an example for why one would favor well-tested utilities over re-inventing the wheel

No, it really isn't. The library does not dictate how things should be done, that's the fantastic thing about using your own utilities. In my implementation, we return the value itself if it's falsy. In yours, we return an empty array. Again, trivial "fix" (not that there's anything to "fix", it's a matter of convention, not correctness).


Is there a way to see what code in the project actually uses these dependencies? I gave up on learning Typescript for another 6 months after a starter template dragged in 400 dependencies.


Why would that matter for learning typescript..?


If using typescript requires 400 dependencies, it’s a non-starter, and learning it is off the table until your boss forces you to.


A Microsoft template for Vue+Typescript (which I've since learned was unmaintained) pulled in 400 dependencies.


Most low effort way is to search the repository on GitHub for the require.

Also worth noting the ones I listed aren't transitive dependencies. Those are both explicit dependencies of this project




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: