-
Notifications
You must be signed in to change notification settings - Fork 317
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added depth limit #318
base: master
Are you sure you want to change the base?
Added depth limit #318
Conversation
104e0d1
to
77ddee5
Compare
Another thing is about the naming: |
Thanks for the review!
|
88768b8
to
cd0a06e
Compare
Need testcase. I try to add them later. |
Any update on this? 👀 |
I added some test case with copilot on keijokapp#1 I'll try to add the same commit here next week in case of no response. |
17b1ac3
to
dc4f0b0
Compare
I added tests to the PR. |
'e.js': ['f.js'], | ||
'f.js': ['b.js', 'c.js'] | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I apologize for my recent lack of activity. I'm currently sorting out a few details, as I've noticed that the implementation of C, E, F, C
with depth=3
in the test file seems to induce a self-dependency, which isn't accounted for in the final test case (the highlighted one). I need a bit more time to fully understand this, as I initially thought this PR primarily involved output formatting.
Could you @keijokapp please provide a specific use case or example that illustrates the necessity of this functionality? It would greatly help in evaluating its impact and relevance.
Thank you for your understanding and support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might not fully understand the question.
which isn't accounted for in the final test case (the highlighted one)
The output of depth=3
does show c -> e -> f -> c
dependency cycle (?) So do tests with depth=1
and depth=2
. In the context of this feature, there isn't anything special about circular dependencies. They are handled just like any other "back-references" (ie references from deep dependencies to non-deep dependencies). If there are circular dependencies in the output, it means there were circular dependency in the original graph. These tests make sure that they are handled (short-circuited) correctly.
please provide a specific use case or example that illustrates the necessity of this functionality
You mean "circular dependencies"? I don't think I'm the right person to explain the use cases for circular dependencies because I personally have a strong distaste for them. But they are nevertheless common and in rare cases not easily avoidable. This tool specifically handles them eg. by coloring them red on the dot graph. The feature doesn't have anything specifically to do with circular dependencies.
When it comes to "limiting the depth", there are two ways to go about it. One is to stop traversing at depth
and ignore all references originating from that point onward. This is not usually what the user wants. Not showing those "back-references" gives a wrong impression about the dependency structure. For example, at depth=1
, the user might see b.js
and c.js
as fully independent branches while, in fact, b.js
is an indirect dependency of c.js
.
Instead, as noted in the original submission, this feature traverses through the whole tree and does not hide those indirect relationships. It only removes the intermediate deep nodes.
I initially thought this PR primarily involved output formatting.
It really is just about the output. It is to remove the "noise" while keeping the useful relationship information. It allows the user to focus around the close proximity of specific modules. It doesn't change anything else about the tool (eg. module resolution, parsing, traversing).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your detailed response, and I appreciate your patience as I navigate through this PR. After multiple reviews, I'm still grappling with some aspects of the implementation and would like to discuss these further.
-
File Structure and Depth Concerns
My apologies for any confusion caused by theC-D-E-C
example I initially suggested—it may not have been the best illustration of the concept. Here’s a refresher on the file structure from our tests:dependencies = { 'a.js': ['b.js', 'c.js'], 'b.js': [], 'c.js': ['d.js', 'e.js'], 'd.js': [], 'e.js': ['f.js'], 'f.js': ['g.js', 'c.js'], 'g.js': ['b.js'] } a.js ├─ b.js └─ c.js ├─ d.js └─ e.js └─ f.js ├─ g.js │ └─ b.js └─ c.js (circular dependency back to c.js)
This tree structure helps illustrate potential dependency chains, such as
A-C-E-F
is like theA => B => C => D
mentioned in the PR description. When discussingdepth=1
, I find that the outputs might not align with what one would expect, likeA-C-D
in the example in the description, which would beA-E-F
in our test case accordingly. This which should help us better understand the effects of limiting depth. Could we consider revisiting this part to ensure it aligns more clearly with our objectives? -
Circular Dependencies
I generally strive to avoid circular dependencies, too. I found this repository trying to include checks for these circular dependencies in the CI/CD process. Kudos to the contributor for implementing such a valuable feature! -
Clarification on the 'depth' Functionality
Could you possibly provide more concrete examples or use cases for thedepth
functionality? While your explanation was insightful, additional examples would help solidify my understanding of its practical impact. -
Depth Traversal Options
Concerning your point on depth traversal:When it comes to "limiting the depth", there are two ways to go about it. One is to stop traversing at
depth
and ignore all references originating from that point onward. This is not usually what the user wants.I slightly disagree here. In some scenarios, particularly during refactoring, it could be beneficial to understand the breadth of a module's direct influence, which would support limiting traversal as a feature.
-
Confusion Over Terminology
These two terms in the description seem to mean the same thing, leading to some confusion. Could you clarify these definitions?
-
the closest input module ("deep dependencies")
-
non-deep modules (i.e. modules that are <=depth steps away from any input module)
- Implementation Details
I've noticed significant changes in theconvertTree(depTree, tree, pathCache)
method, particularly the removal ofpathCache
. This alteration could potentially lead to increase execution times significantly. I believe reintroducingpathCache
might enhance performance. I'll need some more time to evaluate this change thoroughly.
Thank you once again for your support and understanding as we refine this feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a few nodes for the tree so that we can be on the same page with "deep dependencies"
dependencies = {
'a.js': ['b.js', 'c.js'],
'b.js': ['h.js'],
'c.js': ['d.js', 'e.js'],
'd.js': [],
'e.js': ['f.js'],
'f.js': ['g.js', 'c.js'],
'g.js': ['b.js', 'h.js'],
'h.js': ['i.js'],
'i.js': ['j.js'],
'j.js': ['k.js'],
'k.js': ['l.js'],
'l.js': []
}
a.js
├─ b.js
│ └─ h.js
│ └─ i.js
│ └─ j.js
│ └─ k.js
│ └─ l.js
└─ c.js
├─ d.js
└─ e.js
└─ f.js
├─ g.js
│ ├─ b.js
│ └─ h.js
│ └─ i.js
│ └─ j.js
│ └─ k.js
│ └─ l.js
└─ c.js (circular dependency back to c.js)
For my understanding, with depth=1
, we are excluding file b
, c
. And with depth=2
we are also excluding h
, d
and e
in addition. But here h
would appear again in the chain ACEFGH
which made the output unclear.
(This also shows that removing the pathCache
might be a necessity to implement this feature, depending on what we are trying to achieve.)
@PabloLION I answer in a separate comment because the discussion is not specific to the piece of code.
Absolutely. For programmatic usage, the feature might not be all that useful because it can be achieved on the user land. But for CLI usage (eg generating an image), it's crucial. For any code base of non-trivial size (in my case, it was React Reconciler and React DOM packages but even for much smaller code bases), it's highly impractical to see _all _ modules on the graph. In case of React, there are hundreds, if not thousands of them. The tool can't understand the semantical relationships between these modules so relevant modules that might be more related to each other are often all over the place. This feature is for those use cases. It enables the user to focus on the proximity of specific modules.
Ok. I haven't encountered a use case where I only want to see a direct influence. The IDE tooling has been usually enough if I'm only interested in direct dependencies. If there ever is such use case, I'd expect it to be more specific than this one. So it warrants a new (more specifc) option. I guess the important question is, does seeing those indirect dependencies is a deal breaker for those use cases or not? For my uses cases, not seeing those dependencies is absolutely a non-solution. A feedback from a larger audience would be welcome.
Deep dependencies:
or in other words: modules that are Non-deep dependencies:
It's been a long time since I implemented this so I might not remember all considerations that went into that change. The performance implications were definitely one of them. The change is that path cache is not shared between For purity, I could add the cache sharing back. To be honest, the whole state management is quite messed up due to the pseudo-OOP approach. There's no need for classes here and it's highly confusing. The constructor is called as a regular function (without the benefits of a regular function like
You probably mean "including" those files, not "excluding" them. The lower the |
Thank you @keijokapp for your continued dedication and the insightful new information. On Functionality
The explanation is very comprehensive. I now understand that for large repositories, the functionality you're introducing is incredibly useful. Here’s my takeaway: instead of displaying the entire dependency tree from a selected entry point (or input file path), this feature modifies the output to omit the first
On TerminologyI now realize my initial misunderstanding about your use of "deep dependencies," which are:
On ImplementationI now realize that the implementation is not that simple as I initially anticipated because it calls several other methods from the same class, which functions I need to remember. I will examine these interactions more thoroughly in the coming days. Requesting another opinion@kamiazya @pahen, could you assist in selecting a more suitable name for this flag (option)? Given your extensive experience with this project and expertise in naming conventions, your input would be invaluable in refining this PR.
Additionally, if you have any other insights or suggestions to contribute regarding this feature, I would greatly appreciate hearing them. |
This PR adds
depth
option (and--depth
CLI argument). It can be used to remove all modules from the output that are more thandepth
steps away from the closest input module ("deep dependencies").depth = 0
makes it to only include input modules.Note that the whole tree still gets traversed because deep dependencies could reference back to non-deep modules (i.e. modules that are
<=depth
steps away from any input module). In that case, the intermediate deep dependency is removed and the non-deep dependencies are connected directly. This can cause self-dependent modules.Example:
I haven't written tests yet.