GithubHelp home page GithubHelp logo

dvdciri / daggraph Goto Github PK

View Code? Open in Web Editor NEW
1.1K 30.0 58.0 2.56 MB

Dagger dependency graph generator for Android Developers

License: MIT License

JavaScript 55.22% HTML 16.40% Java 21.41% Kotlin 6.96%
dagger2 graph generate dependency-injection component dagger module

daggraph's People

Contributors

cesarferreira avatar dvdciri avatar mbasso avatar wiyarmir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

daggraph's Issues

Wrong dependencies for a provided dependency

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": []
              }
            ]
          }

Unable to see graph on browser

Description

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>

Steps to reproduce

Expected behaviour

Screenshot

Support Kotlin

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:

  • Update the extension when looking for the files
  • Update all the regex and make sure they all support java and kotlin at the same time

Possibility of switching to bytecode/apk analyzing instead of source?

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

Does not work when specifying path to the project root

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)

Daggraph fails with TypeError

Description

Trying to run daggraph exits with an exception.
Node version: v8.9.1
OS: Windows 10

Steps to reproduce

Run daggraph

Expected behaviour

Graph generation without errors.

Log

$ 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)

TypeError: Path must be a string. Received undefined

Description

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>

Steps to reproduce

Execute daggraph
Select a chart type

Expected behaviour

See the chart

Screenshot

Add constructor injected dependencies to the graph

Daggraph need to be able to recognise constructor injected dependencies and add them to the graph.

There are some open questions here:

  • Where should we put the class which is constructor injecting some dependencies?
  • Where should we put the injected dependencies?

This is the regex for koltin: /(?:class)\s(\w*).*?(?:@Inject).*?(?:constructor)\((.*?)\)\s?\{/gs
This is the regex for java: /(?:@Inject).*?\s(\w*?)\((.*?)\)\s?{/gs

Support other DI framework

Description

I would like to support another DI framework that also implement the JSR330 : Tootphpick

Steps to reproduce

Expected behaviour

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 ?

Screenshot

Searchable interface

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.

Doesn't support fully qualified @Component reference

Description

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.

Steps to reproduce

  1. Create a Dagger1 project
  2. Create a file: @dagger.Component public interface Component {}
  3. Run daggraph
  4. See "Couldn't find any components, are you sure this project is using Dagger?"

Expected behaviour

The component should be detected.

TypeError on DaggerAnalyzer - includes is not a function

Description

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)

Steps to reproduce

1 - Installed daggraph as suggested (npm install -g daggraph)
2 - Run daggraph

Expected behaviour

Generate graph.

Screenshot

N/A

Error "SyntaxError: Unexpected token function"

Description

There is error when run daggraph in Android project folder.

Steps to reproduce

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)

Expected behaviour

Created dependency graph

Screenshot

TypeError [ERR_INVALID_ARG_TYPE] After upgrade to 0.3.0

Description

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.

Steps to reproduce

run

npm -g i daggraph
cd <to project folder>
daggraph

Expected behaviour

Default browser should open tab showing dependency graph

Screenshot

screen shot 2018-04-01 at 12 09 51 copy

Annotation in provide method

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": []
              }
            ]
          }

incompatible with Kotlin-DSL for gradle

Description

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

Steps to reproduce

  • Go to root of project
  • daggraph .

Expected behaviour

nothing

Screenshot

nothing

Support old node versions

At the moment daggraph works fine only with the latest versions of node, we need to support also old versions using a tool like babel.

Here is a list of things that needs to be done:

  • Setup babel compiler
  • Convert to ES6 module syntax

Add tests

What would the world be without Tests???

Clarification: "Extended modules"

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 {

'This is not a gradle folder' on a gradle setup with kotlin build files

Description

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.

Steps to reproduce

Don't have a public example to share...

Expected behaviour

Should work for gradle/kotlin as well/

ActivityBuilder Modules are not generated in the graph

Description

ActivityBuilder Modules are not generated in the graph

Example of ActivityBuilderModule

@module
public abstract class ActivityBuilder {
@ContributesAndroidInjector(modules = SplashActivityModule.class)
abstract SplashActivity bindSplashActivity();
}

Load @Inject annotations

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.

SyntaxError: Unexpected token

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)

Duplicated components in dependency graph

Description

Duplicated components in graphs (all of them). Seems that the modules get duplicated and appear several times.

Steps to reproduce

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.

Expected behavior

Component shows only once.

Screenshot

image

SyntaxError: Unexpected token function

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?

SyntaxError: Unexpected token function

Description

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)

Steps to reproduce

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.

Assumption

I don't use the right folder (in my example: app/src/)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.