{"id":13550796,"url":"https://github.com/OlegAlexander/lakos","last_synced_at":"2025-04-03T00:34:57.337Z","repository":{"id":39760031,"uuid":"255132238","full_name":"OlegAlexander/lakos","owner":"OlegAlexander","description":"Visualize Dart/Flutter library dependencies in Graphviz dot. Detect dependency cycles.","archived":false,"fork":false,"pushed_at":"2024-12-26T21:40:04.000Z","size":185,"stargazers_count":91,"open_issues_count":0,"forks_count":8,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-31T13:16:26.358Z","etag":null,"topics":["code-metrics","command-line-tool","dart","dartlang","flutter","graphviz","modular-programming","software-visualization"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/lakos","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OlegAlexander.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-12T17:14:22.000Z","updated_at":"2025-03-27T12:59:44.000Z","dependencies_parsed_at":"2024-01-16T18:58:14.935Z","dependency_job_id":"14a784bb-afde-4aaa-abe8-adcc7df9bac7","html_url":"https://github.com/OlegAlexander/lakos","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OlegAlexander%2Flakos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OlegAlexander%2Flakos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OlegAlexander%2Flakos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OlegAlexander%2Flakos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OlegAlexander","download_url":"https://codeload.github.com/OlegAlexander/lakos/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246916735,"owners_count":20854511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-metrics","command-line-tool","dart","dartlang","flutter","graphviz","modular-programming","software-visualization"],"created_at":"2024-08-01T12:01:37.713Z","updated_at":"2025-04-03T00:34:52.326Z","avatar_url":"https://github.com/OlegAlexander.png","language":"Dart","readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/42989765/94976346-8c142480-04c9-11eb-920f-0d3412f1f042.png\" alt=\"Example dependency graph\" width=\"70%\"/\u003e\u003c/p\u003e\n\n`lakos` is a command line tool and library that can:\n\n- Visualize internal Dart library dependencies in Graphviz `dot`.\n- Detect dependency cycles.\n- Identify orphans.\n- Compute useful dependency graph metrics.\n\n# Command Line Usage\n\n```console\nUsage: lakos [options] \u003croot-directory\u003e\n\n-f, --format=\u003cFORMAT\u003e        Output format.\n                             [dot (default), json]\n\n-o, --output=\u003cFILE\u003e          Save output to a file instead of printing it.\n                             (defaults to \"STDOUT\")\n\n    --[no-]tree              Show directory structure as subgraphs.\n                             (defaults to on)\n\n-m, --[no-]metrics           Compute and show global metrics.\n                             (defaults to --no-metrics)\n\n    --[no-]node-metrics      Show node metrics. Only works when --metrics is true.\n                             (defaults to --no-node-metrics)\n\n-i, --ignore=\u003cGLOB\u003e          Exclude files and directories with a glob pattern.\n                             (defaults to \"!**\")\n\n    --[no-]cycles-allowed    With --no-cycles-allowed lakos runs normally\n                             but exits with a non-zero exit code\n                             if a dependency cycle is detected.\n                             Useful for CI builds.\n                             (defaults to --no-cycles-allowed)\n```\n\n## Examples\n\nPrint dot graph for the current directory (not very useful):\n\n```console\nlakos .\n```\n\nPass output directly to Graphviz `dot` in one line (much more useful):\n\n```console\nlakos . | dot -Tpng -Gdpi=200 -o example.png\n```\n\nSave output to a dot file first and then generate an SVG using Graphviz `dot`:\n\n```console\nlakos -o example.dot .\ndot -Tsvg example.dot -o example.svg\n```\n\n## Notes\n\n- Nodes are internal Dart libraries. Edges are \"uses\" or \"depends on\" relationships.\n- Exports are drawn with a dashed edge.\n- Orphan nodes are octagons (with `--metrics`).\n- Only `import` and `export` directives are supported; `library` and `part` are not.\n- Only libraries under the root-directory are shown; Dart core and external packages are not.\n\n## More Examples\n\n`lakos` run on itself with metrics, ignoring tests.\n\n```console\nlakos -o dot_images/lakos.metrics_no_test.dot -m -i test/** .\n```\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/42989765/94977052-eada9d80-04cb-11eb-98fe-9e27886923df.png\" alt=\"Lakos run on itself with metrics, ignoring tests.\" width=\"70%\"/\u003e\u003c/p\u003e\n\nShow node metrics.\n\n```console\nlakos -o dot_images/args.no_test_node_metrics.dot -m -i test/** --node-metrics /root/.pub-cache/hosted/pub.dartlang.org/args-1.6.0\n```\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/42989765/94977108-2c6b4880-04cc-11eb-9039-433a7390d42d.png\" alt=\"Show node metrics.\" width=\"90%\"/\u003e\u003c/p\u003e\n\nNo directory tree.\n\n```console\nlakos --no-tree -o dot_images/string_scanner.no_test_no_tree.dot -i test/** /root/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.0.5\n```\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/42989765/94977170-72281100-04cc-11eb-9a92-ae932f368ca8.png\" alt=\"No directory tree.\" width=\"70%\"/\u003e\u003c/p\u003e\n\nExample JSON output:\n\n```console\nlakos -f json -o dot_images/pub_cache.metrics_no_test.json -m -i test/** /root/.pub-cache/hosted/pub.dartlang.org/pub_cache-0.2.3\n```\n\n\u003cdetails\u003e \u003csummary\u003eClick to show the JSON output.\u003c/summary\u003e\n\n```json\n{\n  \"rootDir\": \"/root/.pub-cache/hosted/pub.dartlang.org/pub_cache-0.2.3\",\n  \"nodes\": {\n    \"/example/list.dart\": {\n      \"id\": \"/example/list.dart\",\n      \"label\": \"list\",\n      \"cd\": 3,\n      \"inDegree\": 0,\n      \"outDegree\": 1,\n      \"instability\": 1.0,\n      \"sloc\": 14\n    },\n    \"/lib/pub_cache.dart\": {\n      \"id\": \"/lib/pub_cache.dart\",\n      \"label\": \"pub_cache\",\n      \"cd\": 2,\n      \"inDegree\": 2,\n      \"outDegree\": 1,\n      \"instability\": 0.33,\n      \"sloc\": 162\n    },\n    \"/lib/src/impl.dart\": {\n      \"id\": \"/lib/src/impl.dart\",\n      \"label\": \"impl\",\n      \"cd\": 2,\n      \"inDegree\": 1,\n      \"outDegree\": 1,\n      \"instability\": 0.5,\n      \"sloc\": 95\n    }\n  },\n  \"subgraphs\": [\n    {\n      \"id\": \"\",\n      \"label\": \"pub_cache-0.2.3\",\n      \"nodes\": [],\n      \"subgraphs\": [\n        {\n          \"id\": \"/example\",\n          \"label\": \"example\",\n          \"nodes\": [\"/example/list.dart\"],\n          \"subgraphs\": []\n        },\n        {\n          \"id\": \"/lib\",\n          \"label\": \"lib\",\n          \"nodes\": [\"/lib/pub_cache.dart\"],\n          \"subgraphs\": [\n            {\n              \"id\": \"/lib/src\",\n              \"label\": \"src\",\n              \"nodes\": [\"/lib/src/impl.dart\"],\n              \"subgraphs\": []\n            }\n          ]\n        }\n      ]\n    }\n  ],\n  \"edges\": [\n    {\n      \"from\": \"/example/list.dart\",\n      \"to\": \"/lib/pub_cache.dart\",\n      \"directive\": \"import\"\n    },\n    {\n      \"from\": \"/lib/pub_cache.dart\",\n      \"to\": \"/lib/src/impl.dart\",\n      \"directive\": \"import\"\n    },\n    {\n      \"from\": \"/lib/src/impl.dart\",\n      \"to\": \"/lib/pub_cache.dart\",\n      \"directive\": \"import\"\n    }\n  ],\n  \"metrics\": {\n    \"isAcyclic\": false,\n    \"firstCycle\": [\"/lib/pub_cache.dart\", \"/lib/src/impl.dart\", \"/lib/pub_cache.dart\"],\n    \"numNodes\": 3,\n    \"numEdges\": 3,\n    \"avgDegree\": 1.0,\n    \"orphans\": [],\n    \"ccd\": 7,\n    \"acd\": 2.33,\n    \"nccd\": 1.4,\n    \"totalSloc\": 271,\n    \"avgSloc\": 90.33\n  }\n}\n```\n\n\u003c/details\u003e\n\n## Ignoring Multiple Directories\n\nUse curly brackets in the glob pattern to ignore multiple folders:\n\n```console\nlakos -i \"{lib/extensions/**,test/**}\" .\n```\n\n## Styling Graphviz\n\nYou may override the default style attributes directly through the Graphviz `dot` command line arguments. In Graphviz, the style attributes are divided into 3 sections: `graph`, `node`, and `edge`. To override a specific attribute, use the `-G`, `-N`, and `-E` prefix, respectively. For example, `-Nfillcolor=white` will set the node fill color to white. All the Graphviz style attributes can be found [here](https://graphviz.org/doc/info/attrs.html).\n\nExample of left to right layout.\n\n```console\ndot -Tpng dot_images/test.lr.dot -Grankdir=LR -Gdpi=200 -o dot_images/test.lr.png\n```\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/42989765/94977365-3f324d00-04cd-11eb-8e05-89b58da653f7.png\" alt=\"Left to right layout.\" width=\"80%\"/\u003e\u003c/p\u003e\n\nGradient node color.\n\n```console\ndot -Tpng dot_images/string_scanner.gradient.dot -Gdpi=200 -Nfillcolor=steelblue2:steelblue4 -Nfontcolor=white -Ngradientangle=270 -o dot_images/string_scanner.gradient.png\n```\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/42989765/94977486-bb2c9500-04cd-11eb-80b9-5f23401cd2a4.png\" alt=\"Gradient node color.\" width=\"60%\"/\u003e\u003c/p\u003e\n\n## Convert to GML\n\nStill can't get enough graph visualization goodness? Try this!\n\n```console\nlakos -i test/** /root/.pub-cache/hosted/pub.dartlang.org/test-1.15.3 | gv2gml -o dot_images/test.gml\n```\n\n`gv2gml` converts dot format into [GML format](https://en.wikipedia.org/wiki/Graph_Modelling_Language), which you can open in yEd, Gephi, Cytoscape, etc.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/42989765/93006268-f3fcce00-f50e-11ea-9f96-e6ce9c8cffbb.png\" alt=\"GML file imported into yEd.\" width=\"100%\"/\u003e\u003c/p\u003e\n\n## Global Metrics\n\nUse `--metrics` to compute and show these.\n\n**isAcyclic:** True if the library dependencies form a directed acyclic graph (DAG). False if the graph contains dependency cycles. True is better.\n\n**firstCycle:** The first dependency cycle found if the graph is not acyclic. Available in the JSON output.\n\n**numNodes:** Number of Dart libraries (nodes) in the graph.\n\n**numEdges:** Number of edges (dependencies) in the graph.\n\n**avgDegree:** The average number of incoming/outgoing edges per node. Average degree (directed graph) = numEdges / numNodes. This metric can compare graphs of different sizes. Similar to the ACD, the average degree can be interpreted as the average number of nodes that _will_ need to change when one node changes. Lower is better.\n\n**numOrphans:** Number of Dart libraries that have no imports and are imported nowhere. (inDegree and outDegree are 0.)\n\n**ccd:** The Cumulative Component Dependency (CCD) is the sum of all Component Dependencies. The CCD can be interpreted as the total \"coupling\" of the graph. Lower is better.\n\n**acd:** The Average Component Dependency (ACD). ACD = CCD / numNodes. Similar to the avgDegree, the ACD can be interpreted as the average number of nodes that _may_ need to change when one node changes. Lower is better.\n\n**nccd:** The Normalized Cumulative Component Dependency. This is the CCD divided by a CCD of a binary tree of the same size. This metric can compare graphs of different sizes. If the NCCD is below 1.0, the graph is \"horizontal\". If the NCCD is above 1.0, the graph is \"vertical\". If the NCCD is above 2.0, the graph probably contains cycles. Lower is better.\n\n**totalSloc:** Total Source Lines of Code for all nodes.\n\n**avgSloc:** The average SLOC per node. The average SLOC should arguably be kept within some Goldilocks range: not too big and not too small.\n\n## Node Metrics\n\nUse `--metrics --node-metrics` to show these.\n\n**cd:** The Component Dependency (CD) is the number of nodes a particular node depends on directly or transitively, including itself.\n\n**inDegree:** The number of nodes that depend on this node. (Number of incoming edges.)\n\n**outDegree:** The number of nodes this node depends on. (Number of outgoing edges.)\n\n**instability:** A node metric by Robert C. Martin related to the Stable-Dependencies Principle: Depend in the direction of stability. Instability = outDegree / (inDegree + outDegree). This yields a value from 0 to 1, where 0 means 100% stable and 1 means 100% unstable. In general, node instability should decrease in the direction of dependency. In other words, lower level nodes should be more stable and more reusable than higher level nodes.\n\n**sloc:** Source Lines of Code is the number of lines of code ignoring comments and blank lines. Note: my SLOC counter function doesn't deal correctly with comments inside multi-line strings or multi-line comments in the middle of code.\n\n## Exit Codes\n\nUse these in your CI pipeline, especially DependencyCycleDetected.\n\n\u003col start=\"0\"\u003e\n  \u003cli\u003eOk\u003c/li\u003e\n  \u003cli\u003eInvalidOption\u003c/li\u003e\n  \u003cli\u003eNoRootDirectorySpecified\u003c/li\u003e\n  \u003cli\u003eBuildModelFailed\u003c/li\u003e\n  \u003cli\u003eWriteToFileFailed\u003c/li\u003e\n  \u003cli\u003eDependencyCycleDetected\u003c/li\u003e\n\u003c/ol\u003e\n\n# Library Usage\n\nIn addition to the command line tool, `lakos` can also be used as a library.\n\nUse `buildModel` to construct a `Model` object.\n\nUse `Model.getOutput` to print the model in dot or JSON format.\n\nUse `Model.toDirectedGraph` for further analysis with the `directed_graph` library.\n\nSee [example/example.dart](https://github.com/OlegAlexander/lakos/blob/master/example/example.dart).\n\n# Inspiration\n\n`lakos` is named after John Lakos, author of [_Large-Scale C++ Software Design_](https://www.amazon.com/Large-Scale-Software-Design-John-Lakos/dp/0201633620). This book presents:\n\n- A graph-theoretic argument for keeping module dependencies acyclic.\n- A set of \"levelization techniques\", such as Escalation and Demotion, to help avoid cyclic dependencies.\n- A set of coupling metrics, such as the CCD, ACD, and the NCCD.\n- An emphasis on physical design (files, folders) working in harmony with logical design (functions, classes).\n\n# Similar Tools\n\n- Dart: pubviz (for packages)\n- C/C++: cinclude2dot\n- JavaScript/TypeScript: madge, dependency-cruiser\n- C#: NDepend\n- Java: Sonargraph, JArchitect\n- Python: pydeps\n- Haskell: graphmod\n- Swift: SwiftAlyzer\n","funding_links":[],"categories":["Dart"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOlegAlexander%2Flakos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FOlegAlexander%2Flakos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOlegAlexander%2Flakos/lists"}