From 20b133e3efb2b9c4e9d9302bc73bba03dd341325 Mon Sep 17 00:00:00 2001 From: Prokop Simek Date: Mon, 23 Sep 2019 16:06:43 +0200 Subject: [PATCH 1/7] docs(readme): remove libraries.io --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 95941d598..0f8965a38 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ [![Version](https://img.shields.io/npm/v/dx-scanner.svg)](https://npmjs.org/package/dx-scanner) [![Travis (.org)](https://img.shields.io/travis/DXHeroes/dx-scanner)](https://travis-ci.org/DXHeroes/dx-scanner) ![Codecov](https://img.shields.io/codecov/c/github/DXHeroes/dx-scanner) -![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/DXHeroes/dx-scanner) ![last commit](https://img.shields.io/github/last-commit/DXHeroes/dx-scanner) ![GitHub commit activity](https://img.shields.io/github/commit-activity/w/DXHeroes/dx-scanner) [![Downloads/week](https://img.shields.io/npm/dw/dx-scanner.svg)](https://npmjs.org/package/dx-scanner) From 66c13cd385d61760923cbe6b299607b3430c48dc Mon Sep 17 00:00:00 2001 From: Kristyna Slomkova <47568174+Kristynaslomkova@users.noreply.github.com> Date: Mon, 23 Sep 2019 16:28:01 +0200 Subject: [PATCH 2/7] Readme Updates (#54) --- CONTRIBUTING.md | 59 +++++++++++++++++++++++++++++-------------------- README.md | 15 +++++++------ 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3989aa272..c09b03418 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,55 +1,66 @@ # Contributing to DX Scanner -It seems you want to participate on DX Scanner - that's great! We welcome contributions to our [open source project on GitHub](http://github.com/DXHeroes/dx-scanner). +It seems like you want to participate on our DX Scanner - that's great! We welcome contributions to our [open source project on GitHub](http://github.com/DXHeroes/dx-scanner). ## Introduction -We're glad you're interested in DX Scanner in the way of contributing. We value the pro-community developers as you are. + +We are glad that you are interested in DX Scanner in the way of contributing. We value the pro-community developers as you are. ## Help the community -1) Repport an Error or Bug πŸ› + +1) Report an Error or a Bug πŸ› 2) Request a Feature πŸ†• -3) Contribute Code πŸ‘¨β€πŸ’»πŸ‘©β€πŸ’» -4) Contribute Documentation πŸ“ +3) Contribute to the Code πŸ‘¨β€πŸ’»πŸ‘©β€πŸ’» +4) Contribute to the Documentation πŸ“ 5) Provide Support on Issues ℹ️ -## Need a help? +## Need help? -If you have any question about this project, how to use it, or just need clarification about anything open an Issue at https://github.com/DXHeroes/dx-scanner/issues . +If you have any question about this project (for example, how to use it) or if you just need some clarification about anything, please open an Issue at [Issues](https://github.com/DXHeroes/dx-scanner/issues). ## Contributing -1. **Fork & Clone** the repository -2. **Setup** the DX Scanner - - Install Yarn with `npm i -g yarn` if not yet installed - - Install packages with `yarn install` - - Run with `yarn start` - - Build with `yarn build` - - Run tests with `yarn test` - - Lint code with `yarn lint` or `yarn lint:fix` with autofixer -3. **Commit** changes to your own branch -4. **Push** your work back up to your fork -5. Submit a **Pull Request** so that we can review your changes +Follow these steps: + +1. **Fork & Clone** the repository +2. **Setup** the DX Scanner + - Install Yarn with `npm i -g yarn` if not yet installed + - Install packages with `yarn install` + - Run with `yarn start` + - Build with `yarn build` + - Run tests with `yarn test` + - Lint code with `yarn lint` or `yarn lint:fix` with autofixer +3. **Commit** changes to your own branch +4. **Push** your work back up to your fork +5. Submit a **Pull Request** so that we can review your changes -NOTE: Be sure to merge the latest from "upstream" before making a pull request. +**NOTE: Be sure to merge the latest from "upstream" before making a pull request.** ## Contribute Code + ### Practices -Practices check your repository for using tooling, which improves product development. + +Check your repository for using the correct tooling which improves product development. #### JavaScript/TypeScript -We have more than 10 JS/TS practices for now! If you want to add more, you're more than welcome. Check [JavaScript Practices](https://github.com/DXHeroes/dx-scanner/tree/master/src/practices/JavaScript) to see, what's the pattern of implementation of your own practice. + +We have more than 10 JS/TS practices for now! If you want to add more, you are more than welcome. Check [JavaScript Practices](https://github.com/DXHeroes/dx-scanner/tree/master/src/practices/JavaScript) to see what is the pattern of implementation of your own practice. #### Other Languages -Other languages are not supported yet. If you want to contribute, check [Practices](https://github.com/DXHeroes/dx-scanner/tree/master/src/practices). Get inspired by [Javascript Practices](https://github.com/DXHeroes/dx-scanner/tree/master/src/practices/JavaScript) implementation. + +Other languages are not supported yet. If you want to contribute, see [Practices](https://github.com/DXHeroes/dx-scanner/tree/master/src/practices). Get inspired by [Javascript Practices](https://github.com/DXHeroes/dx-scanner/tree/master/src/practices/JavaScript) implementation. ### Inspectors -Inspectors indirectly works with Git code hosting providers APIs. They use common interfaces provided by services so you don't have to which API to use. + +Inspectors indirectly work with Git code hosting providers APIs. They use common interfaces provided by services so you do not have to know which API to use. ### Services + There is a [File System Service](https://github.com/DXHeroes/dx-scanner/tree/master/src/services) working with files. #### Git -Services directly use Git code hosting providers APIs while checking the rate limits. They convert API responses to the own interface, so Inspectors can use them. Only the GitHub Service is implemented for now. If you need e.g. GitLab Service, you can contribute! Get inspired by [GitHub Service](https://github.com/DXHeroes/dx-scanner/blob/master/src/services/git/GitHubService.ts) + +Services directly use Git code hosting providers APIs while checking the rate limits. They convert API responses to their own interface so Inspectors can use them. Only the GitHub Service is implemented so far. If you need for example, GitLab Service, you can contribute! Get inspired by [GitHub Service](https://github.com/DXHeroes/dx-scanner/blob/master/src/services/git/GitHubService.ts) ## Copyright and Licensing diff --git a/README.md b/README.md index 0f8965a38..422197670 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,10 @@ # DX Scanner -DX Scanner is an open-source library that allows you to β€œmeasure” Developer Experience directly based on your source code and recommend practices to adopt that will help you to improve your product development. +DX Scanner is an open source library that allows you to β€œmeasure” Developer Experience directly based on your source code. DX Scanner recommends practices that can help you with improving your product development. + +## What language is supported? -### Which languages are supported? Language | Supported ------------ | ------------- JavaScript/TypeScript | βœ… @@ -63,10 +64,10 @@ dxs [path] ``` ## Configuration βš™οΈ -Add ```dxscannerrc.*``` config file to change default configuration. It can be a ```.json```, ```.yml``` even dotfile! +Add ```dxscannerrc.*``` config file to change default configuration. It can be a ```.json```, ```.yml```, and even a dotfile! **Practices** -You can switch off practices you don't want to scan or change its impact. Use the id of the practice. +You can switch off practices you do not want to scan or change its impact. Use the id of the practice. Possible impact: ``` @@ -92,7 +93,7 @@ Example : ``` ## Contributing πŸ‘©β€πŸ’» πŸ‘¨β€πŸ’» -Feel free to contribute to the DX Scanner. If you want to contribute, please follow our [Contribution Guide](CONTRIBUTING.md). +Feel free to contribute to our DX Scanner. Please follow the [Contribution Guide](CONTRIBUTING.md). ## License πŸ“ @@ -100,7 +101,7 @@ The DX Scanner open source project is licensed under the [Attribution-NonCommerc ## Contributors ✨ -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): +Many thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): @@ -119,4 +120,4 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Any kind of contributions are welcome! From 33163c384698b58286dc885323a9896d2ed5df79 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2019 16:37:31 +0200 Subject: [PATCH 3/7] chore(deps): update dependency nock to v11 (#44) * chore(deps): update dependency nock to v11 * chore: update nock typings --- package.json | 3 +-- test/helpers/gitHubNock.ts | 10 ++++++--- tsconfig.json | 2 +- yarn.lock | 42 ++++++++++---------------------------- 4 files changed, 20 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 16e09a479..d9168b34c 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,6 @@ "@types/glob": "^7.1.1", "@types/jest": "^24.0.18", "@types/lodash": "^4.14.137", - "@types/nock": "^10.0.3", "@types/node": "^12", "@types/rimraf": "^2.0.2", "@types/semver": "^6.0.1", @@ -68,7 +67,7 @@ "eslint-config-prettier": "^6.1.0", "eslint-plugin-prettier": "^3.1.0", "jest": "^24.9.0", - "nock": "^10.0.6", + "nock": "^11.0.0", "prettier": "^1.18.2", "rimraf": "^3.0.0", "semantic-release": "^15.13.21", diff --git a/test/helpers/gitHubNock.ts b/test/helpers/gitHubNock.ts index 7f8185dce..51724b699 100644 --- a/test/helpers/gitHubNock.ts +++ b/test/helpers/gitHubNock.ts @@ -77,7 +77,7 @@ export class GitHubNock { persist = true, ): T { const url = this.repository.pulls_url.replace('{/number}', number !== undefined ? `/${number}` : ''); - const params: nock.POJO = {}; + const params: nock.DataMatcherMap = {}; if (state !== undefined) { params.state = state; } @@ -96,7 +96,7 @@ export class GitHubNock { getIssues(number?: number, persist = true): nock.Interceptor { const url = this.repository.issues_url.replace('{/number}', number !== undefined ? `/${number}` : ''); - const params: nock.POJO = {}; + const params: nock.DataMatcherMap = {}; return GitHubNock.get(url, params, persist); } @@ -105,7 +105,7 @@ export class GitHubNock { return GitHubNock.get(this.repository.url + suffix, {}, persist); } - private static get(url: string, params: nock.POJO, persist = true): nock.Interceptor { + private static get(url: string, params: nock.DataMatcherMap, persist = true): nock.Interceptor { const urlObj = new URL(url); const scope = nock(urlObj.origin); @@ -117,6 +117,10 @@ export class GitHubNock { if (Object.keys(params)) { interceptor.query(params); } + // nock('http://example.com') + // .get('/') + // .query('/') + // .reply(500, 'hello world'); return interceptor; } } diff --git a/tsconfig.json b/tsconfig.json index c9488f76f..2521bd46b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -60,6 +60,6 @@ "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, "preserveConstEnums": true }, - "include": ["src/**/*", "typings/**/*", "test/helpers", "test/**/*"], + "include": ["src/**/*", "typings/**/*", "test/helpers/**/*", "test/**/*"], "exclude": ["node_modules", "lib"] } diff --git a/yarn.lock b/yarn.lock index 459bbf1dd..b088d394d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -728,13 +728,6 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/nock@^10.0.3": - version "10.0.3" - resolved "https://registry.yarnpkg.com/@types/nock/-/nock-10.0.3.tgz#dab1d18ffbccfbf2db811dab9584304eeb6e1c4c" - integrity sha512-OthuN+2FuzfZO3yONJ/QVjKmLEuRagS9TV9lEId+WHL9KhftYG+/2z+pxlr0UgVVXSpVD8woie/3fzQn8ft/Ow== - dependencies: - "@types/node" "*" - "@types/node@*": version "12.0.10" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.10.tgz#51babf9c7deadd5343620055fc8aff7995c8b031" @@ -2259,11 +2252,6 @@ deep-eql@^3.0.1: dependencies: type-detect "^4.0.0" -deep-equal@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -5206,7 +5194,7 @@ lodash.without@~4.4.0: resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.2, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.5.1: +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.5.1: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== @@ -5695,20 +5683,17 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nock@^10.0.6: - version "10.0.6" - resolved "https://registry.yarnpkg.com/nock/-/nock-10.0.6.tgz#e6d90ee7a68b8cfc2ab7f6127e7d99aa7d13d111" - integrity sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w== +nock@^11.0.0: + version "11.3.5" + resolved "https://registry.yarnpkg.com/nock/-/nock-11.3.5.tgz#f2c7b4b672a04c35342593b6bfd821497881199c" + integrity sha512-6WGeZcWc3RExkBcMSYSrUm/5YukDo52m/jhwniQyrnuiCnKRljBwwje9vTwJyEi4J6m2bq0Aj6C1vzuM6iuaeg== dependencies: chai "^4.1.2" debug "^4.1.0" - deep-equal "^1.0.0" json-stringify-safe "^5.0.1" - lodash "^4.17.5" + lodash "^4.17.13" mkdirp "^0.5.0" - propagate "^1.0.0" - qs "^6.5.1" - semver "^5.5.0" + propagate "^2.0.0" node-alias@^1.0.4: version "1.0.4" @@ -6760,10 +6745,10 @@ promzard@^0.3.0: dependencies: read "1" -propagate@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/propagate/-/propagate-1.0.0.tgz#00c2daeedda20e87e3782b344adba1cddd6ad709" - integrity sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk= +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== proto-list@~1.2.1: version "1.2.4" @@ -6842,11 +6827,6 @@ qrcode-terminal@^0.12.0: resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819" integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== -qs@^6.5.1: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" From fe30ba53f87f1f1feddb48d0ab87c9069e950ad9 Mon Sep 17 00:00:00 2001 From: Prokop Simek Date: Mon, 23 Sep 2019 16:49:10 +0200 Subject: [PATCH 4/7] chore: cleanup --- test/helpers/gitHubNock.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/helpers/gitHubNock.ts b/test/helpers/gitHubNock.ts index 51724b699..fcbe89063 100644 --- a/test/helpers/gitHubNock.ts +++ b/test/helpers/gitHubNock.ts @@ -117,10 +117,6 @@ export class GitHubNock { if (Object.keys(params)) { interceptor.query(params); } - // nock('http://example.com') - // .get('/') - // .query('/') - // .reply(500, 'hello world'); return interceptor; } } From c5a405d56894970bf8caa8ef496dbdc21942931a Mon Sep 17 00:00:00 2001 From: Prokop Simek Date: Mon, 23 Sep 2019 16:51:31 +0200 Subject: [PATCH 5/7] Chore/update dependencies (#55) * chore: update nock typings * chore: update renovate config --- .gitlab-ci.yml | 18 ------------------ renovate.json => .renovaterc | 4 ++-- 2 files changed, 2 insertions(+), 20 deletions(-) delete mode 100644 .gitlab-ci.yml rename renovate.json => .renovaterc (61%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 2b612642f..000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,18 +0,0 @@ -cache: - paths: - - node_modules/ - -stages: - - test - -job_lint_and_test: - image: node - stage: test - before_script: - - yarn - - yarn build - script: - - ./bin/run --version - - ./bin/run --help - - yarn lint - - yarn test --coverage diff --git a/renovate.json b/.renovaterc similarity index 61% rename from renovate.json rename to .renovaterc index 949330f8e..5e544d43d 100644 --- a/renovate.json +++ b/.renovaterc @@ -1,8 +1,8 @@ { "extends": [ - "config:base" + "config:base", + "config:preserveSemverRanges" ], - "rangeStrategy": "replace", "automerge": true, "major": { "automerge": false From 183de6672e44a79ef22b83399fdd8c42523267a5 Mon Sep 17 00:00:00 2001 From: Prokop Simek Date: Mon, 23 Sep 2019 16:53:00 +0200 Subject: [PATCH 6/7] chore: fix renovate config --- .renovaterc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.renovaterc b/.renovaterc index 5e544d43d..132158ec6 100644 --- a/.renovaterc +++ b/.renovaterc @@ -1,7 +1,7 @@ { "extends": [ "config:base", - "config:preserveSemverRanges" + ":preserveSemverRanges" ], "automerge": true, "major": { From 721d78001ff658d2bd9bcbb6e893b6ce7ec88f87 Mon Sep 17 00:00:00 2001 From: Adela Homolova <53510747+adelkahomolova@users.noreply.github.com> Date: Tue, 24 Sep 2019 13:54:09 +0200 Subject: [PATCH 7/7] Chore: fix display remote url and split CLI report for components (#52) * WIP: Fix display remote url. The reasons was the JavaScriptComponentDetector, that was fixed. Trying to figure out how to add path for component. * WIP: Display split report for each component. * report: show split CLI report for each component * fix: saving ProjectComponentPlatform and tests for JavaScriptComponentDetector. * fix: show the local path even if the repositoryPath is present * fix: path in reports * fix: Reflect every package - use regex * Fix: allow regex in hasPackage() because of EslintUsedPractice * fix: remove dx-scanner package --- .../JavaScriptComponentDetector.spec.ts | 4 +- .../JavaScript/JavaScriptComponentDetector.ts | 34 +++--- src/inspectors/IPackageInspector.ts | 2 +- .../package/PackageInspectorBase.ts | 12 ++- src/inversify.config.ts | 1 + src/lib/assertNever.ts | 5 + .../JavaScript/ESLintUsedPractice.ts | 3 +- src/reporters/CLIReporter.ts | 102 ++++++++++-------- src/services/git/Git.ts | 8 +- src/services/git/GitHubUrlParser.ts | 12 --- src/services/git/GitServiceUtils.ts | 36 +++++++ src/services/git/model.ts | 4 + src/types.ts | 1 + 13 files changed, 133 insertions(+), 91 deletions(-) create mode 100644 src/lib/assertNever.ts delete mode 100644 src/services/git/GitHubUrlParser.ts create mode 100644 src/services/git/GitServiceUtils.ts diff --git a/src/detectors/JavaScript/JavaScriptComponentDetector.spec.ts b/src/detectors/JavaScript/JavaScriptComponentDetector.spec.ts index e0c906806..dc3b061a0 100644 --- a/src/detectors/JavaScript/JavaScriptComponentDetector.spec.ts +++ b/src/detectors/JavaScript/JavaScriptComponentDetector.spec.ts @@ -41,7 +41,7 @@ describe('JavaScriptComponentDetector', () => { mockJsPackageInspector.packages = [mockPackage('webpack')]; mockJsPackageInspector.hasOneOfPackages = (packages: string[]) => { - const files = packages.filter((file) => file === 'express'); + const files = packages.filter((file) => file === 'webpack'); if (files.length > 0) { return true; } @@ -64,6 +64,6 @@ describe('JavaScriptComponentDetector', () => { detector = new JavaScriptComponentDetector(mockJsPackageInspector); const components = await detector.detectComponent({ language: ProgrammingLanguage.JavaScript, path: './src' }); - expect(components.length).toEqual(0); + expect(components[0].platform).toEqual(ProjectComponentPlatform.UNKNOWN); }); }); diff --git a/src/detectors/JavaScript/JavaScriptComponentDetector.ts b/src/detectors/JavaScript/JavaScriptComponentDetector.ts index ef1341db4..d5d6c00f6 100644 --- a/src/detectors/JavaScript/JavaScriptComponentDetector.ts +++ b/src/detectors/JavaScript/JavaScriptComponentDetector.ts @@ -30,32 +30,24 @@ export class JavaScriptComponentDetector implements IProjectComponentDetector { const frontendPackages = ['webpack', 'jquery', 'gulp', 'grunt', 'browserify', 'babel']; + let frontendOrBackend; if (this.packageInspector.hasOneOfPackages(backendPackages)) { - return [ - { - framework: ProjectComponentFramework.UNKNOWN, - language: langAtPath.language, - path: langAtPath.path, - platform: ProjectComponentPlatform.BackEnd, - repositoryPath: undefined, - type: ProjectComponentType.Application, - }, - ]; + frontendOrBackend = ProjectComponentPlatform.BackEnd; } if (this.packageInspector.hasOneOfPackages(frontendPackages)) { - return [ - { - framework: ProjectComponentFramework.UNKNOWN, - language: langAtPath.language, - path: langAtPath.path, - platform: ProjectComponentPlatform.FrontEnd, - repositoryPath: undefined, - type: ProjectComponentType.Application, - }, - ]; + frontendOrBackend = ProjectComponentPlatform.FrontEnd; } - return []; + return [ + { + framework: ProjectComponentFramework.UNKNOWN, + language: langAtPath.language, + path: langAtPath.path, + platform: frontendOrBackend ? frontendOrBackend : ProjectComponentPlatform.UNKNOWN, + repositoryPath: undefined, + type: ProjectComponentType.Application, + }, + ]; } } diff --git a/src/inspectors/IPackageInspector.ts b/src/inspectors/IPackageInspector.ts index 41a8aca84..62dec24c2 100644 --- a/src/inspectors/IPackageInspector.ts +++ b/src/inspectors/IPackageInspector.ts @@ -3,7 +3,7 @@ export interface IPackageInspector { init(): Promise; hasPackageManagement(): boolean; findPackages(searchTerm: string | RegExp): Package[]; - hasPackage(name: string, options?: PackageOptions): boolean; + hasPackage(name: string | RegExp, options?: PackageOptions): boolean; findPackage(name: string, options?: PackageOptions): Package | undefined; hasOneOfPackages(packages: string[]): boolean; hasLockfile(): boolean | undefined; diff --git a/src/inspectors/package/PackageInspectorBase.ts b/src/inspectors/package/PackageInspectorBase.ts index 1d18c4436..cdccf245d 100644 --- a/src/inspectors/package/PackageInspectorBase.ts +++ b/src/inspectors/package/PackageInspectorBase.ts @@ -25,13 +25,19 @@ export abstract class PackageInspectorBase implements IPackageInspector, IInitia throw new Error('Method not implemented.'); } - hasPackage(name: string, options?: PackageOptions | undefined): boolean { + hasPackage(name: string | RegExp, options?: PackageOptions | undefined): boolean { if (!this.packages) { return false; } for (const pkg of this.packages) { - if (pkg.name.toLowerCase() === name.toLowerCase()) { - return true; + if (typeof name === 'string') { + if (pkg.name.toLowerCase() === name.toLowerCase()) { + return true; + } + } else { + if (name.test(pkg.name.toLowerCase())) { + return true; + } } } return false; diff --git a/src/inversify.config.ts b/src/inversify.config.ts index 4306588cc..afcf0a03a 100644 --- a/src/inversify.config.ts +++ b/src/inversify.config.ts @@ -27,6 +27,7 @@ export const createRootContainer = (args: ArgumentsProvider): Container => { bindScanningStrategyDetectors(container); bindScanningContext(container); args.json ? container.bind(Types.IReporter).to(JSONReporter) : container.bind(Types.IReporter).to(CLIReporter); + container.bind(Types.JSONReporter).to(JSONReporter); container.bind(Types.ArgumentsProvider).toConstantValue(args); container.bind(Scanner).toSelf(); container.bind(FileSystemService).toSelf(); diff --git a/src/lib/assertNever.ts b/src/lib/assertNever.ts new file mode 100644 index 000000000..9dd93eddd --- /dev/null +++ b/src/lib/assertNever.ts @@ -0,0 +1,5 @@ +import { ErrorFactory } from "./errors"; + +export const assertNever = (x: never): never => { + throw ErrorFactory.newInternalError("Unexpected object: " + x); +} diff --git a/src/practices/JavaScript/ESLintUsedPractice.ts b/src/practices/JavaScript/ESLintUsedPractice.ts index 9331c3ad7..7857c9188 100644 --- a/src/practices/JavaScript/ESLintUsedPractice.ts +++ b/src/practices/JavaScript/ESLintUsedPractice.ts @@ -20,7 +20,8 @@ export class ESLintUsedPractice implements IPractice { async evaluate(ctx: PracticeContext): Promise { if (ctx.packageInspector) { - if (ctx.packageInspector.hasPackage('eslint')) { + const eslintRegex = new RegExp('eslint'); + if (ctx.packageInspector.hasPackage(eslintRegex)) { return PracticeEvaluationResult.practicing; } else { return PracticeEvaluationResult.notPracticing; diff --git a/src/reporters/CLIReporter.ts b/src/reporters/CLIReporter.ts index 7e14653e7..e793c07c3 100644 --- a/src/reporters/CLIReporter.ts +++ b/src/reporters/CLIReporter.ts @@ -1,28 +1,25 @@ -import { Color, blue, bold, green, grey, italic, red, reset, yellow, underline } from 'colors'; -import { PracticeAndComponent, PracticeImpact } from '../model'; -import { GitHubUrlParser } from '../services/git/GitHubUrlParser'; -import { IReporter } from './IReporter'; -import { injectable } from 'inversify'; -import { uniq, compact } from 'lodash'; +import { blue, bold, Color, green, grey, italic, red, reset, underline, yellow } from 'colors'; +import { inject, injectable } from 'inversify'; +import { PracticeAndComponent, PracticeImpact, PracticeMetadata } from '../model'; import { IPracticeWithMetadata } from '../practices/DxPracticeDecorator'; +import { Types } from '../types'; +import { ComponentReport, IReporter } from './IReporter'; +import { JSONReporter } from './JSONReporter'; +import { sharedSubpath } from '../detectors/utils'; +import { GitServiceUtils } from '../services/git/GitServiceUtils'; @injectable() export class CLIReporter implements IReporter { + private readonly JSONReporter: JSONReporter; + + constructor(@inject(Types.JSONReporter) JSONReporter: JSONReporter) { + this.JSONReporter = JSONReporter; + } + report(practicesAndComponents: PracticeAndComponent[], practicesOff: IPracticeWithMetadata[]): string { const lines: string[] = []; - const repoNames = uniq( - compact( - practicesAndComponents.map((pac): string | undefined => { - if (pac.component.repositoryPath) { - const git = GitHubUrlParser.getOwnerAndRepoName(pac.component.repositoryPath); - return `${git.owner}/${git.repoName}`; - } - - return pac.component.path; - }), - ), - ); + const report = this.JSONReporter.report(practicesAndComponents, practicesOff); lines.push(bold(blue('----------------------------'))); lines.push(bold(blue('| |'))); @@ -30,27 +27,39 @@ export class CLIReporter implements IReporter { lines.push(bold(blue('| |'))); lines.push(bold(blue('----------------------------'))); - lines.push(bold(blue('Developer Experience Report for:'))); - repoNames.forEach((repoName) => { + let repoName; + const componentsSharedSubpath = sharedSubpath(report.components.map((c) => c.path)); + + for (const component of report.components) { + lines.push('\n----------------------------\n'); + lines.push(bold(blue('Developer Experience Report for:'))); + + if (component.repositoryPath) { + repoName = GitServiceUtils.getUrlToRepo(component.repositoryPath, component.path.replace(componentsSharedSubpath, '')); + } else { + repoName = component.path; + } lines.push(repoName); - }); + lines.push('\n----------------------------'); - lines.push('\n----------------------------'); - for (const key in PracticeImpact) { - const impact = PracticeImpact[key]; + for (const key in PracticeImpact) { + const impact = PracticeImpact[key]; - const impactLine = this.emitImpactSegment(practicesAndComponents, impact); - impactLine && lines.push(impactLine); + const impactLine = this.emitImpactSegment(component, impact); + impactLine && lines.push(impactLine); + } } lines.push('----------------------------'); lines.push(''); + practicesOff.length === 0 - ? lines.push(bold(yellow('No practice was switched off.'))) + ? lines.push(bold(yellow('No practices were switched off.'))) : lines.push(bold(red('You switched off these practices:'))); for (const practice of practicesOff) { lines.push(red(`- ${italic(practice.getMetadata().name)}`)); } + lines.push(''); lines.push('----------------------------'); lines.push(''); @@ -61,14 +70,17 @@ export class CLIReporter implements IReporter { return lines.join('\n'); } - private emitImpactSegment(practicesAndComponents: PracticeAndComponent[], impact: PracticeImpact): string | undefined { + private emitImpactSegment(component: ComponentReport, impact: PracticeImpact): string | undefined { const lines: string[] = []; - practicesAndComponents = practicesAndComponents.filter((pac) => pac.practice.impact === impact); - if (practicesAndComponents.length === 0) { + + const practices = component.practices.filter((practice) => practice.impact === impact); + if (practices.length === 0) { return undefined; } + lines.push(reset('')); let color = blue; + if (impact === PracticeImpact.high) { color = red; lines.push(bold(color('Improvements with highest impact:\n'))); @@ -82,39 +94,35 @@ export class CLIReporter implements IReporter { color = grey; lines.push(bold(color('Also consider:'))); } - for (const pac of practicesAndComponents) { - lines.push(this.linesForPractice(pac, color, practicesAndComponents.length > 1)); - if (pac.practice.defaultImpact !== pac.practice.impact) { - lines.push(bold(this.changedImpact(pac, (color = grey)))); + + for (const practice of practices) { + lines.push(this.linesForPractice(practice, color)); + + if (practice.defaultImpact !== practice.impact) { + lines.push(bold(this.changedImpact(practice, (color = grey)))); } } + lines.push(bold('')); return lines.join('\n'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - private linesForPractice(pac: PracticeAndComponent, color: Color, _includeFindingPath: boolean): string { + private linesForPractice(practice: PracticeMetadata, color: Color): string { const findingPath = ''; - // if (includeFindingPath) { - // const ownerAndRepo = GitHubUrlParser.getOwnerAndRepoName(pac.component.githubUrl!); - // findingPath = `at: ${ownerAndRepo.owner}/${ownerAndRepo.repoName}`; - // } - - const practiceLineTexts = [reset(color(`- ${bold(pac.practice.name)} - ${italic(pac.practice.suggestion)}`))]; - if (pac.practice.url) { - practiceLineTexts.push(color(italic(`${findingPath}(${pac.practice.url})`))); + const practiceLineTexts = [reset(color(`- ${bold(practice.name)} - ${italic(practice.suggestion)}`))]; + if (practice.url) { + practiceLineTexts.push(color(italic(`${findingPath}(${practice.url})`))); } return practiceLineTexts.join(' '); } - private changedImpact(pac: PracticeAndComponent, color: Color) { + private changedImpact(practice: PracticeMetadata, color: Color) { const practiceLineTexts = [ reset( color( - `You changed impact of ${bold(pac.practice.name)} from ${underline(pac.practice.defaultImpact)} to ${underline( - pac.practice.impact, - )}`, + `You changed impact of ${bold(practice.name)} from ${underline(practice.defaultImpact)} to ${underline(practice.impact)}`, ), ), ]; diff --git a/src/services/git/Git.ts b/src/services/git/Git.ts index d2bfac1b4..4760684d4 100644 --- a/src/services/git/Git.ts +++ b/src/services/git/Git.ts @@ -1,5 +1,4 @@ import { Repository } from '../../model'; -import { GitHubUrlParser } from './GitHubUrlParser'; import { isArray } from 'util'; import { inject, injectable } from 'inversify'; import { ErrorFactory } from '../../lib/errors/ErrorFactory'; @@ -9,6 +8,7 @@ import { Metadata, MetadataType, IProjectFilesBrowserService } from '../model'; import { Types } from '../../types'; import { ProjectIssueBrowserService as ContentRepositoryBrowserService } from '../../model'; import { Directory, File, Symlink } from './model'; +import { GitServiceUtils } from './GitServiceUtils'; @injectable() export class Git implements IProjectFilesBrowserService { @@ -109,12 +109,12 @@ export class Git implements IProjectFilesBrowserService { } async getContributorCount(): Promise { - const params = GitHubUrlParser.getOwnerAndRepoName(this.repository.url); + const params = GitServiceUtils.getOwnerAndRepoName(this.repository.url); return this.service.getContributors(params.owner, params.repoName).then((r) => r.totalCount); } async getPullRequestCount(): Promise { - const params = GitHubUrlParser.getOwnerAndRepoName(this.repository.url); + const params = GitServiceUtils.getOwnerAndRepoName(this.repository.url); return this.service.getPullRequests(params.owner, params.repoName, { filter: { state: GitHubPullRequestState.all } }).then((r) => { if (!r) { throw ErrorFactory.newInternalError('Could not get pull requests'); @@ -124,7 +124,7 @@ export class Git implements IProjectFilesBrowserService { } private getRepoContent(path: string): Promise { - const params = GitHubUrlParser.getOwnerAndRepoName(this.repository.url); + const params = GitServiceUtils.getOwnerAndRepoName(this.repository.url); return this.service.getRepoContent(params.owner, params.repoName, path); } diff --git a/src/services/git/GitHubUrlParser.ts b/src/services/git/GitHubUrlParser.ts deleted file mode 100644 index 2624c2ee7..000000000 --- a/src/services/git/GitHubUrlParser.ts +++ /dev/null @@ -1,12 +0,0 @@ -import gitUrlParse from 'git-url-parse'; - -export class GitHubUrlParser { - static getOwnerAndRepoName(url: string): { owner: string; repoName: string } { - const parsedUrl = gitUrlParse(url); - - return { - owner: parsedUrl.owner, - repoName: parsedUrl.name, - }; - } -} diff --git a/src/services/git/GitServiceUtils.ts b/src/services/git/GitServiceUtils.ts new file mode 100644 index 000000000..89358a6d0 --- /dev/null +++ b/src/services/git/GitServiceUtils.ts @@ -0,0 +1,36 @@ +import gitUrlParse from 'git-url-parse'; +import { GitService } from './model'; +import { assertNever } from '../../lib/assertNever'; + +export class GitServiceUtils { + static getUrlToRepo = (url: string, path?: string | undefined, branch = 'master') => { + const parsedUrl = gitUrlParse(url); + + let completeUrl = parsedUrl.toString('https'); + + if (path) { + completeUrl += GitServiceUtils.getPath(parsedUrl.source, path, branch || parsedUrl.ref); + } + + return completeUrl; + }; + + static getOwnerAndRepoName = (url: string) => { + const parsedUrl = gitUrlParse(url); + + return { + owner: parsedUrl.owner, + repoName: parsedUrl.name, + }; + }; + + static getPath = (service: GitService, path: string, branch = 'master') => { + switch (service) { + case GitService.github: + return `/tree/${branch}${path}`; + + default: + return assertNever(service); + } + }; +} diff --git a/src/services/git/model.ts b/src/services/git/model.ts index bd2364b73..186049006 100644 --- a/src/services/git/model.ts +++ b/src/services/git/model.ts @@ -1,3 +1,7 @@ +export enum GitService { + github = 'github.com', +} + export interface UserInfo { login: string; id: number; diff --git a/src/types.ts b/src/types.ts index d45482859..b32d64963 100644 --- a/src/types.ts +++ b/src/types.ts @@ -44,6 +44,7 @@ export const Types = { Practice: Symbol('Practice'), IReporter: Symbol('IReporter'), ConfigProvider: Symbol('ConfigProvider'), + JSONReporter: Symbol('JSONReporter'), }; export type GitFactory = (repository: Repository) => Git;