dvdciri / daggraph Goto Github PK
View Code? Open in Web Editor NEWDagger dependency graph generator for Android Developers
License: MIT License
Dagger dependency graph generator for Android Developers
License: MIT License
When a module is providing a dependency, we try to get the dependencies that are needed for constructing that dependency.
When we try to get the DrmInterface we also want to get the Context and DrmFactory and save it under the dependency.
What is happening is that the word "protected" and "DrmInterface" is also saved in the dependencies.
i.e.
@Provides
protected DrmInterface provideDrmInterface(Context context, DrmFactory drmFactory) {
return drmFactory.getDrmInstance(context);
}
The end result is:
{
"name": "DrmInterface",
"dependencies": [
{
"name": "Singleton",
"dependencies": []
},
{
"name": "DrmInterface",
"dependencies": []
},
{
"name": "Context",
"dependencies": []
},
{
"name": "DrmFactory",
"dependencies": []
}
]
}
But should be:
{
"name": "DrmInterface",
"dependencies": [
{
"name": "Context",
"dependencies": []
},
{
"name": "DrmFactory",
"dependencies": []
}
]
}
The link in the README points to dagger 1.
Will this work for both Dagger 1 and Dagger2?
Found the following error on inspect (Chrome browser)
Uncaught SyntaxError: Unexpected token A in JSON at position 68
at JSON.parse (<anonymous>)
at dependency_tree_graph.html:50
dependency_tree_graph.html
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 800 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
const flare = JSON.parse(`{
"name": "Dependencies",
"children": [
{
"name": "I:\\AndroidStudioProjects\\projectname\\app\\build\\generated\\source\\apt\\dev\\debug\\com\\packagename\\di\\module\\ActivityBindingModule_MyFlightsActivity"
},
{
"name": "I:\\AndroidStudioProjects\\projectname\\app\\build\\generated\\source\\apt\\dev\\debug\\com\\packagename\\di\\module\\ActivityBindingModule_OnBoardingActivity"
},
{
"name": "I:\\AndroidStudioProjects\\projectname\\app\\build\\generated\\source\\apt\\dev\\debug\\com\\packagename\\di\\module\\ActivityBindingModule_UserProfileActivity"
},
{
"name": "I:\\AndroidStudioProjects\\projectname\\app\\build\\generated\\source\\apt\\dev\\debug\\com\\packagename\\module\\home\\MyFlightsModule_MyFlightsFragment"
},
{
"name": "I:\\AndroidStudioProjects\\projectname\\app\\build\\generated\\source\\apt\\dev\\debug\\com\\packagename\\module\\onBoarding\\OnBoardingModule_OnboardingFragment"
},
{
"name": "I:\\AndroidStudioProjects\\projectname\\app\\build\\generated\\source\\apt\\dev\\debug\\com\\packagename\\module\\profile\\UserProfileModule_UserProfileFragment"
},
{
"name": "I:\\AndroidStudioProjects\\projectname\\app\\src\\main\\java\\com\\packagename\\di\\components\\AppComponent",
"children": [
{
"name": "AndroidSupportInjectionModule"
}
]
}
]
}`);
root = flare;
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
d3.select(self.frameElement).style("height", "800px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
</script>
At the moment the tool only supports java applications, we need to extend the support for Kotlin applications as well.
Things that needs to be done:
Hello.
First of all, this is a great tool and I really like the concept, but it laks flexibility.
Right now it only works on source code. Which means It cannot detect Modules/Components from other libraries(because accessing their source code is very difficult). Or Providers/injectors generated in aar module. Also you have implement different rules for Kotlin.
Would you consider switching to analyzing apk directly? Here's the tool that does something simular, but it build graph of all the classes. https://github.com/alexzaitsev/apk-dependency-graph/issues
It doesn not work if i provide a path (myProject/
) to my Kotlin project.
The path in error below is incorrect, should be : /Users/mato/myProject/build/dependency_bubble_graph.html.
instead of
/Users/mato/build/dependency_bubble_graph.html.
Martins-MacBook-Pro:~ mato$sudo daggraph myProject/
Analyzing dagger components and modules..
? What kind of chart do you want to generate? Bubble chart
All done! The chart was saved in /Users/mato/build/dependency_bubble_graph.html. Opening..
The file /Users/mato/build/dependency_bubble_graph.html does not exist.
child_process.js:614
throw err;
^
Error: Command failed: open /Users/mato/build/dependency_bubble_graph.html
The file /Users/mato/build/dependency_bubble_graph.html does not exist.
at checkExecSyncError (child_process.js:574:11)
at execSync (child_process.js:611:13)
at /usr/local/lib/node_modules/daggraph/src/router.js:94:5
at fs.js:1294:7
at FSReqWrap.oncomplete (fs.js:149:15)
Trying to run daggraph
exits with an exception.
Node version: v8.9.1
OS: Windows 10
Run daggraph
Graph generation without errors.
$ daggraph
Analyzing dagger components and modules..
TypeError: pending.then is not a function
at FileHound._searchAsync (C:\Users\krire\AppData\Roaming\nvm\v8.9.1\node_modules\daggraph\node_modules\filehound\lib\filehound.js:705:22)
at tryCatcher (C:\Users\krire\AppData\Roaming\nvm\v8.9.1\node_modules\daggraph\node_modules\bluebird\js\release\util.js:16:23)
at MappingPromiseArray._promiseFulfilled (C:\Users\krire\AppData\Roaming\nvm\v8.9.1\node_modules\daggraph\node_modules\bluebird\js\release\map.js:61:38)
at MappingPromiseArray.PromiseArray._iterate (C:\Users\krire\AppData\Roaming\nvm\v8.9.1\node_modules\daggraph\node_modules\bluebird\js\release\promise_array.js:114:31)
at MappingPromiseArray.init (C:\Users\krire\AppData\Roaming\nvm\v8.9.1\node_modules\daggraph\node_modules\bluebird\js\release\promise_array.js:78:10)
at MappingPromiseArray._asyncInit (C:\Users\krire\AppData\Roaming\nvm\v8.9.1\node_modules\daggraph\node_modules\bluebird\js\release\map.js:30:10)
at Async._drainQueue (C:\Users\krire\AppData\Roaming\nvm\v8.9.1\node_modules\daggraph\node_modules\bluebird\js\release\async.js:138:12)
at Async._drainQueues (C:\Users\krire\AppData\Roaming\nvm\v8.9.1\node_modules\daggraph\node_modules\bluebird\js\release\async.js:143:10)
at Immediate.Async.drainQueues (C:\Users\krire\AppData\Roaming\nvm\v8.9.1\node_modules\daggraph\node_modules\bluebird\js\release\async.js:17:14)
at runCallback (timers.js:789:20)
at tryOnImmediate (timers.js:751:5)
at processImmediate [as _immediateCallback] (timers.js:722:5)
I ran daggraph
in my Android project root folder and got:
? What kind of chart do you want to generate? Bubble chart
TypeError: Path must be a string. Received undefined
at assertPath (path.js:28:11)
at Object.join (path.js:1239:7)
at createFileAndSave (/usr/local/lib/node_modules/daggraph/src/router.js:136:28)
at fileDataPromise.then (/usr/local/lib/node_modules/daggraph/src/router.js:64:42)
at <anonymous>
Execute daggraph
Select a chart type
See the chart
Daggraph need to be able to recognise constructor injected dependencies and add them to the graph.
There are some open questions here:
This is the regex for koltin: /(?:class)\s(\w*).*?(?:@Inject).*?(?:constructor)\((.*?)\)\s?\{/gs
This is the regex for java: /(?:@Inject).*?\s(\w*?)\((.*?)\)\s?{/gs
I would like to support another DI framework that also implement the JSR330 : Tootphpick
As the framework use mainly @Inject annotation, eg : the binding is implemented inside module is done by code. I would like to just make a graph from @Inject. How this is possible ? Where to start ?
At present you can click around to navigate the trees, however it would be useful to target specific Modules/Components/dependencies by name with user input.
Export the div
where the graph is as a jpeg/pdf
If a dependency, a module or a component has a scope, we should be able to recognise it and save it under the correct object.
@PerActivity, @singleton, (i.e. Custom scopes can be also specified)
Would be nice to have a dialog that shows the dependencies for each dependency of the bubble chart.
Running daggraph
on a project whose component has an annotation @dagger.Component
(because the interface itself is also called Component
, for example), does not work.
Changing the Java source temporarily to @Component
works as expected.
@dagger.Component public interface Component {}
daggraph
The component should be detected.
If the build folder doesn't exists in the root path, the tool fail to generate any graph
As per title, add the @subcomponent when we look for @component
It would be great to have dagger 2 support
Not sure if this is an environment issue and daggraph depends on a specific version of other software, but I can't run it from a fresh install. The stack-trace follows:
$ daggraph
Analyzing dagger components and modules..
/usr/local/lib/node_modules/daggraph/src/dagger/DaggerAnalyzer.js:109
if (!injectionPathMap[depIdentifier].includes(path)){
^
TypeError: injectionPathMap[depIdentifier].includes is not a function
at FileSniffer.fileSniffer.on (/usr/local/lib/node_modules/daggraph/src/dagger/DaggerAnalyzer.js:109:50)
at emitTwo (events.js:106:13)
at FileSniffer.emit (events.js:194:7)
at LineStream.<anonymous> (/usr/local/lib/node_modules/daggraph/node_modules/filesniffer/lib/filesniffer.js:194:20)
at emitNone (events.js:86:13)
at LineStream.emit (events.js:188:7)
at emitReadable_ (_stream_readable.js:434:10)
at emitReadable (_stream_readable.js:428:7)
at readableAddChunk (_stream_readable.js:189:13)
at LineStream.Readable.push (_stream_readable.js:136:10)
at LineStream.Transform.push (_stream_transform.js:128:32)
at LineStream._pushBuffer (/usr/local/lib/node_modules/daggraph/node_modules/byline/lib/byline.js:125:17)
at LineStream._transform (/usr/local/lib/node_modules/daggraph/node_modules/byline/lib/byline.js:116:8)
at LineStream.Transform._read (_stream_transform.js:167:10)
at LineStream.Transform._write (_stream_transform.js:155:12)
at doWrite (_stream_writable.js:329:12)
at writeOrBuffer (_stream_writable.js:315:5)
1 - Installed daggraph as suggested (npm install -g daggraph)
2 - Run daggraph
Generate graph.
N/A
There is error when run daggraph in Android project folder.
E:<path_of_android_project>>daggraph
C:\Users<DELETED>\AppData\Roaming\npm\node_modules\daggraph\src\dagger\DaggerAnalyzer.js:13
async function findComponents(projectRootPath){
^^^^^^^^
SyntaxError: Unexpected token function
at Object.exports.runInThisContext (vm.js:76:16)
at Module._compile (module.js:542:28)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object. (C:\Users<DELETED>\AppData\Roaming\npm\node_modules\daggraph\src\router.js:7:25)
at Module._compile (module.js:570:32)
Created dependency graph
Think about a possible graphical visualisation of the graph in a html page or something else.
After upgrade to version 0.3.0, running the command produces error...
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string
See full trace in screenshot.
run
npm -g i daggraph
cd <to project folder>
daggraph
Default browser should open tab showing dependency graph
Write some test on the router
If you have something like this, it doesn't pick up the last two dependency of Picasso.
protected Picasso providePicasso(Context context,
AppAvailableFunctions appAvailableFunctions,
OkHttpClientFactory okHttpClientFactory,
@Named("reduced") List<Interceptor> interceptors,
@Named(PICASSO_CACHE) Cache picassoCache) {
This is what it extract:
{
"name": "Picasso",
"dependencies": [
{
"name": "Context",
"dependencies": []
},
{
"name": "AppAvailableFunctions",
"dependencies": []
},
{
"name": "OkHttpClientFactory",
"dependencies": []
},
{
"name": "Name",
"dependencies": []
},
{
"name": "reduce",
"dependencies": []
}
]
}
When I'm using Kotlin-DSL for build script
files, daggraph does not recognize ptoject. and shows this error :
This is not a gradle folder
daggraph .
nothing
nothing
What would the world be without Tests???
Generation of the graph fails
Generate depenency injection graph
Graph generated
In the readme you say this doesn't support "extended modules". Does that mean that anything in SomeOtherModule
will not be included in graph?
@Module(includes = {
SomeOtherModule.class
})
public class MainModule {
Using the tool on a dagger project setup with gradle but with the new Kotlin build scripts ('build.gradle.kts') fails with the message This is not a gradle folder
.
Don't have a public example to share...
Should work for gradle/kotlin as well/
ActivityBuilder Modules are not generated in the graph
@module
public abstract class ActivityBuilder {
@ContributesAndroidInjector(modules = SplashActivityModule.class)
abstract SplashActivity bindSplashActivity();
}
We have to introduce another step after the load of modules and components, which is loading the injections.
Every time we find a @Inject annotation with a dependency we should save it under the dependency which is injected.
Hello
i wanted to try this lib and installed it using npm install -g daggraph without errors. When i try to create the graph i get the following error:
/usr/local/lib/node_modules/daggraph/node_modules/inquirer/lib/prompts/list.js:32 let index = _.findIndex(this.opt.choices.realChoices, ({ value }) => value === def);
SyntaxError: Unexpected token { at exports.runInThisContext (vm.js:53:16) at Module._compile (module.js:387:25) at Object.Module._extensions..js (module.js:422:10) at Module.load (module.js:357:32) at Function.Module._load (module.js:314:12) at Module.require (module.js:367:17) at require (internal/module.js:16:19) at Function.promptModule.restoreDefaultPrompts (/usr/local/lib/node_modules/daggraph/node_modules/inquirer/lib/inquirer.js:55:33) at Object.inquirer.createPromptModule (/usr/local/lib/node_modules/daggraph/node_modules/inquirer/lib/inquirer.js:65:16) at Object.<anonymous> (/usr/local/lib/node_modules/daggraph/node_modules/inquirer/lib/inquirer.js:77:28)
It would be good to be able to export the raw graph as json
Duplicated components in graphs (all of them). Seems that the modules get duplicated and appear several times.
Just run daggraph in my project. Is a multi-module project (4 in total). In each of the modules, there are Dagger DI related files.
Component shows only once.
While running daggraph
in the top level directory of Android project I'm getting:
/usr/local/lib/node_modules/daggraph/src/dagger/DaggerAnalyzer.js:13
async function findComponents(projectRootPath){
^^^^^^^^
SyntaxError: Unexpected token function
at createScript (vm.js:56:10)
at Object.runInThisContext (vm.js:97:10)
at Module._compile (module.js:542:28)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (/usr/local/lib/node_modules/daggraph/src/router.js:7:25)
Output of npm --version
is 3.5.2
. Are there any further information I can provide to help you with reproducing the issue?
I've made a Docker image to use daggraph without need of npm or any installation (exept Docker of course).
But it failed with this error:
sync function findComponents(projectRootPath){
^^^^^^^^
SyntaxError: Unexpected token function
at createScript (vm.js:56:10)
at Object.runInThisContext (vm.js:97:10)
at Module._compile (module.js:542:28)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (/usr/local/lib/node_modules/daggraph/src/router.js:7:25)
Here is the command I execute in the root folder of my project:
docker run --rm -v $(pwd):/source olivierperez/daggraph daggraph app/src/
Basically it will execute daggraph app/src/
on my project.
I don't use the right folder (in my example: app/src/
)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.