├── .eslintrc ├── .github └── dependabot.yml ├── .gitignore ├── .vscode └── launch.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── TODO.md ├── images ├── cmd.gif └── logo.png ├── package-lock.json ├── package.json ├── src ├── commands │ ├── analyse-file.js │ └── analyse-project.js ├── complexity-analyzer.js ├── config.js ├── controller.js ├── extension.js ├── html-report-provider.js ├── models │ ├── file-analysis.js │ └── project-analysis.js ├── navigator.js ├── report-factory.js ├── report │ ├── classes-table.js │ ├── file-report.js │ ├── files-table.js │ ├── functions-table.js │ ├── header.js │ ├── html-builder.js │ ├── icons.js │ ├── link.js │ ├── metric-box.js │ ├── metric-formatter.js │ ├── metric-row.js │ ├── project-report.js │ ├── report-style.js │ └── table.js └── utils │ └── workspace.js ├── tsconfig.json └── typings ├── node.d.ts └── vscode-typings.d.ts /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module" 5 | }, 6 | 7 | "env": { 8 | "browser": false, 9 | "amd": true, 10 | "node": true, 11 | "es6": true 12 | }, 13 | 14 | "globals": { 15 | "define": false, 16 | "require": true, 17 | 18 | "alert": true, 19 | "console": true, 20 | "module": true, 21 | 22 | "describe": true, 23 | "it": true, 24 | "before": true, 25 | "beforeEach": true, 26 | "after": true, 27 | "afterEach": true, 28 | "expect": true, 29 | "should": true, 30 | 31 | "Promise": true // allow possibly overwriting Promise 32 | }, 33 | 34 | "rules": { 35 | "dot-notation": 0, // allow obj['somePropertyName'] 36 | "new-cap": 0, // do not require 'new' keyword, allows i.e. "var promise = $.Deferred()" 37 | "no-alert": 0, // disencourage alert() and confirm() 38 | "no-console": 0, 39 | "no-mixed-spaces-and-tabs": 1, 40 | "semi-spacing": [2, {"before": false, "after": true}], 41 | "no-spaced-func": 1, 42 | "no-undef": 1, 43 | "no-shadow": 1, 44 | "no-trailing-spaces": 1, 45 | "no-extra-parens": 1, 46 | "no-underscore-dangle": 0, // allow _variableName 47 | "no-new": 0, 48 | "no-nested-ternary": 1, 49 | "no-process-exit": 0, 50 | 51 | // requires local variable names to be used, but allows unused arguments 52 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 53 | "no-use-before-define": 0, 54 | 55 | // disable requirements for strict spacing rules in object properties or assignments 56 | "key-spacing": 0, 57 | "no-multi-spaces": 0, 58 | "keyword-spacing": 2, 59 | 60 | "quotes": 0, // allow both single and double quotes 61 | "space-infix-ops": 1, 62 | "strict": 0 63 | } 64 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | out -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 11 | "stopOnEntry": false 12 | }, 13 | { 14 | "name": "Launch Tests", 15 | "type": "extensionHost", 16 | "request": "launch", 17 | "runtimeExecutable": "${execPath}", 18 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/test" ], 19 | "stopOnEntry": false 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | typings/** 3 | test/** 4 | .gitignore 5 | jsconfig.json 6 | vsc-extension-quickstart.md 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.0.0] - 2017-07-23 4 | ### Added 5 | - Analyse class methods 6 | ### Changed 7 | - `jsconfig.json` is now used for includes and excludes if nothing else is configured 8 | - Configured include and exclude patterns need to be an array 9 | ### Fixed 10 | - Full support for ES6 and newer JS features 11 | - Include and exclude patterns not working 12 | 13 | ## [1.0.4] - 2016-09-24 14 | ### Fixed 15 | - Fixed "invalid path" error on file complexity analysis command 16 | 17 | ## [1.0.3] - 2016-09-24 18 | ### Fixed 19 | - Fixed parsing of JSX syntax 20 | - Fixed parsing of ES6 modules 21 | - Fixed navigation between report pages 22 | 23 | ## [1.0.2] - 2016-08-26 24 | ### Fixed 25 | - Fixed handling of include and exclude settings 26 | 27 | ## [1.0.1] - 2016-07-09 28 | ### Fixed 29 | - Fixed "Cannot read property 'document' of undefined" error 30 | - Fixed report link styles 31 | 32 | ## [1.0.0] - 2016-05-17 33 | ### Changed 34 | - HTML output for the report with navigation between files 35 | 36 | ## [0.2.2] - 2016-02-23 37 | ### Fixed 38 | - Fixed bug in "Project complexity analysis" that caused incorrect paths on Windows machines 39 | 40 | ## [0.2.1] - 2016-02-10 41 | ### Fixed 42 | - Fixed bug in "Project complexity analysis" error handling 43 | 44 | ## [0.2.0] - 2016-02-10 45 | ### Added 46 | - "Project complexity analysis" command 47 | 48 | ### Changed 49 | - "Complexity analysis" command is now titled "File complexity analysis" 50 | - Calculated metrics are now rounded to two decimals. 51 | - Legend is now formatted as a table 52 | 53 | ## 0.1.0 - 2016-02-01 54 | ### Added 55 | - Complexity analysis command 56 | - Option to configure calculated metrics per function 57 | 58 | [Unreleased]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v2.0.0...HEAD 59 | [2.0.0]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v1.0.4...v2.0.0 60 | [1.0.4]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v1.0.3...v1.0.4 61 | [1.0.3]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v1.0.2...v1.0.3 62 | [1.0.2]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v1.0.1...v1.0.2 63 | [1.0.1]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v1.0.1...v1.0.0 64 | [1.0.0]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v1.0.0...v0.2.2 65 | [0.2.2]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v0.2.1...v0.2.2 66 | [0.2.1]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v0.2.0...v0.2.1 67 | [0.2.0]: https://github.com/tomi/vscode-js-complexity-analysis/compare/v0.1.0...v0.2.0 68 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tomi Turtiainen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## JavaScript Complexity Analysis for Visual Studio Code 2 | 3 | Uses [ESComplex] to produce a complexity analysis report for a JavaScript project or file. The following [metrics] can calculated: 4 | 5 | * Lines of code 6 | * Number of parameters 7 | * Cyclomatic complexity 8 | * Halstead metrics 9 | * Maintainability 10 | 11 | 12 | ## Installation 13 | 14 | * Install the latest Visual Studio Code. 15 | * In the command palette (`Ctrl-Shift-P` or `Cmd-Shift-P` or `F1`) select `Install Extension` and choose `JS Complexity Analysis Report`. 16 | 17 | 18 | ## Usage 19 | 20 | This extenson uses [typhonjs-escomplex](https://github.com/typhonjs-node-escomplex/typhonjs-escomplex) to analyse source files. Currently it utilizes babylon w/ all plugins enabled to analyse source code, so it should support most JS syntax. 21 | 22 | ### Project analysis 23 | 24 | ![GIF](images/cmd.gif) 25 | 26 | Produces a per function complexity analysis report of all `.js` files in the project. Open command palette `F1` and search for `Project complexity analysis`. 27 | 28 | By default uses project's `jsconfig.json` configuration for including and excluding files, but files can also be configured using include and exclude glob patterns. Select `Code` --> `Preferences` --> `User Settings` or `Workspace Settings`. For example: 29 | 30 | ```javascript 31 | "complexityAnalysis.exclude": [ 32 | "**/bower_components/**" 33 | ], 34 | 35 | "complexityAnalysis.include": [ 36 | "**/app/**/*.js" 37 | ] 38 | ``` 39 | 40 | ### File analysis 41 | 42 | Produces a per function complexity analysis report of currently open file. Open command palette `F1` and search for `File complexity analysis`. 43 | 44 | 45 | ## Change Log 46 | 47 | [View](https://github.com/tomi/vscode-js-complexity-analysis/blob/master/CHANGELOG.md) 48 | 49 | 50 | ## Bugs 51 | 52 | Report them [here](https://github.com/tomi/vscode-js-complexity-analysis/issues). 53 | 54 | 55 | ## Licence 56 | 57 | [MIT](https://github.com/tomi/vscode-js-complexity-analysis) 58 | 59 | [typhonjs-escomplex]: https://github.com/typhonjs-node-escomplex/typhonjs-escomplex 60 | 61 | ## Acknowledgements 62 | 63 | This project is a grateful recipient of the [Futurice Open Source sponsorship program](http://futurice.com/blog/sponsoring-free-time-open-source-activities). ♥ 64 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | * Loading page 2 | * Links to functions on the line -------------------------------------------------------------------------------- /images/cmd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomi/vscode-js-complexity-analysis/5a89c9f604b63d5e090fefa56682149b2ed51e80/images/cmd.gif -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomi/vscode-js-complexity-analysis/5a89c9f604b63d5e090fefa56682149b2ed51e80/images/logo.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-complexity-analysis", 3 | "version": "2.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/parser": { 8 | "version": "7.18.4", 9 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz", 10 | "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==" 11 | }, 12 | "@eslint/eslintrc": { 13 | "version": "1.3.0", 14 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", 15 | "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", 16 | "dev": true, 17 | "requires": { 18 | "ajv": "^6.12.4", 19 | "debug": "^4.3.2", 20 | "espree": "^9.3.2", 21 | "globals": "^13.15.0", 22 | "ignore": "^5.2.0", 23 | "import-fresh": "^3.2.1", 24 | "js-yaml": "^4.1.0", 25 | "minimatch": "^3.1.2", 26 | "strip-json-comments": "^3.1.1" 27 | } 28 | }, 29 | "@humanwhocodes/config-array": { 30 | "version": "0.9.5", 31 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", 32 | "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", 33 | "dev": true, 34 | "requires": { 35 | "@humanwhocodes/object-schema": "^1.2.1", 36 | "debug": "^4.1.1", 37 | "minimatch": "^3.0.4" 38 | } 39 | }, 40 | "@humanwhocodes/object-schema": { 41 | "version": "1.2.1", 42 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", 43 | "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", 44 | "dev": true 45 | }, 46 | "@typhonjs/babel-parser": { 47 | "version": "0.2.0", 48 | "resolved": "https://registry.npmjs.org/@typhonjs/babel-parser/-/babel-parser-0.2.0.tgz", 49 | "integrity": "sha512-YKqLZaQAVtOjMiqcJIqex1ezduMefBitoQZjsOqr4US+Yq+cOY/obyloOJ7Ee+XDPaaraVrxWkA3VZjOohtVjQ==", 50 | "requires": { 51 | "@babel/parser": "^7.0.0", 52 | "babel-runtime": "^6.0.0" 53 | } 54 | }, 55 | "acorn": { 56 | "version": "8.7.1", 57 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", 58 | "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", 59 | "dev": true 60 | }, 61 | "acorn-jsx": { 62 | "version": "5.3.2", 63 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 64 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 65 | "dev": true 66 | }, 67 | "ajv": { 68 | "version": "6.12.6", 69 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 70 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 71 | "dev": true, 72 | "requires": { 73 | "fast-deep-equal": "^3.1.1", 74 | "fast-json-stable-stringify": "^2.0.0", 75 | "json-schema-traverse": "^0.4.1", 76 | "uri-js": "^4.2.2" 77 | } 78 | }, 79 | "argparse": { 80 | "version": "2.0.1", 81 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 82 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 83 | "dev": true 84 | }, 85 | "babel-runtime": { 86 | "version": "6.26.0", 87 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 88 | "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", 89 | "requires": { 90 | "core-js": "^2.4.0", 91 | "regenerator-runtime": "^0.11.0" 92 | } 93 | }, 94 | "backbone-esnext-events": { 95 | "version": "0.3.5", 96 | "resolved": "https://registry.npmjs.org/backbone-esnext-events/-/backbone-esnext-events-0.3.5.tgz", 97 | "integrity": "sha512-n208qnhO6kARjSHLZIBs1w6ECpybIBzFE6X1x2XTIGx+U+6qlSctizq9bCh+Xf5XHsSyipIgoxc1grZG7nCxfw==", 98 | "requires": { 99 | "babel-runtime": "^6.0.0" 100 | } 101 | }, 102 | "balanced-match": { 103 | "version": "1.0.2", 104 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 105 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 106 | "dev": true 107 | }, 108 | "brace-expansion": { 109 | "version": "1.1.11", 110 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 111 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 112 | "dev": true, 113 | "requires": { 114 | "balanced-match": "^1.0.0", 115 | "concat-map": "0.0.1" 116 | } 117 | }, 118 | "callsites": { 119 | "version": "3.1.0", 120 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 121 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 122 | "dev": true 123 | }, 124 | "chalk": { 125 | "version": "4.1.2", 126 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 127 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 128 | "dev": true, 129 | "requires": { 130 | "ansi-styles": "^4.1.0", 131 | "supports-color": "^7.1.0" 132 | }, 133 | "dependencies": { 134 | "ansi-styles": { 135 | "version": "4.3.0", 136 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 137 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 138 | "dev": true, 139 | "requires": { 140 | "color-convert": "^2.0.1" 141 | } 142 | }, 143 | "supports-color": { 144 | "version": "7.2.0", 145 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 146 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 147 | "dev": true, 148 | "requires": { 149 | "has-flag": "^4.0.0" 150 | } 151 | } 152 | } 153 | }, 154 | "color-convert": { 155 | "version": "2.0.1", 156 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 157 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 158 | "dev": true, 159 | "requires": { 160 | "color-name": "~1.1.4" 161 | } 162 | }, 163 | "color-name": { 164 | "version": "1.1.4", 165 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 166 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 167 | "dev": true 168 | }, 169 | "commander": { 170 | "version": "2.20.3", 171 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 172 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" 173 | }, 174 | "concat-map": { 175 | "version": "0.0.1", 176 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 177 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 178 | "dev": true 179 | }, 180 | "core-js": { 181 | "version": "2.6.12", 182 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", 183 | "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" 184 | }, 185 | "cross-spawn": { 186 | "version": "7.0.3", 187 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 188 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 189 | "dev": true, 190 | "requires": { 191 | "path-key": "^3.1.0", 192 | "shebang-command": "^2.0.0", 193 | "which": "^2.0.1" 194 | } 195 | }, 196 | "debug": { 197 | "version": "4.3.4", 198 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 199 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 200 | "dev": true, 201 | "requires": { 202 | "ms": "2.1.2" 203 | } 204 | }, 205 | "deep-is": { 206 | "version": "0.1.4", 207 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 208 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 209 | "dev": true 210 | }, 211 | "doctrine": { 212 | "version": "3.0.0", 213 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 214 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 215 | "dev": true, 216 | "requires": { 217 | "esutils": "^2.0.2" 218 | } 219 | }, 220 | "dot": { 221 | "version": "1.1.3", 222 | "resolved": "https://registry.npmjs.org/dot/-/dot-1.1.3.tgz", 223 | "integrity": "sha512-/nt74Rm+PcfnirXGEdhZleTwGC2LMnuKTeeTIlI82xb5loBBoXNYzr2ezCroPSMtilK8EZIfcNZwOcHN+ib1Lg==" 224 | }, 225 | "es6-promisify": { 226 | "version": "7.0.0", 227 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-7.0.0.tgz", 228 | "integrity": "sha512-ginqzK3J90Rd4/Yz7qRrqUeIpe3TwSXTPPZtPne7tGBPeAaQiU8qt4fpKApnxHcq1AwtUdHVg5P77x/yrggG8Q==" 229 | }, 230 | "escomplex-plugin-metrics-module": { 231 | "version": "0.1.0", 232 | "resolved": "https://registry.npmjs.org/escomplex-plugin-metrics-module/-/escomplex-plugin-metrics-module-0.1.0.tgz", 233 | "integrity": "sha512-lEHGInx1gAfgIRJeSTXggTvfEtIG061G0Kzk4hIq0qN6nd2prXZihAPRuLB9DdxCmxXvLVFqi+Bnun9rDw1/zg==", 234 | "requires": { 235 | "typhonjs-escomplex-commons": "^0.1.0" 236 | } 237 | }, 238 | "escomplex-plugin-metrics-project": { 239 | "version": "0.1.0", 240 | "resolved": "https://registry.npmjs.org/escomplex-plugin-metrics-project/-/escomplex-plugin-metrics-project-0.1.0.tgz", 241 | "integrity": "sha512-/9Nf2UJ77qo3TBuWwvFnuNPziHC50nPOxldTZvCEzvwePp+BdI5whlDnJHV9rh3gT75CFODJDM80mBJI+px6/g==", 242 | "requires": { 243 | "typhonjs-escomplex-commons": "^0.1.0" 244 | } 245 | }, 246 | "escomplex-plugin-syntax-babylon": { 247 | "version": "0.1.0", 248 | "resolved": "https://registry.npmjs.org/escomplex-plugin-syntax-babylon/-/escomplex-plugin-syntax-babylon-0.1.0.tgz", 249 | "integrity": "sha512-KNjE0Rf1jNteb7zwFyv4G3+TIuHqUfnmXpRf5u9iJkrbVXjmbC7Fg5UEA1Net8gvoV9RLVImFFaLZxclaagqAA==", 250 | "requires": { 251 | "escomplex-plugin-syntax-estree": "^0.1.0", 252 | "typhonjs-escomplex-commons": "^0.1.0" 253 | } 254 | }, 255 | "escomplex-plugin-syntax-estree": { 256 | "version": "0.1.0", 257 | "resolved": "https://registry.npmjs.org/escomplex-plugin-syntax-estree/-/escomplex-plugin-syntax-estree-0.1.0.tgz", 258 | "integrity": "sha512-uhgBgEhq91rq+1OStjDwpxpS5ehQdq1X/Y7DYCQHplczJN9ZzyoiHostZ4CqywLgDkyicvIyt/8LBYjBaNFc7w==", 259 | "requires": { 260 | "typhonjs-escomplex-commons": "^0.1.0" 261 | } 262 | }, 263 | "eslint": { 264 | "version": "8.16.0", 265 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", 266 | "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", 267 | "dev": true, 268 | "requires": { 269 | "@eslint/eslintrc": "^1.3.0", 270 | "@humanwhocodes/config-array": "^0.9.2", 271 | "ajv": "^6.10.0", 272 | "chalk": "^4.0.0", 273 | "cross-spawn": "^7.0.2", 274 | "debug": "^4.3.2", 275 | "doctrine": "^3.0.0", 276 | "escape-string-regexp": "^4.0.0", 277 | "eslint-scope": "^7.1.1", 278 | "eslint-utils": "^3.0.0", 279 | "eslint-visitor-keys": "^3.3.0", 280 | "espree": "^9.3.2", 281 | "esquery": "^1.4.0", 282 | "esutils": "^2.0.2", 283 | "fast-deep-equal": "^3.1.3", 284 | "file-entry-cache": "^6.0.1", 285 | "functional-red-black-tree": "^1.0.1", 286 | "glob-parent": "^6.0.1", 287 | "globals": "^13.15.0", 288 | "ignore": "^5.2.0", 289 | "import-fresh": "^3.0.0", 290 | "imurmurhash": "^0.1.4", 291 | "is-glob": "^4.0.0", 292 | "js-yaml": "^4.1.0", 293 | "json-stable-stringify-without-jsonify": "^1.0.1", 294 | "levn": "^0.4.1", 295 | "lodash.merge": "^4.6.2", 296 | "minimatch": "^3.1.2", 297 | "natural-compare": "^1.4.0", 298 | "optionator": "^0.9.1", 299 | "regexpp": "^3.2.0", 300 | "strip-ansi": "^6.0.1", 301 | "strip-json-comments": "^3.1.0", 302 | "text-table": "^0.2.0", 303 | "v8-compile-cache": "^2.0.3" 304 | }, 305 | "dependencies": { 306 | "escape-string-regexp": { 307 | "version": "4.0.0", 308 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 309 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 310 | "dev": true 311 | }, 312 | "glob-parent": { 313 | "version": "6.0.2", 314 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 315 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 316 | "dev": true, 317 | "requires": { 318 | "is-glob": "^4.0.3" 319 | } 320 | }, 321 | "is-glob": { 322 | "version": "4.0.3", 323 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 324 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 325 | "dev": true, 326 | "requires": { 327 | "is-extglob": "^2.1.1" 328 | } 329 | } 330 | } 331 | }, 332 | "eslint-scope": { 333 | "version": "7.1.1", 334 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", 335 | "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", 336 | "dev": true, 337 | "requires": { 338 | "esrecurse": "^4.3.0", 339 | "estraverse": "^5.2.0" 340 | } 341 | }, 342 | "eslint-utils": { 343 | "version": "3.0.0", 344 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", 345 | "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", 346 | "dev": true, 347 | "requires": { 348 | "eslint-visitor-keys": "^2.0.0" 349 | }, 350 | "dependencies": { 351 | "eslint-visitor-keys": { 352 | "version": "2.1.0", 353 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", 354 | "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", 355 | "dev": true 356 | } 357 | } 358 | }, 359 | "eslint-visitor-keys": { 360 | "version": "3.3.0", 361 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", 362 | "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", 363 | "dev": true 364 | }, 365 | "espree": { 366 | "version": "9.3.2", 367 | "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", 368 | "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", 369 | "dev": true, 370 | "requires": { 371 | "acorn": "^8.7.1", 372 | "acorn-jsx": "^5.3.2", 373 | "eslint-visitor-keys": "^3.3.0" 374 | } 375 | }, 376 | "esquery": { 377 | "version": "1.4.0", 378 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", 379 | "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", 380 | "dev": true, 381 | "requires": { 382 | "estraverse": "^5.1.0" 383 | } 384 | }, 385 | "esrecurse": { 386 | "version": "4.3.0", 387 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 388 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 389 | "dev": true, 390 | "requires": { 391 | "estraverse": "^5.2.0" 392 | } 393 | }, 394 | "estraverse": { 395 | "version": "5.3.0", 396 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 397 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 398 | "dev": true 399 | }, 400 | "esutils": { 401 | "version": "2.0.3", 402 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 403 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 404 | "dev": true 405 | }, 406 | "fast-deep-equal": { 407 | "version": "3.1.3", 408 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 409 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 410 | "dev": true 411 | }, 412 | "fast-json-stable-stringify": { 413 | "version": "2.1.0", 414 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 415 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 416 | "dev": true 417 | }, 418 | "fast-levenshtein": { 419 | "version": "2.0.6", 420 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 421 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 422 | "dev": true 423 | }, 424 | "file-entry-cache": { 425 | "version": "6.0.1", 426 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 427 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 428 | "dev": true, 429 | "requires": { 430 | "flat-cache": "^3.0.4" 431 | } 432 | }, 433 | "flat-cache": { 434 | "version": "3.0.4", 435 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", 436 | "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", 437 | "dev": true, 438 | "requires": { 439 | "flatted": "^3.1.0", 440 | "rimraf": "^3.0.2" 441 | }, 442 | "dependencies": { 443 | "rimraf": { 444 | "version": "3.0.2", 445 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 446 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 447 | "dev": true, 448 | "requires": { 449 | "glob": "^7.1.3" 450 | } 451 | } 452 | } 453 | }, 454 | "flatted": { 455 | "version": "3.2.5", 456 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", 457 | "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", 458 | "dev": true 459 | }, 460 | "fs.realpath": { 461 | "version": "1.0.0", 462 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 463 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 464 | "dev": true 465 | }, 466 | "functional-red-black-tree": { 467 | "version": "1.0.1", 468 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 469 | "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", 470 | "dev": true 471 | }, 472 | "glob": { 473 | "version": "7.2.3", 474 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 475 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 476 | "dev": true, 477 | "requires": { 478 | "fs.realpath": "^1.0.0", 479 | "inflight": "^1.0.4", 480 | "inherits": "2", 481 | "minimatch": "^3.1.1", 482 | "once": "^1.3.0", 483 | "path-is-absolute": "^1.0.0" 484 | } 485 | }, 486 | "globals": { 487 | "version": "13.15.0", 488 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", 489 | "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", 490 | "dev": true, 491 | "requires": { 492 | "type-fest": "^0.20.2" 493 | } 494 | }, 495 | "has-flag": { 496 | "version": "4.0.0", 497 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 498 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 499 | "dev": true 500 | }, 501 | "ignore": { 502 | "version": "5.2.0", 503 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 504 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 505 | "dev": true 506 | }, 507 | "import-fresh": { 508 | "version": "3.3.0", 509 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 510 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 511 | "dev": true, 512 | "requires": { 513 | "parent-module": "^1.0.0", 514 | "resolve-from": "^4.0.0" 515 | } 516 | }, 517 | "imurmurhash": { 518 | "version": "0.1.4", 519 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 520 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 521 | "dev": true 522 | }, 523 | "inflight": { 524 | "version": "1.0.6", 525 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 526 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 527 | "dev": true, 528 | "requires": { 529 | "once": "^1.3.0", 530 | "wrappy": "1" 531 | } 532 | }, 533 | "inherits": { 534 | "version": "2.0.4", 535 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 536 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 537 | "dev": true 538 | }, 539 | "is-extglob": { 540 | "version": "2.1.1", 541 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 542 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 543 | "dev": true 544 | }, 545 | "isexe": { 546 | "version": "2.0.0", 547 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 548 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 549 | "dev": true 550 | }, 551 | "js-yaml": { 552 | "version": "4.1.0", 553 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 554 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 555 | "dev": true, 556 | "requires": { 557 | "argparse": "^2.0.1" 558 | } 559 | }, 560 | "json-schema-traverse": { 561 | "version": "0.4.1", 562 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 563 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 564 | "dev": true 565 | }, 566 | "json-stable-stringify-without-jsonify": { 567 | "version": "1.0.1", 568 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 569 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 570 | "dev": true 571 | }, 572 | "levn": { 573 | "version": "0.4.1", 574 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 575 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 576 | "dev": true, 577 | "requires": { 578 | "prelude-ls": "^1.2.1", 579 | "type-check": "~0.4.0" 580 | } 581 | }, 582 | "lodash": { 583 | "version": "4.17.21", 584 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 585 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 586 | }, 587 | "lodash.merge": { 588 | "version": "4.6.2", 589 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 590 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 591 | "dev": true 592 | }, 593 | "minimatch": { 594 | "version": "3.1.2", 595 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 596 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 597 | "dev": true, 598 | "requires": { 599 | "brace-expansion": "^1.1.7" 600 | } 601 | }, 602 | "ms": { 603 | "version": "2.1.2", 604 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 605 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 606 | "dev": true 607 | }, 608 | "natural-compare": { 609 | "version": "1.4.0", 610 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 611 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 612 | "dev": true 613 | }, 614 | "once": { 615 | "version": "1.4.0", 616 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 617 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 618 | "dev": true, 619 | "requires": { 620 | "wrappy": "1" 621 | } 622 | }, 623 | "optionator": { 624 | "version": "0.9.1", 625 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 626 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 627 | "dev": true, 628 | "requires": { 629 | "deep-is": "^0.1.3", 630 | "fast-levenshtein": "^2.0.6", 631 | "levn": "^0.4.1", 632 | "prelude-ls": "^1.2.1", 633 | "type-check": "^0.4.0", 634 | "word-wrap": "^1.2.3" 635 | } 636 | }, 637 | "parent-module": { 638 | "version": "1.0.1", 639 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 640 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 641 | "dev": true, 642 | "requires": { 643 | "callsites": "^3.0.0" 644 | } 645 | }, 646 | "path-is-absolute": { 647 | "version": "1.0.1", 648 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 649 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 650 | "dev": true 651 | }, 652 | "path-key": { 653 | "version": "3.1.1", 654 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 655 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 656 | "dev": true 657 | }, 658 | "prelude-ls": { 659 | "version": "1.2.1", 660 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 661 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 662 | "dev": true 663 | }, 664 | "punycode": { 665 | "version": "2.1.1", 666 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 667 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 668 | "dev": true 669 | }, 670 | "regenerator-runtime": { 671 | "version": "0.11.1", 672 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", 673 | "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" 674 | }, 675 | "regexpp": { 676 | "version": "3.2.0", 677 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", 678 | "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", 679 | "dev": true 680 | }, 681 | "resolve-from": { 682 | "version": "4.0.0", 683 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 684 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 685 | "dev": true 686 | }, 687 | "shebang-command": { 688 | "version": "2.0.0", 689 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 690 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 691 | "dev": true, 692 | "requires": { 693 | "shebang-regex": "^3.0.0" 694 | } 695 | }, 696 | "shebang-regex": { 697 | "version": "3.0.0", 698 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 699 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 700 | "dev": true 701 | }, 702 | "strip-ansi": { 703 | "version": "6.0.1", 704 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 705 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 706 | "dev": true, 707 | "requires": { 708 | "ansi-regex": "^5.0.1" 709 | }, 710 | "dependencies": { 711 | "ansi-regex": { 712 | "version": "5.0.1", 713 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 714 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 715 | "dev": true 716 | } 717 | } 718 | }, 719 | "strip-json-comments": { 720 | "version": "3.1.1", 721 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 722 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 723 | "dev": true 724 | }, 725 | "text-table": { 726 | "version": "0.2.0", 727 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 728 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 729 | "dev": true 730 | }, 731 | "type-check": { 732 | "version": "0.4.0", 733 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 734 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 735 | "dev": true, 736 | "requires": { 737 | "prelude-ls": "^1.2.1" 738 | } 739 | }, 740 | "type-fest": { 741 | "version": "0.20.2", 742 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 743 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 744 | "dev": true 745 | }, 746 | "typhonjs-ast-walker": { 747 | "version": "0.2.1", 748 | "resolved": "https://registry.npmjs.org/typhonjs-ast-walker/-/typhonjs-ast-walker-0.2.1.tgz", 749 | "integrity": "sha1-a+uNuMxFtbxDjIyVhT+NOFQTyz4=" 750 | }, 751 | "typhonjs-escomplex": { 752 | "version": "0.1.0", 753 | "resolved": "https://registry.npmjs.org/typhonjs-escomplex/-/typhonjs-escomplex-0.1.0.tgz", 754 | "integrity": "sha512-B2r31mRH/ZuuogZADqOBP4NPnqBH2mzEP+Pjm+5R8/z0iHLwUTRadkDQL4yv33qsHdIHlvnpOHh6iUo69v2mPA==", 755 | "requires": { 756 | "@typhonjs/babel-parser": "^0.2.0", 757 | "commander": "^2.0.0", 758 | "typhonjs-escomplex-module": "^0.1.0", 759 | "typhonjs-escomplex-project": "^0.1.0" 760 | } 761 | }, 762 | "typhonjs-escomplex-commons": { 763 | "version": "0.1.1", 764 | "resolved": "https://registry.npmjs.org/typhonjs-escomplex-commons/-/typhonjs-escomplex-commons-0.1.1.tgz", 765 | "integrity": "sha512-nIeiokmcupP3t34KVhynHi0LrjMM6QzbHiwK8+Kl/GnAq4o/jK+cCiu4Zt9mZgJKUzUGRtW1kqoZI2RlAchkgg==" 766 | }, 767 | "typhonjs-escomplex-module": { 768 | "version": "0.1.0", 769 | "resolved": "https://registry.npmjs.org/typhonjs-escomplex-module/-/typhonjs-escomplex-module-0.1.0.tgz", 770 | "integrity": "sha512-ViviuBg7Etrl8HeQcwQ9gui9s0rpnFPXltXUFAvOzeQScb+xrLijBq7Z0cveriKQegne/tGTn4ljZg0prwDyTw==", 771 | "requires": { 772 | "escomplex-plugin-metrics-module": "^0.1.0", 773 | "escomplex-plugin-syntax-babylon": "^0.1.0", 774 | "typhonjs-ast-walker": "^0.2.0", 775 | "typhonjs-escomplex-commons": "^0.1.0", 776 | "typhonjs-plugin-manager": "^0.2.0" 777 | } 778 | }, 779 | "typhonjs-escomplex-project": { 780 | "version": "0.1.0", 781 | "resolved": "https://registry.npmjs.org/typhonjs-escomplex-project/-/typhonjs-escomplex-project-0.1.0.tgz", 782 | "integrity": "sha512-2NdkUsDmxcgsNFqn14CYl5Xfzlig7kKtU/ACOLRN6/nP9c3rlHizVr8YdBYRf2H1sOjmjfNmbC6i1RVnnJZxZQ==", 783 | "requires": { 784 | "escomplex-plugin-metrics-project": "^0.1.0", 785 | "typhonjs-escomplex-commons": "^0.1.0", 786 | "typhonjs-escomplex-module": "^0.1.0", 787 | "typhonjs-plugin-manager": "^0.2.0" 788 | } 789 | }, 790 | "typhonjs-object-util": { 791 | "version": "0.4.2", 792 | "resolved": "https://registry.npmjs.org/typhonjs-object-util/-/typhonjs-object-util-0.4.2.tgz", 793 | "integrity": "sha1-HJwqcJ4EeDRdgJh5VicxwwnhpVQ=", 794 | "requires": { 795 | "babel-runtime": "^6.0.0" 796 | } 797 | }, 798 | "typhonjs-plugin-manager": { 799 | "version": "0.2.0", 800 | "resolved": "https://registry.npmjs.org/typhonjs-plugin-manager/-/typhonjs-plugin-manager-0.2.0.tgz", 801 | "integrity": "sha1-JgxKzfarvpT9NcoKJSUYOa2eFNM=", 802 | "requires": { 803 | "babel-runtime": "^6.0.0", 804 | "backbone-esnext-events": "<1.0.0", 805 | "typhonjs-object-util": "^0.4.0" 806 | } 807 | }, 808 | "uri-js": { 809 | "version": "4.4.1", 810 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 811 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 812 | "dev": true, 813 | "requires": { 814 | "punycode": "^2.1.0" 815 | } 816 | }, 817 | "v8-compile-cache": { 818 | "version": "2.3.0", 819 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", 820 | "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", 821 | "dev": true 822 | }, 823 | "which": { 824 | "version": "2.0.2", 825 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 826 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 827 | "dev": true, 828 | "requires": { 829 | "isexe": "^2.0.0" 830 | } 831 | }, 832 | "word-wrap": { 833 | "version": "1.2.3", 834 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 835 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 836 | "dev": true 837 | }, 838 | "wrappy": { 839 | "version": "1.0.2", 840 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 841 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 842 | "dev": true 843 | } 844 | } 845 | } 846 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-complexity-analysis", 3 | "displayName": "JS Complexity Analysis", 4 | "description": "Produce a complexity analysis report of your JS project", 5 | "version": "2.0.0", 6 | "publisher": "TomiTurtiainen", 7 | "engines": { 8 | "vscode": "^1.5.1" 9 | }, 10 | "scripts": { 11 | "lint": "eslint --ext .js ./src" 12 | }, 13 | "categories": [ 14 | "Other" 15 | ], 16 | "activationEvents": [ 17 | "onCommand:complexityAnalysis.analyseFile", 18 | "onCommand:complexityAnalysis.analyseProject", 19 | "onLanguage:javascript" 20 | ], 21 | "main": "./out/extension", 22 | "contributes": { 23 | "commands": [ 24 | { 25 | "command": "complexityAnalysis.analyseFile", 26 | "title": "File complexity analysis" 27 | }, 28 | { 29 | "command": "complexityAnalysis.analyseProject", 30 | "title": "Project complexity analysis" 31 | } 32 | ], 33 | "configuration": { 34 | "type": "object", 35 | "title": "Complexity analysis configuration", 36 | "properties": { 37 | "complexityAnalysis.include": { 38 | "type": "array", 39 | "default": [], 40 | "description": "An array of files/folders that should be included by the parser. Glob patterns are accepted (eg. src/**/*.js)" 41 | }, 42 | "complexityAnalysis.exclude": { 43 | "type": "array", 44 | "default": [], 45 | "description": "An array of files/folders that should be ignored by the parser. Glob patterns are accepted (eg. test/**/*.js)" 46 | } 47 | } 48 | } 49 | }, 50 | "repository": { 51 | "type": "git", 52 | "url": "https://github.com/tomi/vscode-js-complexity-analysis.git" 53 | }, 54 | "license": "SEE LICENSE IN LICENSE.md", 55 | "bugs": { 56 | "url": "https://github.com/tomi/vscode-js-complexity-analysis/issues" 57 | }, 58 | "homepage": "https://github.com/tomi/vscode-js-complexity-analysis", 59 | "icon": "images/logo.png", 60 | "galleryBanner": { 61 | "color": "#ffffff", 62 | "theme": "light" 63 | }, 64 | "devDependencies": { 65 | "eslint": "^8.16.0" 66 | }, 67 | "dependencies": { 68 | "dot": "1.1.3", 69 | "es6-promisify": "7.0.0", 70 | "lodash": "^4.17.21", 71 | "typhonjs-escomplex": "0.1.0" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/commands/analyse-file.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { workspace, window } from "vscode"; 4 | import Analyzer from "../complexity-analyzer"; 5 | import FileAnalysis from "../models/file-analysis.js"; 6 | import FileReport from "../report/file-report.js"; 7 | 8 | function AnalyseFile(reportFactory, navigator) { 9 | 10 | function buildReport(document) { 11 | const filePath = workspace.asRelativePath(document.fileName); 12 | 13 | const fileContents = document.getText(); 14 | const rawAnalysis = Analyzer.analyse(fileContents); 15 | const analysis = new FileAnalysis(filePath, rawAnalysis); 16 | 17 | const report = new FileReport(analysis, false); 18 | reportFactory.addReport(filePath, report); 19 | 20 | navigator.navigate(`/${ filePath }`); 21 | } 22 | 23 | function runAnalysis(editor) { 24 | try { 25 | buildReport(editor.document); 26 | } catch (e) { 27 | console.log(e); 28 | window.showErrorMessage("Failed to analyse file. " + e); 29 | } 30 | } 31 | 32 | this.execute = runAnalysis; 33 | } 34 | 35 | export default AnalyseFile; 36 | -------------------------------------------------------------------------------- /src/commands/analyse-project.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { readFileAsync } from "fs"; 4 | import { window } from "vscode"; 5 | import { analyse } from "../complexity-analyzer"; 6 | import { getWorkspaceFiles } from "../utils/workspace"; 7 | 8 | import FileAnalysis from "../models/file-analysis.js"; 9 | import ProjectAnalysis from "../models/project-analysis.js"; 10 | import FileReport from "../report/file-report.js"; 11 | import ProjectReport from "../report/project-report.js"; 12 | 13 | function AnalyseProject(reportFactory, navigator) { 14 | function runAnalysis() { 15 | try { 16 | buildReport() 17 | .then(null, handleError); 18 | } catch (error) { 19 | handleError(error); 20 | } 21 | } 22 | 23 | function buildReport() { 24 | return getWorkspaceFiles() 25 | .then(files => { 26 | const analysePromises = files.map(analyseSingleFile); 27 | 28 | return Promise.all(analysePromises); 29 | }) 30 | .then(createAggregateReport); 31 | } 32 | 33 | function analyseSingleFile({ fsPath, relativePath }) { 34 | return readFileAsync(fsPath, "utf8") 35 | .then(fileContents => { 36 | try { 37 | const rawAnalysis = analyse(fileContents); 38 | const analysis = new FileAnalysis(relativePath, rawAnalysis); 39 | 40 | const report = new FileReport(analysis); 41 | reportFactory.addReport(relativePath, report); 42 | 43 | return analysis; 44 | } catch (e) { 45 | const errorMsg = `File ${ relativePath } analysis failed: ${ e }`; 46 | console.error(errorMsg); 47 | return errorMsg; 48 | } 49 | }) 50 | } 51 | 52 | function createAggregateReport(analyses, channel, metrics) { 53 | const projectAnalysis = new ProjectAnalysis(); 54 | const errors = []; 55 | 56 | analyses.forEach(analysis => { 57 | if (typeof analysis !== "string") { 58 | projectAnalysis.add(analysis); 59 | } else { 60 | errors.push(analysis); 61 | } 62 | }); 63 | 64 | const aggregate = projectAnalysis.getSummary(); 65 | 66 | const report = new ProjectReport(aggregate, errors); 67 | reportFactory.addReport("/", report); 68 | 69 | navigator.navigate("/"); 70 | } 71 | 72 | function handleError(error) { 73 | window.showErrorMessage("Failed to analyse file. " + error); 74 | console.log(error); 75 | } 76 | 77 | this.execute = runAnalysis; 78 | } 79 | 80 | export default AnalyseProject; 81 | -------------------------------------------------------------------------------- /src/complexity-analyzer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { sum } from "lodash"; 4 | import { analyzeModule, processProject } from "typhonjs-escomplex"; 5 | 6 | function analyse(js) { 7 | return analyzeModule(js); 8 | } 9 | 10 | function process(analyses) { 11 | const summary = processProject(analyses); 12 | 13 | summary.totalLOC = sum(summary.reports.map(report => 14 | report.aggregate.sloc.logical)); 15 | 16 | return summary; 17 | } 18 | 19 | export default { 20 | analyse, 21 | process 22 | }; 23 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { readFileAsync } from "fs"; 4 | import { join } from "path"; 5 | import { workspace as _workspace } from "vscode"; 6 | import { isEmpty } from "lodash"; 7 | const workspace = _workspace; 8 | 9 | const CONFIG_BLOCK_NAME = "complexityAnalysis"; 10 | 11 | const DEFAULT_INCLUDE = "**/*.js"; 12 | 13 | const navigation = { 14 | scheme: "jsComplexityAnalysis", 15 | authority: "complexity-analysis" 16 | }; 17 | 18 | /** 19 | * Returns configured include and exclude patterns 20 | */ 21 | function getIncludeExclude() { 22 | const extensionConfig = workspace.getConfiguration(CONFIG_BLOCK_NAME); 23 | 24 | const workspaceConfig = _getWorkspaceConfig(extensionConfig); 25 | if (workspaceConfig) { 26 | return Promise.resolve(workspaceConfig); 27 | } 28 | 29 | return _getJsConfigConfig(workspace.rootPath) 30 | .then(jsconfig => { 31 | if (jsconfig) { 32 | return jsconfig; 33 | } 34 | 35 | return { 36 | include: [], 37 | exclude: [] 38 | }; 39 | }); 40 | } 41 | 42 | function _getWorkspaceConfig(extensionConfig) { 43 | const hasConfig = extensionConfig.has("include") || extensionConfig.has("exclude"); 44 | 45 | if (!hasConfig) { 46 | return null; 47 | } 48 | 49 | const include = extensionConfig.get("include"); 50 | const exclude = extensionConfig.get("exclude"); 51 | 52 | if (!Array.isArray(include)) { 53 | throw new Error("complexityAnalysis.include needs to be an array") 54 | } 55 | 56 | if (!Array.isArray(exclude)) { 57 | throw new Error("complexityAnalysis.exclude needs to be an array") 58 | } 59 | 60 | return { 61 | include: isEmpty(include) ? [DEFAULT_INCLUDE] : include, 62 | exclude 63 | }; 64 | } 65 | 66 | function _getJsConfigConfig(rootPath) { 67 | const jsconfigFilename = join(rootPath, "jsconfig.json"); 68 | 69 | return readFileAsync(jsconfigFilename, "utf8") 70 | .then(fileContents => { 71 | const jsconfig = JSON.parse(fileContents); 72 | if (!jsconfig.include && !jsconfig.exclude) { 73 | return null; 74 | } 75 | 76 | return { 77 | include: jsconfig.include || DEFAULT_INCLUDE, 78 | exclude: jsconfig.exclude || [] 79 | }; 80 | }) 81 | .catch(() => undefined); 82 | } 83 | 84 | export default { 85 | getIncludeExclude, 86 | options: { 87 | navigation 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /src/controller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { commands, workspace } from "vscode"; 4 | 5 | import config from "./config"; 6 | import Navigator from "./navigator"; 7 | import ReportFactory from "./report-factory"; 8 | import HtmlReportProvider from "./html-report-provider"; 9 | import AnalyseFileCommand from "./commands/analyse-file"; 10 | import AnalyseProjectCommand from "./commands/analyse-project"; 11 | 12 | const AnalyseFileCmdName = "complexityAnalysis.analyseFile"; 13 | const AnalyseProjectCmdName = "complexityAnalysis.analyseProject"; 14 | 15 | function Controller(context) { 16 | const reportFactory = new ReportFactory(); 17 | const reportProvider = new HtmlReportProvider(reportFactory, config.options.navigation); 18 | const navigator = new Navigator(config.options.navigation, reportProvider); 19 | const cmdAnalyseFile = new AnalyseFileCommand(reportFactory, navigator); 20 | const cmdAnalyseProject = new AnalyseProjectCommand(reportFactory, navigator); 21 | 22 | function activate() { 23 | context.subscriptions.push( 24 | commands.registerTextEditorCommand( 25 | AnalyseFileCmdName, cmdAnalyseFile.execute)); 26 | 27 | context.subscriptions.push( 28 | commands.registerCommand( 29 | AnalyseProjectCmdName, cmdAnalyseProject.execute)); 30 | 31 | context.subscriptions.push( 32 | workspace.registerTextDocumentContentProvider( 33 | config.options.navigation.scheme, reportProvider)); 34 | } 35 | 36 | function dispose() { 37 | } 38 | 39 | this.activate = activate; 40 | this.dispose = dispose; 41 | } 42 | 43 | export default Controller; 44 | -------------------------------------------------------------------------------- /src/extension.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import Controller from "./controller"; 4 | 5 | function activate(context) { 6 | const controller = new Controller(context); 7 | 8 | context.subscriptions.push(controller); 9 | controller.activate(); 10 | } 11 | 12 | const _activate = activate; 13 | export { _activate as activate }; 14 | -------------------------------------------------------------------------------- /src/html-report-provider.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { EventEmitter } from "vscode"; 4 | 5 | /** 6 | * 7 | */ 8 | function HtmlReportProvider(reportFactory, options) { 9 | const eventEmitter = new EventEmitter(); 10 | this.scheme = options.scheme; 11 | 12 | function getHtml(path) { 13 | const report = reportFactory.getReport(path); 14 | if (report) { 15 | const html = report.toHtml(); 16 | return html; 17 | } 18 | 19 | return `Invalid path ${ path }`; 20 | } 21 | 22 | this.provideTextDocumentContent = function(uri) { 23 | // Remove leading slash unless it's alone 24 | const path = uri.path.replace(/^\//, "") || "/"; 25 | 26 | return getHtml(path); 27 | }; 28 | 29 | this.onDidChange = function() { 30 | return eventEmitter.event; 31 | }; 32 | 33 | this.update = function(uri) { 34 | eventEmitter.fire(uri); 35 | }; 36 | } 37 | 38 | export default HtmlReportProvider; 39 | -------------------------------------------------------------------------------- /src/models/file-analysis.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { max as _max, flatMap } from "lodash"; 4 | 5 | /** 6 | * Analysis data for single file 7 | */ 8 | function FileAnalysis(path, analysis) { 9 | this.path = path; 10 | 11 | this.dependencies = analysis.dependencies; 12 | 13 | // Scale to between 0 and 100 14 | this.maintainability = Math.max(0, analysis.maintainability * 100 / 171); 15 | this.sloc = analysis.aggregate.sloc.logical; 16 | 17 | const functionsMax = _max(analysis.methods.map(m => m.cyclomatic)) || 0; 18 | const classMethodsMax = _max( 19 | flatMap(analysis.classes.map(c => c.methods.map(m => m.cyclomatic))) 20 | ) || 0; 21 | this.cyclomatic = { 22 | avg: analysis.methodAverage.cyclomatic, 23 | max: _max([functionsMax, classMethodsMax]) 24 | }; 25 | this.difficulty = analysis.aggregate.halstead.difficulty 26 | this.bugs = analysis.aggregate.halstead.bugs; 27 | 28 | this.functions = analysis.methods.map(f => ({ 29 | name: f.name, 30 | line: f.lineStart, 31 | params: f.paramCount, 32 | sloc: f.sloc.logical, 33 | cyclomatic: f.cyclomatic, 34 | difficulty: f.halstead.difficulty, 35 | bugs: f.halstead.bugs 36 | })); 37 | 38 | this.classes = analysis.classes.map(c => ({ 39 | name: c.name, 40 | line: c.lineStart, 41 | sloc: c.aggregate.sloc.logical, 42 | difficulty: c.aggregate.halstead.difficulty, 43 | bugs: c.aggregate.halstead.bugs, 44 | methods: c.methods.map(method => ({ 45 | name: method.name, 46 | line: method.lineStart, 47 | params: method.paramCount, 48 | sloc: method.sloc.logical, 49 | cyclomatic: method.cyclomatic, 50 | difficulty: method.halstead.difficulty, 51 | bugs: method.halstead.bugs 52 | })) 53 | })) 54 | 55 | Object.freeze(this); 56 | } 57 | 58 | export default FileAnalysis; 59 | -------------------------------------------------------------------------------- /src/models/project-analysis.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function calculateAverages(analyses) { 4 | const result = {}; 5 | const sum = { 6 | sloc: 0, 7 | cyclomatic: 0, 8 | maintainability: 0 9 | }; 10 | const metrics = Object.keys(sum); 11 | const divisor = analyses.length || 1; 12 | 13 | analyses.forEach(analysis => { 14 | metrics.forEach(metric => { 15 | sum[metric] += analysis[metric]; 16 | }); 17 | }); 18 | 19 | metrics.forEach(metric => { 20 | result[metric] = sum[metric] / divisor; 21 | }); 22 | 23 | return result; 24 | } 25 | 26 | function calculateTotalSloc(analyses) { 27 | return analyses.reduce((sum, analysis) => sum + analysis.sloc, 0); 28 | } 29 | 30 | function ProjectAnalysis() { 31 | const analyses = []; 32 | 33 | this.add = function(analysis) { 34 | analyses.push(analysis); 35 | } 36 | 37 | this.getSummary = function() { 38 | const totalSloc = calculateTotalSloc(analyses); 39 | const averages = calculateAverages(analyses); 40 | 41 | return { 42 | totalSloc: totalSloc, 43 | avgSloc: averages.sloc, 44 | avgMaintainability: averages.maintainability, 45 | avgCyclomatic: averages.cyclomatic, 46 | fileAnalyses: analyses 47 | }; 48 | } 49 | } 50 | 51 | export default ProjectAnalysis; 52 | -------------------------------------------------------------------------------- /src/navigator.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import * as vscode from 'vscode'; 4 | import { window, ViewColumn, Uri } from "vscode"; 5 | 6 | class Navigator { 7 | constructor(options, reportProvider) { 8 | function getTargetColumn() { 9 | const numOpenEditors = window.visibleTextEditors.length; 10 | 11 | switch (numOpenEditors) { 12 | case 0: 13 | return ViewColumn.One; 14 | case 1: 15 | return ViewColumn.Two; 16 | case 2: 17 | return ViewColumn.Three; 18 | case 3: 19 | return ViewColumn.Three; 20 | default: 21 | return ViewColumn.One; 22 | } 23 | } 24 | 25 | function navigate(path) { 26 | const panel = vscode.window.createWebviewPanel( 27 | "complexity-analysis", // Identifies the type of the webview. Used internally 28 | "Complexity Analysis Coding", // Title of the panel displayed to the user 29 | getTargetColumn(), // Editor column to show the new webview panel in. 30 | {} // Webview options. More on these later. 31 | ); 32 | 33 | const uri = Uri.parse(`${options.scheme}://${options.authority}${path}`); 34 | reportProvider.update(uri); 35 | panel.webview.html = reportProvider.provideTextDocumentContent(uri); 36 | } 37 | 38 | this.navigate = navigate; 39 | } 40 | } 41 | 42 | export default Navigator; 43 | -------------------------------------------------------------------------------- /src/report-factory.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class ReportFactory { 4 | constructor() { 5 | // Key: report uri as string 6 | // Value: FileReport 7 | const reports = new Map(); 8 | 9 | function addReport(path, report) { 10 | reports.set(path, report); 11 | } 12 | 13 | function getReport(uri) { 14 | return reports.get(uri); 15 | } 16 | 17 | function clear() { 18 | reports.clear(); 19 | } 20 | 21 | this.addReport = addReport; 22 | this.getReport = getReport; 23 | this.clear = clear; 24 | } 25 | } 26 | 27 | export default ReportFactory; 28 | -------------------------------------------------------------------------------- /src/report/classes-table.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import Table from "./table"; 4 | import { formatMetric } from "./metric-formatter.js"; 5 | import { fileLineLink as link } from "./link.js"; 6 | 7 | const columns = [ 8 | { title: "Function", align: "left" }, 9 | { title: "SLOC", align: "right" }, 10 | { title: "# params", align: "right" }, 11 | { title: "Complexity", align: "right" }, 12 | { title: "Difficulty", align: "right" }, 13 | { title: "Est # bugs", align: "right" } 14 | ]; 15 | 16 | function formatName(filePath, name, line) { 17 | const encodedName = name 18 | .replace(/&/g, "&") 19 | .replace(//g, ">") 21 | .replace(/"/g, """) 22 | .replace(/'/g, "'"); 23 | 24 | return link(encodedName, filePath, line); 25 | } 26 | 27 | function ClassesTable(analysis) { 28 | const filePath = analysis.path; 29 | 30 | const classes = analysis.classes.map(c => { 31 | const rows = c.methods.map(f => [ 32 | formatName(filePath, f.name, f.line), 33 | f.sloc, 34 | f.params, 35 | formatMetric(f.cyclomatic, 6, 10), 36 | formatMetric(f.difficulty), 37 | formatMetric(f.bugs) 38 | ]); 39 | 40 | const functionsTable = new Table({ 41 | columns: columns, 42 | rows: rows 43 | }); 44 | 45 | return c.name + "

" + functionsTable.toHtml(); 46 | }); 47 | 48 | return classes.join("

"); 49 | } 50 | 51 | export default ClassesTable; 52 | -------------------------------------------------------------------------------- /src/report/file-report.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import HtmlBuilder from "./html-builder"; 4 | import metricRow from "./metric-row"; 5 | import reportStyle from "./report-style"; 6 | import header from "./header"; 7 | import functionsTable from "./functions-table"; 8 | import { localLink as link } from "./link"; 9 | 10 | const overviewMetrics = { 11 | maintainability: 12 | { 13 | title: "Maintainability", 14 | description: "Value between 0 and 100. Represents the relative ease of maintaining the code. A high value means better maintainability.", 15 | errorRange: [0, 10], 16 | warningRange: [10, 20], 17 | infoUrl: "https://blogs.msdn.microsoft.com/zainnab/2011/05/26/code-metrics-maintainability-index/" 18 | }, 19 | loc: 20 | { 21 | title: "Lines of code", 22 | description: "Logical number of source lines of code.", 23 | infoUrl: "https://en.wikipedia.org/wiki/Source_lines_of_code" 24 | }, 25 | difficulty: 26 | { 27 | title: "Difficulty", 28 | description: "How difficult it is to write or understand the program.", 29 | errorRange: [60, 9999], 30 | warningRange: [30, 60], 31 | infoUrl: "https://en.wikipedia.org/wiki/Halstead_complexity_measures" 32 | }, 33 | bugs: 34 | { 35 | title: "Estimated # of Bugs", 36 | description: "Estimate for the number of errors in the implementation.", 37 | errorRange: [60, 9999], 38 | warningRange: [30, 60], 39 | infoUrl: "https://en.wikipedia.org/wiki/Halstead_complexity_measures" 40 | }, 41 | }; 42 | 43 | function backLink() { 44 | return `${ link("", "◀ back") }`; 45 | } 46 | 47 | function buildFileSummary(htmlBuilder, analysis, includeBackLink) { 48 | const metrics = [ 49 | { metric: overviewMetrics.maintainability, value: analysis.maintainability }, 50 | { metric: overviewMetrics.loc, value: analysis.sloc }, 51 | { metric: overviewMetrics.difficulty, value: analysis.difficulty }, 52 | { metric: overviewMetrics.bugs, value: analysis.bugs }, 53 | ]; 54 | 55 | htmlBuilder 56 | .appendBody(header("Summary")) 57 | .appendBody(metricRow(metrics)); 58 | 59 | if (analysis.functions.length > 0) { 60 | htmlBuilder 61 | .appendBody(header("Functions")) 62 | .appendBody(functionsTable(analysis.path, analysis.functions)); 63 | } 64 | 65 | analysis.classes.forEach(classAnalysis => { 66 | htmlBuilder 67 | .appendBody(header(`class ${ classAnalysis.name }`)) 68 | .appendBody(functionsTable(analysis.path, classAnalysis.methods)) 69 | .appendBody("

"); 70 | }) 71 | 72 | if (includeBackLink) { 73 | htmlBuilder.appendBody(backLink()); 74 | } 75 | } 76 | 77 | function FileReport(analysis, includeBackLink = true) { 78 | function toHtml() { 79 | const htmlBuilder = new HtmlBuilder(); 80 | 81 | htmlBuilder.appendStyle(reportStyle); 82 | 83 | buildFileSummary(htmlBuilder, analysis, includeBackLink); 84 | 85 | return htmlBuilder.toHtml(); 86 | } 87 | 88 | this.toHtml = toHtml; 89 | } 90 | 91 | export default FileReport; 92 | -------------------------------------------------------------------------------- /src/report/files-table.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import Table from "./table"; 4 | import { formatMetric } from "./metric-formatter"; 5 | import { localLink as link } from "./link"; 6 | 7 | const columns = [ 8 | { title: "Name", align: "left" }, 9 | { title: "SLOC", align: "right" }, 10 | { title: "Avg
Complexity", align: "right" }, 11 | { title: "Max
Complexity", align: "right" }, 12 | { title: "Est errors", align: "right" } 13 | ]; 14 | 15 | function formatFile(filePath) { 16 | return link(filePath, filePath); 17 | } 18 | 19 | function FilesTable(analysis) { 20 | const rows = analysis.fileAnalyses.map(f => [ 21 | formatFile(f.path), 22 | f.sloc, 23 | formatMetric(f.cyclomatic.avg, 6, 10), 24 | formatMetric(f.cyclomatic.max, 6, 10), 25 | formatMetric(f.bugs) 26 | ]); 27 | 28 | const filesTable = new Table({ 29 | columns: columns, 30 | rows: rows 31 | }); 32 | 33 | return filesTable.toHtml(); 34 | } 35 | 36 | export default FilesTable; 37 | -------------------------------------------------------------------------------- /src/report/functions-table.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import Table from "./table"; 4 | import metricFormatter from "./metric-formatter"; 5 | import link from "./link.js"; 6 | 7 | const columns = [ 8 | { title: "Function", align: "left" }, 9 | { title: "SLOC", align: "right" }, 10 | { title: "# params", align: "right" }, 11 | { title: "Complexity", align: "right" }, 12 | { title: "Difficulty", align: "right" }, 13 | { title: "Est # bugs", align: "right" } 14 | ]; 15 | 16 | function formatName(filePath, name, line) { 17 | const encodedName = name 18 | .replace(/&/g, "&") 19 | .replace(//g, ">") 21 | .replace(/"/g, """) 22 | .replace(/'/g, "'"); 23 | 24 | return link.fileLineLink(encodedName, filePath, line); 25 | } 26 | 27 | function FunctionsTable(filePath, functions) { 28 | const rows = functions.map(f => [ 29 | formatName(filePath, f.name, f.line), 30 | f.sloc, 31 | f.params, 32 | metricFormatter.formatMetric(f.cyclomatic, 6, 10), 33 | metricFormatter.formatMetric(f.difficulty), 34 | metricFormatter.formatMetric(f.bugs) 35 | ]); 36 | 37 | const functionsTable = new Table({ 38 | columns: columns, 39 | rows: rows 40 | }); 41 | 42 | return functionsTable.toHtml(); 43 | } 44 | 45 | export default FunctionsTable; 46 | -------------------------------------------------------------------------------- /src/report/header.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function header(title) { 4 | return `

${ title }

`; 5 | } 6 | 7 | export default header; 8 | -------------------------------------------------------------------------------- /src/report/html-builder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function getStyleDefinition(css) { 4 | return css ? `` : ""; 5 | } 6 | 7 | class HtmlBuilder { 8 | constructor() { 9 | let styles = ""; 10 | let body = ""; 11 | 12 | function appendStyle(css) { 13 | styles += css; 14 | 15 | return this; 16 | } 17 | 18 | function appendBody(html) { 19 | body += html; 20 | 21 | return this; 22 | } 23 | 24 | function toHtml() { 25 | return ` 26 | 27 | ${getStyleDefinition(styles)} 28 | 29 | 30 | ${body} 31 | 32 | `; 33 | } 34 | 35 | this.appendStyle = appendStyle; 36 | this.appendBody = appendBody; 37 | this.toHtml = toHtml; 38 | } 39 | } 40 | 41 | export default HtmlBuilder; 42 | -------------------------------------------------------------------------------- /src/report/icons.js: -------------------------------------------------------------------------------- 1 | const ICON_OK = 2 | ` 3 | 4 | 5 | `; 6 | 7 | const ICON_WARNING = 8 | ` 9 | 10 | 11 | `; 12 | 13 | const ICON_WARNING_SMALL = 14 | ` 15 | 16 | 17 | `; 18 | 19 | const ICON_ERROR = 20 | ` 21 | 22 | 23 | `; 24 | 25 | const ICON_ERROR_SMALL = 26 | ` 27 | 28 | 29 | `; 30 | 31 | // const INFO = 32 | // ` 33 | // 34 | // 35 | // `; 36 | 37 | export const ok = ICON_OK; 38 | export const warning = ICON_WARNING; 39 | export const warning_small = ICON_WARNING_SMALL; 40 | export const error = ICON_ERROR; 41 | export const error_small = ICON_ERROR_SMALL; 42 | -------------------------------------------------------------------------------- /src/report/link.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import config from "../config"; 4 | import { workspace } from "vscode"; 5 | import { join } from "path"; 6 | 7 | const BASE_URL = `${ config.options.navigation.scheme }://${ config.options.navigation.authority }/`; 8 | 9 | function localLink(localUrl, name) { 10 | const url = BASE_URL + localUrl; 11 | const href = encodeURI(`command:vscode.previewHtml?${ JSON.stringify(url) }`); 12 | 13 | return `${ name }`; 14 | } 15 | 16 | function fileLineLink(name, file, line) { 17 | const rootPath = workspace.rootPath; 18 | const href = `file://${ join(rootPath, file) }#L${ line }`; 19 | 20 | return `${ name }`; 21 | } 22 | 23 | export default { 24 | localLink, 25 | fileLineLink 26 | }; 27 | -------------------------------------------------------------------------------- /src/report/metric-box.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { round } from "lodash"; 4 | import { template as _template } from "dot"; 5 | import { error, warning } from "./icons"; 6 | 7 | const template = _template(` 8 |
9 |
{{= it.icon }}
10 |
{{= it.value }}
11 |
{{= it.title }}
12 |
13 | `); 14 | 15 | function getTitle(metric) { 16 | const url = metric.infoUrl; 17 | const title = metric.title; 18 | const description = metric.description || ""; 19 | 20 | return url ? 21 | `${ title }` : 22 | `${ title }`; 23 | } 24 | 25 | function isInRange(range, value) { 26 | if (Array.isArray(range)) { 27 | return range[0] <= value && value < range[1]; 28 | } else { 29 | return false; 30 | } 31 | } 32 | 33 | function getIcon(metric, value) { 34 | if (isInRange(metric.errorRange, value)) { 35 | return error; 36 | } else if (isInRange(metric.warningRange, value)) { 37 | return warning 38 | } else { 39 | return ""; 40 | } 41 | } 42 | 43 | /** 44 | * @param {any} options: 45 | * - metric: Metric to show 46 | * - title 47 | * - description (optional) 48 | * - infoUrl (optional) 49 | * - errorRange (optional) 50 | * - warningRange (optional) 51 | * - value: Value of the metric 52 | */ 53 | function MetricBox(options) { 54 | const metric = options.metric; 55 | const value = options.value; 56 | 57 | return template({ 58 | icon: getIcon(metric, value), 59 | value: round(value, 1), 60 | title: getTitle(metric) 61 | }); 62 | } 63 | 64 | export default MetricBox; 65 | -------------------------------------------------------------------------------- /src/report/metric-formatter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { round } from "lodash"; 4 | import { error_small, warning_small } from "./icons.js"; 5 | 6 | function formatMetric(value, warningThreshold, errorThreshold) { 7 | const rounded = round(value, 1); 8 | 9 | if (value > errorThreshold) { 10 | return rounded + " " + error_small; 11 | } else if (value > warningThreshold) { 12 | return rounded + " " + warning_small; 13 | } else { 14 | return rounded; 15 | } 16 | } 17 | 18 | export default { 19 | formatMetric 20 | }; 21 | -------------------------------------------------------------------------------- /src/report/metric-row.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { template as _template } from "dot"; 4 | import metricBox from "./metric-box"; 5 | 6 | const template = _template(` 7 |
{{= it.metrics }}
8 | `); 9 | 10 | function MetricRow(metrics) { 11 | const renderedMetrics = metrics.map(m => metricBox(m)).join(""); 12 | 13 | return template({ 14 | metrics: renderedMetrics 15 | }); 16 | } 17 | 18 | export default MetricRow; 19 | -------------------------------------------------------------------------------- /src/report/project-report.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import HtmlBuilder from "./html-builder"; 4 | import metricRow from "./metric-row"; 5 | import reportStyle from "./report-style"; 6 | import header from "./header"; 7 | import filesTable from "./files-table"; 8 | 9 | const overviewMetrics = { 10 | maintainability: 11 | { 12 | title: "Average Maintainability", 13 | description: "Value between 0 and 100. Represents the relative ease of maintaining the code. A high value means better maintainability.", 14 | errorRange: [0, 10], 15 | warningRange: [10, 20], 16 | infoUrl: "https://blogs.msdn.microsoft.com/zainnab/2011/05/26/code-metrics-maintainability-index/" 17 | }, 18 | loc: 19 | { 20 | title: "Lines of code", 21 | description: "Logical number of source lines of code.", 22 | infoUrl: "https://en.wikipedia.org/wiki/Source_lines_of_code" 23 | } 24 | }; 25 | 26 | function getErrors(errors) { 27 | return errors.join("
"); 28 | } 29 | 30 | function buildProjectSummary(htmlBuilder, analysis, errors) { 31 | const metrics = [ 32 | { metric: overviewMetrics.maintainability, value: analysis.avgMaintainability }, 33 | { metric: overviewMetrics.loc, value: analysis.totalSloc } 34 | ]; 35 | 36 | htmlBuilder 37 | .appendBody(header("Summary")) 38 | .appendBody(metricRow(metrics)) 39 | .appendBody(header("Files")) 40 | .appendBody(filesTable(analysis)); 41 | 42 | if (errors.length > 0) { 43 | htmlBuilder 44 | .appendBody(header("Errors")) 45 | .appendBody(getErrors(errors)); 46 | } 47 | } 48 | 49 | function ProjectReport(analysis, errors) { 50 | function toHtml() { 51 | const htmlBuilder = new HtmlBuilder(); 52 | 53 | htmlBuilder.appendStyle(reportStyle); 54 | 55 | buildProjectSummary(htmlBuilder, analysis, errors); 56 | 57 | return htmlBuilder.toHtml(); 58 | } 59 | 60 | this.toHtml = toHtml; 61 | } 62 | 63 | export default ProjectReport; 64 | -------------------------------------------------------------------------------- /src/report/report-style.js: -------------------------------------------------------------------------------- 1 | const STYLE = 2 | `* { 3 | font-family: Verdana; 4 | } 5 | body { 6 | margin: 5px; 7 | } 8 | h1 { 9 | font-size: 20px; 10 | } 11 | .metric-row { 12 | display: flex; 13 | justify-content: space-around; 14 | flex-wrap: wrap; 15 | } 16 | .metric { 17 | margin: 10px; 18 | width: 260px; 19 | height: 100px; 20 | position: relative; 21 | border: 1px solid #000000; 22 | } 23 | .metric-icon { 24 | position: absolute; 25 | top: 5px; 26 | right: 5px; 27 | } 28 | .metric-title { 29 | font-size: 20px; 30 | text-align: center; 31 | line-height: 30px; 32 | } 33 | .metric-title a { 34 | font-size: 20px; 35 | } 36 | .metric-title span { 37 | font-size: 20px; 38 | color: inherit; 39 | } 40 | .metric-value { 41 | font-size: 40px; 42 | text-align: center; 43 | line-height: 60px; 44 | margin-left: 10px; 45 | } 46 | table { 47 | border-collapse: collapse; 48 | margin:0px; 49 | padding:0px; 50 | } 51 | table td{ 52 | border:1px solid #000000; 53 | padding:7px; 54 | font-weight:normal; 55 | } 56 | table thead td{ 57 | font-weight:bold; 58 | } 59 | a { 60 | color: inherit; 61 | } 62 | a:hover { 63 | cursor: pointer; 64 | text-decoration: underline; 65 | } 66 | `; 67 | 68 | export default STYLE; 69 | -------------------------------------------------------------------------------- /src/report/table.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { template } from "dot"; 4 | 5 | const tableTemplate = template(` 6 | 7 | {{= it.header }} 8 | {{= it.body }} 9 |
10 | `); 11 | 12 | const rowTemplate = template(`{{= it.cells }}`); 13 | 14 | const cellTemplate = template(`{{= it.value }}`); 15 | 16 | function getAlign(column) { 17 | return column.align || "left"; 18 | } 19 | 20 | function getCell(align, value) { 21 | return cellTemplate({ align, value }); 22 | } 23 | 24 | function buildHeader(columns) { 25 | const buildCells = () => 26 | columns.map((col, i) => getCell(getAlign(col), col.title)).join(""); 27 | 28 | return rowTemplate({ cells: buildCells() }); 29 | } 30 | 31 | function buildRows(rows, columns) { 32 | const buildCells = cells => 33 | cells.map((cell, i) => getCell(getAlign(columns[i]), cell)); 34 | const buildRow = row => rowTemplate({ cells: buildCells(row).join("") }); 35 | 36 | return rows.map(buildRow).join(""); 37 | } 38 | 39 | class HtmlTable { 40 | constructor(options) { 41 | this.columns = options.columns; 42 | this.rows = options.rows; 43 | } 44 | 45 | toHtml() { 46 | return tableTemplate({ 47 | header: buildHeader(this.columns), 48 | body: buildRows(this.rows, this.columns) 49 | }); 50 | } 51 | } 52 | 53 | export default HtmlTable; 54 | -------------------------------------------------------------------------------- /src/utils/workspace.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import config from "../config"; 4 | import { workspace } from "vscode"; 5 | 6 | /** 7 | * Returns files in the workspace by taking include and 8 | * exclude pattern into consideration 9 | * 10 | * @returns {Uri} 11 | */ 12 | function getWorkspaceFiles() { 13 | return config.getIncludeExclude() 14 | .then(({ include, exclude }) => { 15 | const includePattern = _createGlob(include); 16 | const excludePattern = _createGlob(exclude); 17 | 18 | return workspace.findFiles(includePattern, excludePattern); 19 | }) 20 | .then(files => files.map(fileUri => ({ 21 | fsPath: fileUri.fsPath, 22 | relativePath: workspace.asRelativePath(fileUri) 23 | }))); 24 | } 25 | 26 | function _createGlob(patterns) { 27 | switch (patterns.length) { 28 | case 0: 29 | return ""; 30 | case 1: 31 | return patterns[0]; 32 | default: 33 | return `{${ patterns.join(",") }}`; 34 | } 35 | }; 36 | 37 | export default { 38 | getWorkspaceFiles 39 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "moduleResolution": "node", 5 | "module": "commonjs", 6 | "target": "esnext", 7 | "lib": ["esnext"], 8 | "strict": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "allowJs": true, 12 | "outDir": "out" 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | ".vscode-test" 17 | ], 18 | "include": ["src/**/*.ts", "src/**/*.js"], 19 | } 20 | -------------------------------------------------------------------------------- /typings/node.d.ts: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /typings/vscode-typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | --------------------------------------------------------------------------------