diff --git a/.github/workflows/test-and-deploy-to-npm.yml b/.github/workflows/test-and-deploy-to-npm.yml index 80f5d2b..486ca8a 100644 --- a/.github/workflows/test-and-deploy-to-npm.yml +++ b/.github/workflows/test-and-deploy-to-npm.yml @@ -18,6 +18,10 @@ jobs: - uses: actions/checkout@v4 with: submodules: true + - run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + - uses: actions/setup-node@v4 with: node-version: '18.17.1' diff --git a/.gitignore b/.gitignore index fd48c26..8598652 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ importMap.json commitfile.txt logs.html buildData.json -__temporaryTestData__/ \ No newline at end of file +__temporaryTestData__/ +*.html \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 4c0e036..2e30805 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,11 +11,18 @@ "type": "node-terminal" }, { - "command": "npm run build && scope --report-for-commit 0417cc011a9c53b308584e2cc79e47d5568cf99b", + "command": "npm run build && scope --report-for-commit 161671d540ffe9de3b1480efb4e2dff35ee677e5", "name": "Run test of --report-for-commit", "request": "launch", "type": "node-terminal" }, + { + "command": "scope --find-refs src/ts/models/Model.ts", + "name": "scope --find-refs", + "request": "launch", + "type": "node-terminal", + "cwd": "${workspaceFolder}/test/_repo" + }, { "name": "Debug Jest Tests (Windows)", "type": "node", diff --git a/README.md b/README.md index 9f83a7d..7caf689 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,11 @@ From the repository you want to test the package run npm i scope-tags -D ``` +Make sure your Git configuration has case-sensitivity enabled +``` +git config --global core.ignorecase false +``` + ### How to run From the repository you want to test the package run @@ -112,20 +117,19 @@ Publishing is made automatically by pushing a commit to the main branch, see [gi The mock repo can be updated automatically by running `./pushTestRepo.sh` -### Features to do +### Features to do in order of importance -- [ ] Add keyboard shortcut hints when selecting tags and files -> https://github.com/enquirer/enquirer#select-choices -- [ ] Add groups on select prompt: - - [ ] Group files based on common path (files from same directory sould be grouped) - - [ ] Group tags based on parent modules -- [ ] Add remove hanging tags option to tag manager - search for tags not assigned to any module and ask the user if they want to delete them -- [ ] Unit tests for common actions: +- [ ] Find a way to clone test repository locally - this will make unit testing much quicker +- [ ] Unit tests for: + - [ ] The basic actions which can be performed on files - adding, deleting, modifying, renaming. After initial database entry the script should automatically handle all cases. - [ ] Testing if files are correstly updated in database depending on changes in git - [ ] On loading `tags.json` assert that all parents exist in database, if not then these modules won't be displayed - [ ] Add unit tests for even the basic stuff - reading and parsing JSON files, synchronization between the database and repository, etc. - [ ] Add unit tests for the basic actions which can be performed on files - adding, deleting, modifying, renaming. After initial database entry the script should automatically handle all cases. - [ ] Add [adf-validator](https://github.com/torifat/adf-validator/tree/master) which would give more specific errors (right now comments are just not being posted) +- [ ] Add remove hanging tags option to tag manager - search for tags not assigned to any module and ask the user if they want to delete them +- [ ] Add keyboard shortcut hints when selecting tags and files -> https://github.com/enquirer/enquirer#select-choices ### Special thanks -- [adf-validator](https://github.com/torifat/adf-validator) for showing how to validate Jira's ADF \ No newline at end of file +- [adf-validator](https://github.com/torifat/adf-validator) for showing how to validate Jira's ADF diff --git a/adf.json b/adf.json new file mode 100644 index 0000000..4f8e1c9 --- /dev/null +++ b/adf.json @@ -0,0 +1 @@ + {"type":"doc","version":1,"content":[{"type":"expand","attrs":{"title":"'Project' scope tags v0.3.0 │ 20.05.2024 13:57 │ -"},"content":[{"type":"table","content":[{"type":"tableRow","content":[{"type":"tableHeader","content":[{"type":"paragraph","content":[{"type":"text","text":"Affected tags","marks":[{"type":"strong"}]}]}]},{"type":"tableHeader","attrs":{"colwidth":[20]},"content":[{"type":"paragraph","content":[{"type":"text","text":"Lines","marks":[{"type":"strong"}]}]}]},{"type":"tableHeader","content":[{"type":"paragraph","content":[{"type":"text","text":"Used by module","marks":[{"type":"strong"}]}]}]},{"type":"tableHeader","content":[{"type":"paragraph","content":[{"type":"text","text":"Used by tags","marks":[{"type":"strong"}]}]}]}]},{"type":"tableRow","content":[{"type":"tableHeader","attrs":{},"content":[{"type":"paragraph","content":[{"type":"text","text":"Default module / Tag"}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"paragraph","content":[{"type":"text","text":"++ 60\n-- 27"}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"nestedExpand","attrs":{"title":"Default module"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Tag"}]}]},{"type":"nestedExpand","attrs":{"title":"Third module"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Some new tag"}]}]},{"type":"nestedExpand","attrs":{"title":"Fourth module"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Fourth tag"}]}]},{"type":"nestedExpand","attrs":{"title":"5 untagged files"},"content":[{"type":"paragraph","content":[{"type":"text","text":"src/Relevancy/RelevancyManager.ts\nsrc/References/TSReferenceFinder.ts\nsrc/Logger/Logger.ts\nsrc/Git/Types.ts\nsrc/HTMLCreator/HTMLCreator.ts"}]}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"nestedExpand","attrs":{"title":"Tag"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Default module"}]}]},{"type":"nestedExpand","attrs":{"title":"Some new tag"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Third module"}]}]},{"type":"nestedExpand","attrs":{"title":"Fourth tag"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Fourth module"}]}]}]}]},{"type":"tableRow","content":[{"type":"tableHeader","attrs":{},"content":[{"type":"paragraph","content":[{"type":"text","text":"Fourth module / Fourth tag"}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"paragraph","content":[{"type":"text","text":"++ 56\n-- 21"}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"paragraph","content":[{"type":"text","text":"1 modules hidden by low relevancy"}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"paragraph","content":[{"type":"text","text":"1 tags hidden by low relevancy"}]}]}]},{"type":"tableRow","content":[{"type":"tableHeader","attrs":{},"content":[{"type":"paragraph","content":[{"type":"text","text":"Third module / Some new tag"}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"paragraph","content":[{"type":"text","text":"++ 21\n-- 12"}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"nestedExpand","attrs":{"title":"Default module"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Tag"}]}]},{"type":"nestedExpand","attrs":{"title":"Fourth module"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Fourth tag"}]}]},{"type":"nestedExpand","attrs":{"title":"Second module"},"content":[{"type":"paragraph","content":[{"type":"text","text":"asdflkj"}]}]},{"type":"nestedExpand","attrs":{"title":"2 untagged files"},"content":[{"type":"paragraph","content":[{"type":"text","text":"src/Logger/Logger.ts\nsrc/HTMLCreator/HTMLCreator.ts"}]}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"nestedExpand","attrs":{"title":"Tag"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Default module"}]}]},{"type":"nestedExpand","attrs":{"title":"Fourth tag"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Fourth module"}]}]},{"type":"nestedExpand","attrs":{"title":"asdflkj"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Second module"}]}]}]}]},{"type":"tableRow","content":[{"type":"tableHeader","attrs":{},"content":[{"type":"nestedExpand","attrs":{"title":"1 untagged file"},"content":[{"type":"paragraph","content":[{"type":"text","text":"src/Relevancy/RelevancyManager.ts"}]}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"paragraph","content":[{"type":"text","text":"++ 1\n-- 1"}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"nestedExpand","attrs":{"title":"Default module"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Tag"}]}]},{"type":"nestedExpand","attrs":{"title":"Fourth module"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Fourth tag"}]}]},{"type":"nestedExpand","attrs":{"title":"Second module"},"content":[{"type":"paragraph","content":[{"type":"text","text":"asdflkj"}]}]},{"type":"nestedExpand","attrs":{"title":"5 untagged files"},"content":[{"type":"paragraph","content":[{"type":"text","text":"src/Git/GitRepository.ts\nsrc/Commands/runVerifyCommand.ts\nsrc/Commands/runVerifyUnpushedCommitsCommand.ts\nsrc/Logger/Logger.ts\nsrc/Commands/runLogCommitCommand.ts"}]}]}]},{"type":"tableHeader","attrs":{},"content":[{"type":"nestedExpand","attrs":{"title":"Tag"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Default module"}]}]},{"type":"nestedExpand","attrs":{"title":"Fourth tag"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Fourth module"}]}]},{"type":"nestedExpand","attrs":{"title":"asdflkj"},"content":[{"type":"paragraph","content":[{"type":"text","text":"Second module"}]}]}]}]}],"attrs":{"layout":"full-width"}}]}]} \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 6698981..0750862 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,15 +1,14 @@ -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { - preset: "ts-jest", - testEnvironment: "node", - transformIgnorePatterns: ["/node_modules/"], - roots: ["/test/", "/src/"], - collectCoverage: true, - collectCoverageFrom: ["**/src/**", "!**/node_modules/**"], - coverageDirectory: "./coverage", - coverageReporters: ["json", "lcovonly", "text", "clover"], - testPathIgnorePatterns: ["/_utils/"], - globalSetup: "/test/setup.js", - globalTeardown: "/test/teardown.js", + preset: 'ts-jest', + testEnvironment: 'node', + transformIgnorePatterns: ['/node_modules/', '/__temporaryTestData__/'], + roots: ["/test/", "/src/"], + collectCoverage: false, + collectCoverageFrom: ["src/**", "!**/node_modules/**"], + coverageDirectory: './coverage', + coverageReporters: ['json', 'lcovonly', 'text', 'clover'], + testPathIgnorePatterns: ['/_utils/'], + globalSetup: '/test/setup.js', + globalTeardown: '/test/teardown.js', setupFilesAfterEnv: ["./jest.setup.js"], }; \ No newline at end of file diff --git a/log.html b/log.html deleted file mode 100644 index b44febc..0000000 --- a/log.html +++ /dev/null @@ -1,391 +0,0 @@ - - - - - - - - build-123 - Scope Tags - - -
- Instructions -

build-123

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
Configuration
Package version0.2.9
Current date2024-5-15 15:34:14
Build data file locationC:\Users\mateusz.duda\scope-tags\buildData.json
Build tagbuild-123
Posted reports0 of 1 generated
-
-
-
-

Issues to be updated

- -
-
-
-
-

JIRA-KEY

- Back to top -
-
-

- From commit: added tests for tags definition file - (35077cb91ab71c20eef1d13482c26af45a51238b) -

-

Has relevancy data?: no

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
File pathChangeRenamed - Relevancy - LinesModule / TagReferenced files
- package.json - MODIFIED--++1 -

- Default module / Tag
Second module / - asdflkj -

-
-
- src/Commands/runAddCommand.ts - MODIFIED--++8 -

Default module / Tag

-
-
src/scope.ts
-
- src/Commands/runVerifyUnpushedCommitsCommand.ts - MODIFIED--++12

-
src/scope.ts
-
- src/Git/GitRepository.ts - MODIFIED--++6

-
- src/Console/FileTagger.ts -
-
- src/Commands/runCommitCommand.ts -
-
- src/Commands/runAddCommand.ts -
-
- src/Commands/runVerifyCommand.ts -
-
- src/Commands/runVerifyUnpushedCommitsCommand.ts -
-
- src/Report/ReportGenerator.ts -
-
- src/Commands/runReportForCommitCommand.ts -
-
- src/Commands/runReportForCommitListCommand.ts -
-
- src/Commands/runTagCommand.ts -
-
- src/Commands/runSkipVerificationAndPushCommand.ts -
-
- src/Commands/runLogCommitCommand.ts -
-
- src/Commands/runListUnpushedCommitsCommand.ts -
-
- src/Git/Types.ts - MODIFIED--++1

-
- src/Relevancy/Relevancy.ts -
-
- src/Scope/FileTagsDatabase.ts -
-
- src/Relevancy/RelevancyManager.ts -
-
- src/Git/GitRepository.ts -
-
- src/Console/FileTagger.ts -
-
- src/Commands/runAddCommand.ts -
-
- src/Logger/Logger.ts -
-
- src/Report/ReportGenerator.ts -
-
- src/Commands/runLogCommitCommand.ts -
-
- src/Commands/runVerifyUnpushedCommitsCommand.ts -
-
- src/Scope/TagsDefinitionFile.ts - MODIFIED--++20

-
- src/Scope/FileTagsDatabase.ts -
-
- src/Console/TagManager.ts -
-
- src/Report/ReportGenerator.ts -
-
- src/Console/ModuleManager.ts -
-
- src/Console/Menu.ts -
-
- src/Console/FileTagger.ts -
-
- src/Commands/runCommitCommand.ts -
-
- src/Commands/runAddCommand.ts -
-
- src/Commands/runReportForCommitCommand.ts -
-
- src/Commands/runReportForCommitListCommand.ts -
-
- src/Commands/runStartCommandLineInterfaceCommand.ts -
-
- src/Commands/runTagCommand.ts -
-
- test/scope/tagsDefinitionFile.test.ts - ADDED--++60

-
-
-
-
-
-

Instructions

-
- Reading additional data - Some entries have additional data on hover. You can hover over them to read the associated data - full commit - message, tags associated with a file, etc. -
-
-
-
- - diff --git a/logs.html b/logs.html index 09b5676..c669985 100644 --- a/logs.html +++ b/logs.html @@ -63,11 +63,11 @@

build-123

Package version - 0.2.9 + 0.3.1 Current date - 2024-5-15 14:54:17 + 2024-5-20 17:32:42 Build data file location @@ -100,16 +100,18 @@

JIRA-KEY

From commit: added tests for tags definition file + >testing new relevancy (35077cb91ab71c20eef1d13482c26af45a51238b)(161671d540ffe9de3b1480efb4e2dff35ee677e5)

-

Has relevancy data?: no

+

Has relevancy data?: yes

@@ -117,257 +119,220 @@

JIRA-KEY

- + - + - + - - + + - - + + - - - - - - - - - - - - - - - - - - - + - - - + + + + + + + + + + + + - - - - - - - - -
File path Renamed Relevancy Lines Module / TagReferenced filesUsed by
package.json.scope/database.json MODIFIEDIGNORED - -++1 -

- Default module / Tag
Second module / - asdflkj -

-
++3

-
src/Commands/runAddCommand.tssrc/Relevancy/Relevancy.ts MODIFIED --++8HIGH++4 --6

Default module / Tag

-
src/scope.ts
-
- src/Commands/runVerifyUnpushedCommitsCommand.ts + src/Relevancy/RelevancyManager.ts + +
-
MODIFIED--++12

-
src/scope.ts
-
- src/Git/GitRepository.ts +
-
MODIFIED--++6

+ src/References/IReferenceFinder.ts +
- src/Console/FileTagger.ts + src/References/ExternalMapReferenceFinder.ts
- src/Commands/runCommitCommand.ts + src/References/TSReferenceFinder.ts
- src/Commands/runAddCommand.ts -
-
- src/Commands/runVerifyCommand.ts + src/Report/JiraBuilder.ts
- src/Commands/runVerifyUnpushedCommitsCommand.ts + src/Logger/Logger.ts
-
+
src/Report/ReportGenerator.ts
-
- src/Commands/runReportForCommitCommand.ts -
-
- src/Commands/runReportForCommitListCommand.ts -
-
- src/Commands/runTagCommand.ts -
-
- src/Commands/runSkipVerificationAndPushCommand.ts -
-
- src/Commands/runLogCommitCommand.ts +
+ src/Commands/runFindReferencesCommand.ts
+
src/Git/Types.ts
- src/Commands/runListUnpushedCommitsCommand.ts + src/HTMLCreator/HTMLCreator.ts
src/Git/Types.tssrc/Relevancy/RelevancyManager.ts MODIFIED - -++1++1 --1

- src/Relevancy/Relevancy.ts + src/Git/GitRepository.ts +
+
+ src/Commands/runAddCommand.ts
- src/Scope/FileTagsDatabase.ts + src/Commands/runVerifyCommand.ts
- src/Relevancy/RelevancyManager.ts + src/Commands/runVerifyUnpushedCommitsCommand.ts
- src/Git/GitRepository.ts + src/Logger/Logger.ts
- src/Console/FileTagger.ts + src/Commands/runReportForCommitCommand.ts
- src/Commands/runAddCommand.ts -
-
- src/Logger/Logger.ts -
-
- src/Report/ReportGenerator.ts + src/Commands/runReportForCommitListCommand.ts
src/Commands/runLogCommitCommand.ts
-
- src/Commands/runVerifyUnpushedCommitsCommand.ts -
src/Scope/TagsDefinitionFile.tssrc/Report/JiraBuilder.ts MODIFIED --++20

LOW++56 --21 -
- src/Scope/FileTagsDatabase.ts +

+ Default module / Tag
Fourth module / + Fourth tag +

+
+
+ src/Report/ReportGenerator.ts
+
+ src/Report/ReportGenerator.ts + MODIFIED-HIGH++21 --12 +

+ Third module / Some new tag +

+
- src/Console/TagManager.ts + src/Report/JiraBuilder.ts
- src/Report/ReportGenerator.ts -
-
- src/Console/ModuleManager.ts + src/Logger/Logger.ts
- src/Console/Menu.ts + src/HTMLCreator/HTMLCreator.ts
- src/Console/FileTagger.ts -
-
- src/Commands/runCommitCommand.ts + src/Commands/runReportForCommitCommand.ts
- src/Commands/runAddCommand.ts -
-
- src/Commands/runReportForCommitCommand.ts -
-
src/Commands/runReportForCommitListCommand.ts
-
- src/Commands/runStartCommandLineInterfaceCommand.ts -
-
- src/Commands/runTagCommand.ts -
- test/scope/tagsDefinitionFile.test.ts - ADDED--++60

-

diff --git a/package-lock.json b/package-lock.json index 5c63369..b8fa23b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,12 @@ { "name": "scope-tags", +<<<<<<< HEAD "version": "0.3.1-node18", "lockfileVersion": 3, +======= + "version": "0.3.0", + "lockfileVersion": 1, +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 "requires": true, "packages": { "": { @@ -329,6 +334,7 @@ "node": ">=6.9.0" } }, +<<<<<<< HEAD "node_modules/@babel/highlight/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://npm.testowaplatforma123.net/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -686,6 +692,26 @@ "license": "MIT" }, "node_modules/@eslint-community/eslint-utils": { +======= + "@babel/runtime": { + "version": "7.24.5", + "resolved": "https://npm.testowaplatforma123.net/@babel%2fruntime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://npm.testowaplatforma123.net/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + } + } + }, + "@eslint-community/eslint-utils": { +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 "version": "4.4.0", "resolved": "https://npm.testowaplatforma123.net/@eslint-community%2feslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", @@ -711,6 +737,7 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, +<<<<<<< HEAD "node_modules/@eslint/eslintrc": { "version": "2.1.4", "resolved": "https://npm.testowaplatforma123.net/@eslint%2feslintrc/-/eslintrc-2.1.4.tgz", @@ -718,6 +745,66 @@ "dev": true, "license": "MIT", "peer": true, +======= + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@sindresorhus/is": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", + "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==" + }, + "@stoplight/better-ajv-errors": { + "version": "1.0.3", + "resolved": "https://npm.testowaplatforma123.net/@stoplight%2fbetter-ajv-errors/-/better-ajv-errors-1.0.3.tgz", + "integrity": "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==", + "requires": { + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "dependencies": { + "jsonpointer": { + "version": "5.0.1", + "resolved": "https://npm.testowaplatforma123.net/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, + "leven": { + "version": "3.1.0", + "resolved": "https://npm.testowaplatforma123.net/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + } + } + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@ts-morph/common": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.19.0.tgz", + "integrity": "sha512-Unz/WHmd4pGax91rdIKWi51wnVUW11QttMEPpBiBgIewnc9UQIX7UDLxr5vRlqeByXCwhkF6VabSsI0raWcyAQ==", + "requires": { + "fast-glob": "^3.2.12", + "minimatch": "^7.4.3", + "mkdirp": "^2.1.6", + "path-browserify": "^1.0.1" + }, +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -736,6 +823,7 @@ "url": "https://opencollective.com/eslint" } }, +<<<<<<< HEAD "node_modules/@eslint/eslintrc/node_modules/ajv": { "version": "6.12.6", "resolved": "https://npm.testowaplatforma123.net/ajv/-/ajv-6.12.6.tgz", @@ -748,13 +836,1001 @@ "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" +======= + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "@types/mkdirp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.2.tgz", + "integrity": "sha512-o0K1tSO0Dx5X6xlU5F1D6625FawhC3dU3iqr25lluNv/+/QIVH8RLNEiVokgIZo+mz+87w/3Mkg/VvQS+J51fQ==", + "requires": { + "@types/node": "*" + }, + "dependencies": { + "@types/node": { + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "requires": { + "undici-types": "~5.26.4" + } + } + } + }, + "@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "dev": true + }, + "@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^4.0.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/nodegit": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@types/nodegit/-/nodegit-0.27.10.tgz", + "integrity": "sha512-BCqbmp9iwOcCKRQui7GYMAHsD3DlPdAmzyNvX6hsoBSGMqgkOmrak2sF7gWrOoisH460MPFHaLEgQwuAYQqe2g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "requires": { + "@types/node": "*" + }, + "dependencies": { + "@types/node": { + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "requires": { + "undici-types": "~5.26.4" + } + } + } + }, + "@types/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==", + "dev": true, + "requires": { + "@types/glob": "*", + "@types/node": "*" + } + }, + "@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "@types/uuid": { + "version": "9.0.8", + "resolved": "https://npm.testowaplatforma123.net/@types%2fuuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dev": true, + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://npm.testowaplatforma123.net/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, +<<<<<<< HEAD "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { +======= + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha512-Yisb7ew0ZEyDtRYQ+b+26o9KbiYPFxwcsxKzbssigzRRMJ9LpExPVUg6Fos7eP7yP3q7///tzze4nm4lTptPBw==", + "dev": true, + "requires": { + "default-require-extensions": "^1.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true + }, + "array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "requires": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + } + }, + "array-equal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.2.tgz", + "integrity": "sha512-gUHx76KtnhEgB3HOuFYiCm3FIdEs6ocM2asHvNTkfu/Y09qQVrrVVaOKENmS2KkSaGoxgXNqC+ZVtR/n0MOkSA==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==", + "dev": true + }, + "array.prototype.reduce": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz", + "integrity": "sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-array-method-boxes-properly": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" + } + }, + "arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true + }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "requires": { + "possible-typed-array-names": "^1.0.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" + }, + "aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true + } + } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "dev": true + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-jest": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-23.6.0.tgz", + "integrity": "sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew==", + "dev": true, + "requires": { + "babel-plugin-istanbul": "^4.1.6", + "babel-preset-jest": "^23.2.0" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-istanbul": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", + "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.13.0", + "find-up": "^2.1.0", + "istanbul-lib-instrument": "^1.10.1", + "test-exclude": "^4.2.1" + } + }, + "babel-plugin-jest-hoist": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz", + "integrity": "sha512-N0MlMjZtahXK0yb0K3V9hWPrq5e7tThbghvDr0k3X75UuOOqwsWW6mk8XHD2QvEC0Ca9dLIfTgNU36TeJD6Hnw==", + "dev": true + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha512-C4Aq+GaAj83pRQ0EFgTvw5YO6T3Qz2KGrNRwIj9mSoNHVvdZY4KO2uA6HNtNXCw993iSZnckY1aLW8nOi8i4+w==", + "dev": true + }, + "babel-preset-jest": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz", + "integrity": "sha512-AdfWwc0PYvDtwr009yyVNh72Ev68os7SsPmOFVX7zSA+STXuk5CV2iMVazZU01bEoHCSwTkgv4E4HOOcODPkPg==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^23.2.0", + "babel-plugin-syntax-object-rest-spread": "^6.13.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "better-ajv-errors": { + "version": "0.6.7", + "resolved": "https://npm.testowaplatforma123.net/better-ajv-errors/-/better-ajv-errors-0.6.7.tgz", + "integrity": "sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/runtime": "^7.0.0", + "chalk": "^2.4.1", + "core-js": "^3.2.1", + "json-to-ast": "^2.0.3", + "jsonpointer": "^4.0.1", + "leven": "^3.1.0" + }, + "dependencies": { + "core-js": { + "version": "3.37.1", + "resolved": "https://npm.testowaplatforma123.net/core-js/-/core-js-3.37.1.tgz", + "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://npm.testowaplatforma123.net/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + } + } + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "brace-expansion": { +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 "version": "1.1.11", "resolved": "https://npm.testowaplatforma123.net/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", @@ -3120,6 +4196,7 @@ "integrity": "sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==", "license": "MIT" }, +<<<<<<< HEAD "node_modules/code-error-fragment": { "version": "0.0.230", "resolved": "https://npm.testowaplatforma123.net/code-error-fragment/-/code-error-fragment-0.0.230.tgz", @@ -3130,6 +4207,15 @@ } }, "node_modules/code-point-at": { +======= + "code-error-fragment": { + "version": "0.0.230", + "resolved": "https://npm.testowaplatforma123.net/code-error-fragment/-/code-error-fragment-0.0.230.tgz", + "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==", + "dev": true + }, + "code-point-at": { +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 "version": "1.1.0", "resolved": "https://npm.testowaplatforma123.net/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", @@ -4485,6 +5571,45 @@ "responselike": "^2.0.0", "to-readable-stream": "^2.0.0", "type-fest": "^0.10.0" +<<<<<<< HEAD +======= + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://npm.testowaplatforma123.net/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true + }, + "handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 }, "engines": { "node": ">=10" @@ -4493,6 +5618,7 @@ "url": "https://github.com/sindresorhus/got?sponsor=1" } }, +<<<<<<< HEAD "node_modules/got/node_modules/type-fest": { "version": "0.10.0", "resolved": "https://npm.testowaplatforma123.net/type-fest/-/type-fest-0.10.0.tgz", @@ -4503,6 +5629,38 @@ }, "funding": { "url": "https://github.com/sponsors/sindresorhus" +======= + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://npm.testowaplatforma123.net/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://npm.testowaplatforma123.net/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + } +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 } }, "node_modules/graceful-fs": { @@ -5058,7 +6216,56 @@ "node": ">=10" } }, +<<<<<<< HEAD "node_modules/istanbul-lib-report/node_modules/make-dir": { +======= + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-stable-stringify": { + "version": "1.1.1", + "resolved": "https://npm.testowaplatforma123.net/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", + "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", + "dev": true, + "requires": { + "call-bind": "^1.0.5", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://npm.testowaplatforma123.net/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "json-to-ast": { + "version": "2.1.0", + "resolved": "https://npm.testowaplatforma123.net/json-to-ast/-/json-to-ast-2.1.0.tgz", + "integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==", + "dev": true, + "requires": { + "code-error-fragment": "0.0.230", + "grapheme-splitter": "^1.0.4" + } + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "jsonfile": { +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 "version": "4.0.0", "resolved": "https://npm.testowaplatforma123.net/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", @@ -6069,6 +7276,7 @@ "graceful-fs": "^4.1.6" } }, +<<<<<<< HEAD "node_modules/jsonify": { "version": "0.0.1", "resolved": "https://npm.testowaplatforma123.net/jsonify/-/jsonify-0.0.1.tgz", @@ -6076,6 +7284,29 @@ "license": "Public Domain", "funding": { "url": "https://github.com/sponsors/ljharb" +======= + "jsonify": { + "version": "0.0.1", + "resolved": "https://npm.testowaplatforma123.net/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true + }, + "jsonpointer": { + "version": "4.1.0", + "resolved": "https://npm.testowaplatforma123.net/jsonpointer/-/jsonpointer-4.1.0.tgz", + "integrity": "sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==", + "dev": true + }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 } }, "node_modules/jsonpointer": { @@ -8144,6 +9375,7 @@ "node": ">=8" } }, +<<<<<<< HEAD "node_modules/read-pkg-up": { "version": "7.0.1", "resolved": "https://npm.testowaplatforma123.net/read-pkg-up/-/read-pkg-up-7.0.1.tgz", @@ -8159,6 +9391,40 @@ }, "funding": { "url": "https://github.com/sponsors/sindresorhus" +======= + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://npm.testowaplatforma123.net/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } +>>>>>>> c70012e919791fa57b2b9a3d9b3a34c0de56ad99 } }, "node_modules/read-pkg-up/node_modules/find-up": { diff --git a/package.json b/package.json index ebc86a0..e2f95bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scope-tags", - "version": "0.3.1-node18", + "version": "0.3.3", "description": "Output human readable test scope report for QA", "main": "dist/scope.js", "types": "dist/scope.d.ts", @@ -16,7 +16,7 @@ "local-build": "npm run build && npm link", "start": "npm run build && scope", "tag": "npm run build && scope --tag-unpushed-commits", - "test": "npm run build && jest ./test --verbose --silent=false --runInBand", + "test": "jest -i test/* --verbose --silent=false --runInBand", "clean": "rimraf .scope/" }, "repository": { @@ -55,7 +55,11 @@ "enquirer": "^2.4.1", "html-creator": "^0.7.2", "nodegit": "^0.28.0-alpha.26", - "ts-morph": "^22.0.0" + "ts-morph": "^22.0.0", + "@stoplight/better-ajv-errors": "^1.0.3", + "node-fetch": "^2.7.0", + "ajv": "^4.11.8", + "better-ajv-errors": "^0.6.7" }, "bin": { "scope": "./dist/scope.js" diff --git a/src/Commands/runFindReferencesCommand.ts b/src/Commands/runFindReferencesCommand.ts index b5df3b4..2732060 100644 --- a/src/Commands/runFindReferencesCommand.ts +++ b/src/Commands/runFindReferencesCommand.ts @@ -4,10 +4,38 @@ import { Relevancy } from "../Relevancy/Relevancy"; export function runFindReferencesCommand(args: Array, root: string) { const filePath = args[1]; if (!filePath) { - console.log("--report-for-commit-list requires a path to build metadata, use: --report-for-commit-list "); + console.log("--find-refs requires a path to a file, use: --find-refs "); process.exit(1); } const tsReferenceFinder = new TSReferenceFinder(root, "tsconfig.json"); - tsReferenceFinder.findReferences(filePath, Relevancy.HIGH); + + const references = tsReferenceFinder.findReferences(filePath, Relevancy.HIGH); + + if (!references.length) { + console.log("No references found"); + } else { + console.log(`Found ${references.length} references`); + } + + references + .sort((entryA, entryB) => { + const pathA = entryA.filename.toUpperCase(); // ignore upper and lowercase + const pathB = entryB.filename.toUpperCase(); // ignore upper and lowercase + if (pathA < pathB) { + return -1; + } + if (pathA > pathB) { + return 1; + } + return 0; + }) + .forEach(reference => { + let info = `- ${reference.filename}`; + + if (reference.unused) { + info += " (unused)"; + } + console.log(info); + }) } diff --git a/src/Commands/runHelpCommand.ts b/src/Commands/runHelpCommand.ts index d523763..a521d15 100644 --- a/src/Commands/runHelpCommand.ts +++ b/src/Commands/runHelpCommand.ts @@ -1,6 +1,6 @@ export function runHelpCommand(args: Array) { console.log(` - scope\t\t\tStarts command line interface, which enables you to add and remove tags and modules, and assign tags between modules + npx scope\t\t\tStarts command line interface, which enables you to add and remove tags and modules, and assign tags between modules Definitions: @@ -14,16 +14,19 @@ Definitions: Options: - --tag\t\t\tEnables to tag specific files or directories, usage: scope --tag - --untag\t\t\tRemoves tags for files (single or directory) in database, also removes 'ignored' status, usage: scope --untag + --tag\t\t\tEnables to tag specific files or directories, usage: npx scope --tag + --untag\t\t\tRemoves tags for files (single or directory) in database, also removes 'ignored' status, usage: npx scope --untag - --see\t\t\tPrints the tags assigned to file or directory, usage: scope --see -\t\t\t\tIf path is ommited, lists the commits which are currently "observed" by the script, usage: scope --see + --see\t\t\tPrints the tags assigned to file or directory, usage: npx scope --see +\t\t\t\tIf path is ommited, lists the commits which are currently "observed" by the script, usage: npx scope --see + --find-refs\t\t\tUses ts-morph to find .ts file references, usage: npx scope --find-references + --logcommit\t\t\tLogs files associated with a commit, usage: npx scope --logcommit + --add\t\t\tLists files which were modified in commits, which are not yet pushed to remote, and tags them - --commit\t\t\tLists files which were modified by a specific commit and tags them, usage: scope --commit + --commit\t\t\tLists files which were modified by a specific commit and tags them, usage: npx scope --commit - --verify\t\t\tReturns 0 if all files modified by a commit were tagged or ignored and 1 otherwise, usage: scope --verify + --verify\t\t\tReturns 0 if all files modified by a commit were tagged or ignored and 1 otherwise, usage: npx scope --verify --verify-unpushed-commits\tWorks similar to --verify, but checks for commits no yet pushed to remote, returns 0 or 1 analogous to --verify --report-for-commit\t\tGenerates human readable report with statistics for files modified in a commit, usage: --report-for-commit diff --git a/src/Commands/runReportForCommitCommand.ts b/src/Commands/runReportForCommitCommand.ts index b363b34..b9f758f 100644 --- a/src/Commands/runReportForCommitCommand.ts +++ b/src/Commands/runReportForCommitCommand.ts @@ -9,6 +9,8 @@ import { FileTagsDatabase } from "../Scope/FileTagsDatabase"; import { TagsDefinitionFile } from "../Scope/TagsDefinitionFile"; import { fileExists } from "../FileSystem/fileSystemUtils"; import { RelevancyManager } from "../Relevancy/RelevancyManager"; +import { ADFValidator } from "../Report/ADFValidator"; +import { JiraBuilder } from "../Report/JiraBuilder"; export function runReportForCommitCommand(args: Array, root: string) { // Checks if all files from the commit are present in database (or excluded) @@ -44,16 +46,28 @@ export function runReportForCommitCommand(args: Array, root: string) { } }); - const generator = new ReportGenerator(repository, tagsDefinitionFile, fileTagsDatabase, configFile, referenceFinders); + const generator = new ReportGenerator(repository, tagsDefinitionFile, fileTagsDatabase, configFile, referenceFinders, true); const relevancyManager = new RelevancyManager(); repository.getCommitByHash(hash).then(async (commit: Commit) => { const relevancyMap = relevancyManager.loadRelevancyMapFromCommits([commit]); - const report = await generator.generateReportForCommit(commit, projects[0].name, relevancyMap); + const report = await generator.generateReportForCommit(commit, projects[0].name, relevancyMap, false); if (generator.isReportEmpty(report)) { console.log("Report is empty (no tags were found in modified files)."); return; } - generator.getReportAsJiraComment(report, true); - }); + + const jiraBuilder = new JiraBuilder(); + + return generator.getReportAsJiraComment(report, jiraBuilder, true); + }).then(async commentReport => { + if (!commentReport) { + return; + } + + const validator = new ADFValidator(); + + await validator.loadSchema(); + validator.validateADF(commentReport.adfDocument); + }) } diff --git a/src/Commands/runReportForCommitListCommand.ts b/src/Commands/runReportForCommitListCommand.ts index d6c4169..9229ec6 100644 --- a/src/Commands/runReportForCommitListCommand.ts +++ b/src/Commands/runReportForCommitListCommand.ts @@ -10,6 +10,8 @@ import { fileExists, getExtension, removeFile, resolvePath, saveHTMLLogs } from import { RelevancyManager } from "../Relevancy/RelevancyManager"; import { ConfigurationProperty, Logger } from "../Logger/Logger"; import { ConfigFile } from "../Scope/ConfigFile"; +import { ADFValidator } from "../Report/ADFValidator"; +import { JiraBuilder } from "../Report/JiraBuilder"; const os = require("os"); @@ -92,20 +94,35 @@ export async function runReportForCommitListCommand(args: Array, root: s const relevancyMap = relevancyTagger.loadRelevancyMapFromCommits(commits); console.log(`[Scope tags]: Generating report for issue '${issue}'...'`); - const report = await generator.generateReportForCommits(commits, projects[0].name, buildTag, false, relevancyMap); + const report = await generator.generateReportForCommits(commits, projects[0].name, buildTag, relevancyMap); if (generator.isReportEmpty(report)) { - console.log("[Scope tags]: Report ommited because no tags for modified files were found'"); + console.log("[Scope tags]: Report ommited because no tags for modified files were found"); continue; } - const commentReportJSON = generator.getReportAsJiraComment(report, false); + const jiraBuilder = new JiraBuilder(); + + const commentReport = generator.getReportAsJiraComment(report, jiraBuilder, false); + const commentReportADF = `{adf:display=block}${commentReport.comment}{adf}`; + + console.log(`[Scope tags]: Validating ADF of report for issue '${issue}'...'`); + + const validator = new ADFValidator(); + + await validator.loadSchema(); + const reportIsValid = validator.validateADF(commentReport.adfDocument, issue); + + if (!reportIsValid) { + console.log(`[Scope tags]: Ommiting report for issue ${issue} as it have not passed ADF validation`); + continue; + } console.log(`[Scope tags]: Posting report as comment for issue '${issue}'...'`); const reportPostedSuccessfully = await buildIntegration.updateIssue({ issue: issue, - report: commentReportJSON, + report: commentReportADF, hostname: os.hostname(), }); diff --git a/src/Commands/runVerifyCommand.ts b/src/Commands/runVerifyCommand.ts index abbbf59..1bc6df0 100644 --- a/src/Commands/runVerifyCommand.ts +++ b/src/Commands/runVerifyCommand.ts @@ -18,9 +18,12 @@ export function runVerifyCommand(args: Array, root: string) { const relevancyManager = new RelevancyManager(); repository.getCommitByHash(commitHash).then(async (commit: Commit) => { + console.log(`Loading relevancy map...'`); + const relevancyMap = relevancyManager.loadRelevancyMapFromCommits([commit]); + console.log(`Checking commit '${commit.summary()}'`); - const commitInfo = await repository.verifyCommit(commit, config, fileTagsDatabase, relevancyManager); + const commitInfo = await repository.verifyCommit(commit, config, fileTagsDatabase, relevancyManager, relevancyMap); console.log(commitInfo); }); diff --git a/src/Commands/runVerifyUnpushedCommitsCommand.ts b/src/Commands/runVerifyUnpushedCommitsCommand.ts index 0d22e34..95fab8b 100644 --- a/src/Commands/runVerifyUnpushedCommitsCommand.ts +++ b/src/Commands/runVerifyUnpushedCommitsCommand.ts @@ -39,6 +39,9 @@ export async function verifyUnpushedCommits(args: Array, root: string, u const unpushedCommits: Commit[] = await repository.getUnpushedCommits(); + console.log(`[Scope tags]: Loading relevancy map...'`); + const relevancyMap = relevancyManager.loadRelevancyMapFromCommits(unpushedCommits); + if (!unpushedCommits.length) { console.log("No commits found that can be verified"); return VerificationStatus.VERIFIED; @@ -46,7 +49,7 @@ export async function verifyUnpushedCommits(args: Array, root: string, u for (const commit of unpushedCommits) { console.log(`[Scope tags] Checking: '${commit.message().trim()}'`) - const verificationInfo = await repository.verifyCommit(commit, config, fileTagsDatabase, relevancyManager, useGitNatively); + const verificationInfo = await repository.verifyCommit(commit, config, fileTagsDatabase, relevancyManager, relevancyMap, useGitNatively); if (verificationInfo.isSkipped) { console.log(`[Scope Tags] Skipped check for '${commit.summary()}'`); @@ -79,7 +82,7 @@ export async function verifyUnpushedCommits(args: Array, root: string, u const filesWhichShouldHaveRelevancyAdded: string[] = []; info.filesToBeRelevancyTagged.forEach(fileData => { - const fileHasRelevancy = info.relevancy.some(relevancy => relevancy.path === fileData.newPath); + const fileHasRelevancy = relevancyMap.get(fileData.newPath); if (!fileHasRelevancy) { filesWhichShouldHaveRelevancyAdded.push(fileData.newPath); diff --git a/src/Console/FileTagger.ts b/src/Console/FileTagger.ts index b6bad7a..2a47b93 100644 --- a/src/Console/FileTagger.ts +++ b/src/Console/FileTagger.ts @@ -160,7 +160,7 @@ export class FileTagger { private async _selectFiles(fileData: Array): Promise { const prompt = new MultiSelect({ name: 'value', - message: 'Select files to apply the same tags', + message: 'Select files to apply the same tags and use ENTER to confirm', footer: 'CTRL+C to go to next step, G to select whole group', limit: 10, choices: this._mapFileDataToOptions(fileData), diff --git a/src/FileSystem/fileSystemUtils.ts b/src/FileSystem/fileSystemUtils.ts index 2453553..1c8cfe1 100644 --- a/src/FileSystem/fileSystemUtils.ts +++ b/src/FileSystem/fileSystemUtils.ts @@ -1,5 +1,5 @@ import fs from "fs"; -import path from "path"; +import path, { join } from "path"; export function scopeFolderExists(root: string): boolean { const scopeFolderPath = path.join(root, ".scope"); @@ -27,8 +27,14 @@ export function getFileDirectoryPath(filePath: string): string { return filePath.substring(0, filePath.lastIndexOf("/")); } -export function fileExists(filePath: string): boolean { - return fs.existsSync(filePath); +export function fileExists(filePath: string, relativeTo?: string): boolean { + let path = filePath; + + if (relativeTo) { + path = join(relativeTo, filePath); + } + + return fs.existsSync(path); } export function isDirectory(filePath: string): boolean { diff --git a/src/Git/GitRepository.ts b/src/Git/GitRepository.ts index 7766b6e..acf4b55 100644 --- a/src/Git/GitRepository.ts +++ b/src/Git/GitRepository.ts @@ -5,6 +5,7 @@ import { FileTagsDatabase, FileStatusInDatabase } from "../Scope/FileTagsDatabas import { RelevancyManager } from "../Relevancy/RelevancyManager"; import { ConfigFile } from "../Scope/ConfigFile"; import { execSync } from "child_process"; +import { RelevancyMap } from "../Relevancy/Relevancy"; export class GitRepository { @@ -106,7 +107,6 @@ export class GitRepository { } public getFileDataUsingNativeGitCommand(commit: Commit): FileData[] { - /** * Has the following format: * M src/Git/GitRepository.ts @@ -134,8 +134,14 @@ export class GitRepository { `cd ${this._root} && git update-index && git diff-tree --no-commit-id --numstat -r ${commit.sha()}` ).toString().trim().split('\n'); - if (nameStatusOutput.length !== numstatOutput.length) { - throw new Error(`Output of git-diff --name-status does not match one for --no-commit-id for commit ${commit.sha()}`); + if (nameStatusOutput.length === 0) { + console.debug(nameStatusOutput); + throw new Error(`Output of git-diff --name-status is empty for commit ${commit.sha()}`); + } + + if (numstatOutput.length === 0) { + console.debug(numstatOutput); + throw new Error(`Output of git-diff --numstat is empty for commit ${commit.sha()}`); } const statusesToGitDeltaTypeMap = new Map([ @@ -154,19 +160,27 @@ export class GitRepository { const fileDataArray: FileData[] = []; - numstatOutput.forEach((numStatLine: string, i: number) => { + nameStatusOutput.forEach((nameStatusLine: string, i: number) => { + const [changeType, relatedFilePath, optionalRenamedPath] = nameStatusLine.split('\t'); + + const numStatLine = numstatOutput.find(output => output.includes(relatedFilePath)); + + if (!numStatLine) { + throw new Error(`No matching git-diff --name-status for file ${relatedFilePath}, output is ${nameStatusOutput}`); + } + const [linesAdded, linesRemoved, filePath] = numStatLine.split('\t'); - const [changeType, relatedFilePath, optionalRenamedPath] = nameStatusOutput[i].split('\t'); + let ourChangeType = statusesToGitDeltaTypeMap.get(changeType[0]) || GitDeltaType.UNREADABLE; - if (filePath !== relatedFilePath) { - throw new Error(`Error while processing git-diff: File path '${filePath}' does not match one of '${relatedFilePath}'`); + if (optionalRenamedPath) { + ourChangeType = GitDeltaType.RENAMED; } fileDataArray.push({ oldPath: filePath, newPath: optionalRenamedPath || filePath, - change: statusesToGitDeltaTypeMap.get(changeType[0]) || GitDeltaType.UNREADABLE, + change: ourChangeType, linesAdded: parseInt(linesAdded), linesRemoved: parseInt(linesRemoved), commitedIn: commit @@ -176,11 +190,14 @@ export class GitRepository { return fileDataArray; } - public async getFileDataForCommits(commits: Array): Promise { + public async getFileDataForCommits(commits: Array, useGitNatively = false): Promise { const fileDataForCommits: Array = []; for (const commit of commits) { - const fileDataForCommit = await this.getFileDataForCommit(commit); + const fileDataForCommit = useGitNatively + ? this.getFileDataUsingNativeGitCommand(commit) + : await this.getFileDataForCommit(commit); + fileDataForCommit.forEach(fileData => fileDataForCommits.push(fileData)); } @@ -248,27 +265,12 @@ export class GitRepository { // Mostly from https://github.com/nodegit/nodegit/blob/master/examples/add-and-commit.js public async commitFiles(commitMessage: string, filePaths: string[]): Promise { - const { execSync } = require('child_process'); - const repository = await this._getRepository(); - // const index = await repository.refreshIndex(); - - // for (const filePath of filePaths) { - // await index.addByPath(filePath); - // } - - // await index.write(); - - // const oid = await index.writeTree(); - - // const parent = await repository.getHeadCommit(); const author = Signature.now("Scott Chacon", "schacon@gmail.com"); const committer = Signature.now("Scott A Chacon", "scott@github.com"); const commitId = await repository.createCommitOnHead(filePaths, author, committer, commitMessage); - repository.createCommitOnHead - return commitId; } @@ -297,7 +299,14 @@ export class GitRepository { await index.write(); } - public async verifyCommit(commit: Commit, config: ConfigFile, database: FileTagsDatabase, relevancyManager: RelevancyManager, useGitNatively = false): Promise { + public async verifyCommit( + commit: Commit, + config: ConfigFile, + database: FileTagsDatabase, + relevancyManager: RelevancyManager, + relevancyMap?: RelevancyMap, + useGitNatively = false + ): Promise { const commitInfo: VerificationInfo = { isVerified: false, filesToTag: [], @@ -341,13 +350,24 @@ export class GitRepository { commitInfo.filesToBeRelevancyTagged = fileDataArray.filter(fileData => { const fileStatus = statusMap.get(fileData); return fileStatus !== FileStatusInDatabase.IGNORED; + }).filter(fileData => { + if (!relevancyMap) { + return true; + } + + const mapHasRelevancy = [...relevancyMap.values()].some(relevancyInfo => relevancyInfo.some(info => info.path === fileData.oldPath || info.path === fileData.newPath)) + + if (mapHasRelevancy) { + return false; + } + + return true; }); if (!commitInfo.filesToTag.length) { commitInfo.isVerified = true; } - // Check relevancy commitInfo.hasRelevancy = relevancyManager.doesCommitMessageHaveRelevancyData(commit.message()); if (commitInfo.hasRelevancy) { @@ -376,7 +396,18 @@ export class GitRepository { } public isMergeCommit(commit: Commit): boolean { - return commit.parentcount() > 1; + if (commit.parentcount() > 1) { + return true; + } + + const trimmedCommitMessage = commit.summary(); + + // Ugly, but works + if (trimmedCommitMessage.includes("Merge")) { + return true; + } + + return false; } private async _getRepository(): Promise { @@ -426,4 +457,8 @@ export class GitRepository { return false; } } + + public get root() { + return this._root; + } } diff --git a/src/HTMLCreator/HTMLCreator.ts b/src/HTMLCreator/HTMLCreator.ts index c142db5..18cfe99 100644 --- a/src/HTMLCreator/HTMLCreator.ts +++ b/src/HTMLCreator/HTMLCreator.ts @@ -188,10 +188,32 @@ export class HTMLCreator { ] }); issue.commitInfos.forEach(commitLog => this._appendCommitTable(commitLog, issue.key, seeCommitURL, repositoryURL)); + if (issue.errors.length) { + this._html.document.addElementToId(issue.key, { + type: "h3", + content: "Error messages" + }); + + issue.errors.forEach(errorMessage => { + this._appendErrorMessage(issue.key, errorMessage); + }) + + } this._html.document.addElementToId(issue.key, { type: "hr" }); }); } + private _appendErrorMessage(key: string, errorMessage: string) { + this._html.document.addElementToId(key, { + type: "pre", + content: [{ + type: "code", + content: errorMessage + } + ] + }); + } + public appendInstructions() { this._html.document.addElementToType("main", { type: "div", diff --git a/src/Logger/Logger.ts b/src/Logger/Logger.ts index b7f3341..96e1bb9 100644 --- a/src/Logger/Logger.ts +++ b/src/Logger/Logger.ts @@ -30,6 +30,7 @@ export type ConfigurationMap = Map; export type IssueLog = { key: string, commitInfos: CommitLog[], + errors: string[], }; export type CommitLog = { @@ -73,6 +74,7 @@ export class Logger { public static pushIssueInfo(issueKey: string, commits: Commit[]) { Logger._issues.push(({ key: issueKey, + errors: [], commitInfos: commits.map(commit => ({ hash: commit.sha(), fileLogs: [], @@ -80,7 +82,21 @@ export class Logger { summary: commit.summary(), hasRelevancy: Logger._relevancyManager.doesCommitMessageHaveRelevancyData(commit.message()), })), - } as IssueLog)); + } as unknown as IssueLog)); + } + + static pushErrorMessage(issue: string, error: any) { + let matchingIssue = Logger._issues.find(issueLog => issueLog.key === issue); + + if (!matchingIssue) { + matchingIssue = this._getDetachedIssueLogs(); + return; + } + + const justText = error.toString().replace( + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ''); + + matchingIssue.errors.push(justText); } static pushFileInfo(fileData: FileData, fileInfo: FileInfo) { @@ -94,7 +110,7 @@ export class Logger { }); }); - matchingCommitFileLogs = matchingCommitFileLogs || this._getDetachedFileLogs(); + matchingCommitFileLogs = matchingCommitFileLogs || this._getDetachedIssueLogs().commitInfos[0].fileLogs; const newFileLog: FileLog = { path: fileData.oldPath, @@ -111,14 +127,15 @@ export class Logger { matchingCommitFileLogs.push(newFileLog); } - private static _getDetachedFileLogs(): FileLog[] { + private static _getDetachedIssueLogs(): IssueLog { const currentDetachedIssueLog = Logger._issues.find(issueInfo => issueInfo.key === Logger._DETACHED_ID); if (currentDetachedIssueLog) { - return currentDetachedIssueLog.commitInfos[0].fileLogs; + return currentDetachedIssueLog; } const newDetachedIssueLog: IssueLog = { key: Logger._DETACHED_ID, + errors: [], commitInfos: [{ hash: Logger._DETACHED_ID, fileLogs: [], @@ -129,7 +146,7 @@ export class Logger { }; Logger._issues.push(newDetachedIssueLog); - return newDetachedIssueLog.commitInfos[0].fileLogs; + return newDetachedIssueLog; } public static exportLogsToHTML(configFile: ConfigFile): string { diff --git a/src/References/ExternalMapReferenceFinder.ts b/src/References/ExternalMapReferenceFinder.ts index 992f30a..491be67 100644 --- a/src/References/ExternalMapReferenceFinder.ts +++ b/src/References/ExternalMapReferenceFinder.ts @@ -55,7 +55,7 @@ export class ExternalMapReferenceFinder implements IReferenceFinder { const referenceList: Array = []; this._importMap.forEach(importData => { - if (importData.imports.includes(fileNameOrPath)) { + if (importData.imports.includes(fileNameOrPath) && !referenceList.some(reference => reference.filename === importData.file)) { referenceList.push({ filename: importData.file, unused: false, diff --git a/src/References/TSReferenceFinder.ts b/src/References/TSReferenceFinder.ts index 29edae6..62b9edf 100644 --- a/src/References/TSReferenceFinder.ts +++ b/src/References/TSReferenceFinder.ts @@ -30,10 +30,13 @@ export class TSReferenceFinder implements IReferenceFinder { const referenceList: Array = []; const languageService = this._project.getLanguageService(); - const sourceFile = this._project.getSourceFile(fileNameOrPath); + const allSourceFiles = this._project.getSourceFiles(); + + const pathRelativeToRoot = path.join(this._root, fileNameOrPath); + const sourceFile = this._project.getSourceFile(pathRelativeToRoot); if (!sourceFile) { - console.log(`[TSReferenceFinder] File '${fileNameOrPath}' is not in scope of tsconfig.json of the project.`); + console.log(`[TSReferenceFinder] File '${pathRelativeToRoot}' is not in scope of tsconfig.json of the project.`); return []; } diff --git a/src/Relevancy/Relevancy.ts b/src/Relevancy/Relevancy.ts index 0a618a2..5f59416 100644 --- a/src/Relevancy/Relevancy.ts +++ b/src/Relevancy/Relevancy.ts @@ -1,5 +1,3 @@ -import { FilePath } from "../Git/Types"; - export enum Relevancy { LOW = "LOW", // Does not search for references HIGH = "HIGH", // Searches for references @@ -14,7 +12,7 @@ export type CommitMessageRelevancyInfo = { commit: string }; -export type RelevancyMap = Map>; +export type RelevancyMap = Map>; // Maps commit SHA to relevancies // Descriptions diff --git a/src/Report/ADFValidator.ts b/src/Report/ADFValidator.ts new file mode 100644 index 0000000..00526b9 --- /dev/null +++ b/src/Report/ADFValidator.ts @@ -0,0 +1,57 @@ +const nodeFetch = require("node-fetch"); +const betterAjvErrors = require('better-ajv-errors'); +import Ajv from 'ajv'; +import { Logger } from '../Logger/Logger'; + +export class ADFValidator { + private static SCHEMA_URL = "https://unpkg.com/@atlaskit/adf-schema@36.8.5/dist/json-schema/v1/full.json"; + + private _schema: Object; + + public constructor() { } + + public async loadSchema(): Promise { + console.log("[ADFValidator] Fetching latest JSON schema"); + + const response = await nodeFetch(ADFValidator.SCHEMA_URL); + + if (!response.ok) { + throw new Error(`[ADFValidator] Error ${response.errorCode} while fetching latest ADF schema from ${ADFValidator.SCHEMA_URL}`); + } + + this._schema = await response.json(); + } + + public validateADF( + commentJSON: string, + issue?: string + ): boolean { + console.log("[ADFValidator] Validating ADF..."); + + if (!this._schema) { + throw new Error(`[ADFValidator] Schema is not loaded yet, call await loadSchema() before validation`); + } + + const ajv = new Ajv({ jsonPointers: true }); + + const data = JSON.parse(commentJSON); + + const validate = ajv.compile(this._schema); + const isValid = validate(data); + if (!isValid) { + const errors = betterAjvErrors(this._schema, data, validate.errors, { + indent: 2, + }); + console.log(errors); + + if (issue) { + Logger.pushErrorMessage(issue, errors); + } + console.log(`[ADFValidator] Error while validating ${commentJSON}`); + return false; + } + + console.log("[ADFValidator] Successfully validated ADF"); + return true; + } +} \ No newline at end of file diff --git a/src/Report/AdfUtils.ts b/src/Report/AdfUtils.ts index 0b15636..eab347f 100644 --- a/src/Report/AdfUtils.ts +++ b/src/Report/AdfUtils.ts @@ -61,9 +61,9 @@ export const p = (...content: any) => ({ }); export const text = (text: any) => { - if (!text.length) { - throw new Error("[AdfUtils] Text node cannot be empty"); - } + // if (!text.length) { + // throw new Error("[AdfUtils] Text node cannot be empty"); + // } return { type: 'text', diff --git a/src/Report/BuildIntegration.ts b/src/Report/BuildIntegration.ts index 1925f97..f0bcc23 100644 --- a/src/Report/BuildIntegration.ts +++ b/src/Report/BuildIntegration.ts @@ -76,7 +76,7 @@ export class BuildIntegration { return false; } - process.stdout.write(`Sending report to issue '${request.issue}' ... `); + process.stdout.write(`[BuildIntegration] Sending report to issue '${request.issue}' ... `); const rawResponse = await fetch(updateURL, { method: "POST", @@ -87,10 +87,15 @@ export class BuildIntegration { body: JSON.stringify(request) }); - const content = await rawResponse.json(); - console.log(content); - - return rawResponse.ok; + try { + const content = await rawResponse.json(); + console.log(content); + process.stdout.write(`[BuildIntegration] Response '${content}', OK: ${rawResponse.ok}`); + return rawResponse.ok; + } catch (error) { + process.stdout.write(`[BuildIntegration] Could not parse response.`); + return false; + } } public getBuildTag() { diff --git a/src/Report/JiraBuilder.ts b/src/Report/JiraBuilder.ts index 12461ab..f3c04cb 100644 --- a/src/Report/JiraBuilder.ts +++ b/src/Report/JiraBuilder.ts @@ -1,10 +1,10 @@ import { formatDate } from "./TimeUtils"; import { expand, table, doc, tableRow, tableHeader, p, strong, text, link, nestedExpand } from "./AdfUtils"; -import { getScriptVersion } from "../scope"; import { ReferencedFileInfo } from "../References/IReferenceFinder"; import { TagIdentifier } from "../Scope/FileTagsDatabase"; import { FileInfo } from "./ReportGenerator"; import { Relevancy } from "../Relevancy/Relevancy"; +import { getScriptVersion } from "../scope"; export type TagIdentifierWithRelevancy = TagIdentifier & { relevancy: Relevancy; @@ -56,7 +56,10 @@ export class JiraBuilder { buildTag: string, printToConsole = false, logURL?: string, - ): string { + ): { + adfDocument: string, + comment: string + } { let tableTitle = `'${projectName}' scope tags v${getScriptVersion()} │ ${formatDate(date, "Europe/Warsaw")}`; tableTitle += buildTag ? ` │ ${buildTag}` : ""; @@ -83,9 +86,10 @@ export class JiraBuilder { this.debugPrintADF(adfDocument); } - const jsonReply = JSON.stringify(expandTable) - - return `{adf:display=block}${jsonReply}{adf}`; + return { + adfDocument: JSON.stringify(adfDocument), + comment: JSON.stringify(expandTable) + } } private _getHeaderRow(): any { diff --git a/src/Report/ReportGenerator.ts b/src/Report/ReportGenerator.ts index 5d1cb15..abf7559 100644 --- a/src/Report/ReportGenerator.ts +++ b/src/Report/ReportGenerator.ts @@ -48,20 +48,21 @@ export class ReportGenerator { private _fileTagsDatabase: FileTagsDatabase, private _configFile: ConfigFile, private _referenceFinders: Array, + private _printDebugInfo: boolean = false ) { } - public async generateReportForCommit(commit: Commit, projectName: string, relevancyMap: RelevancyMap): Promise { - return this.generateReportForCommits([commit], projectName, "-", true, relevancyMap); + public async generateReportForCommit(commit: Commit, projectName: string, relevancyMap: RelevancyMap, useGitNatively = false): Promise { + return this.generateReportForCommits([commit], projectName, "-", relevancyMap, useGitNatively); } public async generateReportForCommits( commits: Array, projectName: string = "undefined", jobName: string = "undefined", - printDebugInfo: boolean = false, relevancyMap?: RelevancyMap, + useGitNatively = false ): Promise { - const filesAffectedByCommits: Array = await this._repository.getFileDataForCommits(commits); + const filesAffectedByCommits: Array = await this._repository.getFileDataForCommits(commits, useGitNatively); return new Promise(async (resolve, reject) => { @@ -70,7 +71,7 @@ export class ReportGenerator { const affectedModules = this._getAffectedModules(fileInfoArray); - if (printDebugInfo) { + if (this._printDebugInfo) { console.log("---- File Info ----") console.log(fileInfoArray); console.log("---- Affected Modules ----") @@ -125,14 +126,18 @@ export class ReportGenerator { const commitRelevancyArray = relevancyMap.get(fileData.commitedIn.sha()); if (!commitRelevancyArray) { - console.log(`[ReportGenerator]: No relevancy data for commit '${fileData.commitedIn}'`); + if (this._printDebugInfo) { + console.log(`[ReportGenerator]: No relevancy data for commit '${fileData.commitedIn}'`); + } return null; } const fileRelevancy = commitRelevancyArray.find(entry => entry.path === fileData.newPath); if (!fileRelevancy) { - console.log(`[ReportGenerator]: No relevancy found for file '${fileData.newPath}'`); + if (this._printDebugInfo) { + console.log(`[ReportGenerator]: No relevancy found for file '${fileData.newPath}'`); + } return null; } @@ -153,7 +158,7 @@ export class ReportGenerator { linesRemoved: fileData.linesRemoved, usedIn: this._getUsedIn(fileData, relevancy), relevancy: relevancy, - ignored: this._configFile.isFileExtensionIgnored(fileData.newPath), + ignored: this._fileTagsDatabase.isIgnored(fileData.newPath, this._configFile.getIgnoredFileExtenstions()), }; Logger.pushFileInfo(fileData, fileInfo); @@ -183,7 +188,8 @@ export class ReportGenerator { private _getFileReferences(file: string, relevancy: Relevancy | null): Array { const references: Array = []; - if (!fileExists(file)) { + // Command should be run outside of repo, so check path relative to repo + if (!fileExists(file, this._repository.root)) { return references; } @@ -191,6 +197,7 @@ export class ReportGenerator { if (!referenceFinder.getSupportedFilesExtension().includes(getExtension(file))) { return; } + const foundReferences = referenceFinder.findReferences(file, relevancy); foundReferences.forEach(reference => { @@ -204,7 +211,9 @@ export class ReportGenerator { } }; - console.log(fileReference.toString()); + if (this._printDebugInfo) { + console.log(fileReference.toString()); + } references.push(fileReference); }); @@ -337,7 +346,7 @@ export class ReportGenerator { } }; - public getReportAsJiraComment(report: Report, printToConsole = false): string { + public getReportAsJiraComment(report: Report, jiraBuilder: JiraBuilder, printToConsole = false) { const finalReportTableData: Array = report.allModules .map(moduleReport => { const modulesAndTagsInfo = this._getReferencedTags(moduleReport); @@ -366,7 +375,6 @@ export class ReportGenerator { unusedReferences: untaggedFilesTableRowInfo.unusedReferences, }; - const jiraBuilder = new JiraBuilder(); return jiraBuilder.parseReport( finalReportTableData, untaggedFilesTableRow, @@ -379,6 +387,6 @@ export class ReportGenerator { } public isReportEmpty(report: Report): boolean { - return report.allModules.length === 0; + return report.allModules.length === 0 && report.untaggedFilesAsModule.files.length === 0; } } \ No newline at end of file diff --git a/src/Scope/FileTagsDatabase.ts b/src/Scope/FileTagsDatabase.ts index ac574c6..91d0531 100644 --- a/src/Scope/FileTagsDatabase.ts +++ b/src/Scope/FileTagsDatabase.ts @@ -339,6 +339,15 @@ export class FileTagsDatabase implements IJSONFileDatabase { this._fileTagsDatabaseData.ignoredFiles[ignoredFileIndex] = newPath; } + private _deleteFileFromDatabase(filePath: string) { + delete this._fileTagsDatabaseData.files[filePath]; + } + + private _deleteIgnoredFileFromDatabase(filePath: string) { + const index = this._fileTagsDatabaseData.ignoredFiles.indexOf(filePath); + this._fileTagsDatabaseData.ignoredFiles.splice(index, 1); + } + // Returns files to be tagged public updateDatabaseBasedOnChanges(fileDataArray: FileData[]): FileData[] { fileDataArray.forEach(fileData => { @@ -352,7 +361,11 @@ export class FileTagsDatabase implements IJSONFileDatabase { } // If file deleted - remove from database if (fileData.change === GitDeltaType.DELETED) { - // TODO: Add + if (this.isFileInDatabase(fileData.oldPath)) { + this._deleteFileFromDatabase(fileData.oldPath); + } else if (this.isIgnored(fileData.oldPath)) { + this._deleteIgnoredFileFromDatabase(fileData.oldPath); + } } }); return fileDataArray.filter(fileData => fileData.change !== GitDeltaType.DELETED); @@ -361,4 +374,12 @@ export class FileTagsDatabase implements IJSONFileDatabase { public getPath(): string { return FileTagsDatabase.PATH; } + + public get fileCount(): number { + return Object.keys(this._fileTagsDatabaseData.files).length; + } + + public get ignoredFilesCount(): number { + return this._fileTagsDatabaseData.ignoredFiles.length; + } } diff --git a/src/scope.ts b/src/scope.ts index c94dea1..480cd6b 100644 --- a/src/scope.ts +++ b/src/scope.ts @@ -1,5 +1,4 @@ -#!/usr/bin/env node -import { runUntagCommand } from "./Commands/runUntagCommand"; +#!/usr/bin/env nodeimport { runUntagCommand } from "./Commands/runUntagCommand"; import { runCommitCommand } from "./Commands/runCommitCommand"; import { runAddCommand } from "./Commands/runAddCommand"; import { runVerifyCommand } from "./Commands/runVerifyCommand"; @@ -14,6 +13,7 @@ import { getGitProjectRoot } from "./Git/Project"; import { runSkipVerificationForCommits } from "./Commands/runSkipVerificationAndPushCommand"; import { runLogCommitCommand } from "./Commands/runLogCommitCommand"; import { runSeeCommand } from "./Commands/runSeeCommand"; +import { runUntagCommand } from "./Commands/runUntagCommand"; // Will be needed to get output from script const [, , ...args] = process.argv; @@ -25,6 +25,8 @@ if (!root) { process.exit(1); } +// IMPORTANT: Do not export anything from here, is breaks tests + switch (args[0]) { case "--version": { console.log(getScriptVersion()); @@ -66,7 +68,7 @@ switch (args[0]) { runReportForCommitListCommand(args, root); break; } - case "--find-references": { + case "--find-refs": { runFindReferencesCommand(args, root); break; } diff --git a/test/_repo b/test/_repo index c314fd4..5ce012a 160000 --- a/test/_repo +++ b/test/_repo @@ -1 +1 @@ -Subproject commit c314fd45fb22a6067adcc4160a233e7619ca61f1 +Subproject commit 5ce012a955edd39a8329b3507f2d8f40ef660c45 diff --git a/test/commits/fileData.test.ts b/test/commits/fileData.test.ts index be3f179..3e786da 100644 --- a/test/commits/fileData.test.ts +++ b/test/commits/fileData.test.ts @@ -1,4 +1,4 @@ -import { cloneMockRepositoryToFolder, commitEmptyFiles, makeUniqueFolderForTest } from "../_utils/utils"; +import { cloneMockRepositoryToFolder, commitEmptyFiles, makeUniqueFolderForTest } from "../utils/utils"; import { FileData, GitDeltaType } from "../../src/Git/Types"; describe("Tesing if file data is retrieved correctly", () => { @@ -18,7 +18,7 @@ describe("Tesing if file data is retrieved correctly", () => { const fileDataArray: FileData[] = repo.getFileDataUsingNativeGitCommand(commit); - expect(fileDataArray.length === 1).toBeDefined(); + expect(fileDataArray.length).toBe(1); const ourFileData = fileDataArray[0]; diff --git a/test/commits/verification.test.ts b/test/commits/verification.test.ts index 2d49b42..d6eb8e2 100644 --- a/test/commits/verification.test.ts +++ b/test/commits/verification.test.ts @@ -1,10 +1,13 @@ -import { appendSomeTextToFile, cloneMockRepositoryToFolder, commitEmptyFiles, commitFiles, commitModitication, createEmptyFiles, makeUniqueFolderForTest } from "../_utils/utils"; +import { appendSomeTextToFile, cloneMockRepositoryToFolder, commitEmptyFiles, commitFiles, commitModitication, createEmptyFiles, makeUniqueFolderForTest, mergeBranchToCurrent } from "../utils/utils"; import { VerificationStatus, verifyUnpushedCommits } from "../../src/Commands/runVerifyUnpushedCommitsCommand"; -import { Utils } from "../../src/Scope/Utils"; -import { FileTagsDatabase } from "../../src/Scope/FileTagsDatabase"; +import { GitRepository } from "../../src/Git/GitRepository"; import { ConfigFile } from "../../src/Scope/ConfigFile"; +import { FileTagsDatabase } from "../../src/Scope/FileTagsDatabase"; +import { RelevancyManager } from "../../src/Relevancy/RelevancyManager"; +import { Utils } from "../../src/Scope/Utils"; import { join } from "path"; import { readdirSync } from "fs-extra"; +import { FileData } from "../../src/Git/Types"; describe("Commit verification by scope tags script", () => { @@ -147,4 +150,120 @@ describe("Commit verification by scope tags script", () => { expect(verificationStatus).toBe(VerificationStatus.NOT_VERIFIED); }); + + it("When commit is a merge commit, the commit is marked as merge commit", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const branchToMergeName = "branch-with-some-new-files"; + + mergeBranchToCurrent(REPO_PATH, branchToMergeName); + + const repository = new GitRepository(REPO_PATH); + const config = new ConfigFile(REPO_PATH); + const database = new FileTagsDatabase(REPO_PATH); + const relevancy = new RelevancyManager(); + + const [mergeCommit, realCommit] = await repository.getUnpushedCommits(); + + expect(mergeCommit).toBeDefined(); + expect(realCommit).toBeDefined(); + + const mergeCommitInfo = await repository.verifyCommit(mergeCommit, config, database, relevancy, undefined, true); + const realCommitInfo = await repository.verifyCommit(realCommit, config, database, relevancy, undefined, true); + + expect(mergeCommitInfo.isMergeCommit).toBe(true); + + expect(realCommitInfo.isMergeCommit).toBe(false); + expect(realCommitInfo.isSkipped).toBe(false); + + const verificationStatus = await verifyUnpushedCommits([], REPO_PATH, true); + + expect(verificationStatus).toBe(VerificationStatus.NOT_VERIFIED); + }); + + // Not pretty fix for squashed commits, at least in BitBucket + it("When commit summary has 'Merge', the commit is marked as merge commit", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const testFile = "src/some-file-in-squashed-merge.js"; + + const database = new FileTagsDatabase(REPO_PATH); + const config = new ConfigFile(REPO_PATH); + + const repository = new GitRepository(REPO_PATH); + const relevancy = new RelevancyManager(); + + // Some common merge commit messages + + const commonMergeCommitMessages = [ + "Merge pull request #9 from mhagger/recursive-option", + "Merged in STH-1234-test-branch (pull request #1234)" + ]; + + for (const message of commonMergeCommitMessages) { + await commitModitication( + [testFile], + REPO_PATH, + message + ); + } + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(commonMergeCommitMessages.length); + + for (const commit of unpushedCommits) { + const commitInfo = await repository.verifyCommit(commit, config, database, relevancy, undefined, true); + expect(commitInfo.isMergeCommit).toBe(true); + } + const verificationStatus = await verifyUnpushedCommits([], REPO_PATH, true); + + console.debug(`Verification status: ${Utils.getEnumKeyByEnumValue(VerificationStatus, verificationStatus)}`); + + expect(verificationStatus).toBe(VerificationStatus.VERIFIED); + }); + + it("When multiple commits modifies the same file, but only the last one includes relevancy, all commits are marked as verified", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const filesToModify = [ + "src/tagged-file.js", + "src/tagged-file-with-multiple-modules.js", + "src/file-ignored-by-database.js" + ]; + + const relevancy = new RelevancyManager(); + + await commitModitication(filesToModify, REPO_PATH); + + const repository = await commitModitication(filesToModify, REPO_PATH, `test commit + + __relevancy__[{"path":"src/tagged-file.js","relevancy":"LOW","commit":"__current__"},{"path":"src/tagged-file-with-multiple-modules.js","relevancy":"HIGH","commit":"__current__"}]__relevancy__ + `); + + const unpushedCommits = await repository.getUnpushedCommits(); + + expect(unpushedCommits.length).toBe(2); + + const commitWithRelevancy = unpushedCommits[0]; + const commitWithoutRelevancy = unpushedCommits[1]; + + expect(relevancy.doesCommitMessageHaveRelevancyData(commitWithRelevancy.message())).toBe(true); + expect(relevancy.doesCommitMessageHaveRelevancyData(commitWithoutRelevancy.message())).toBe(false); + + const commitWithRelevancyfileDataArray: FileData[] = repository.getFileDataUsingNativeGitCommand(commitWithRelevancy); + const commitWithoutRelevancyfileDataArray: FileData[] = repository.getFileDataUsingNativeGitCommand(commitWithoutRelevancy); + + expect(commitWithRelevancyfileDataArray.length).toBe(3); + expect(commitWithoutRelevancyfileDataArray.length).toBe(3); + + const verificationStatus = await verifyUnpushedCommits([], REPO_PATH, true); + + console.debug(`Verification status: ${Utils.getEnumKeyByEnumValue(VerificationStatus, verificationStatus)}`); + + expect(verificationStatus).toBe(VerificationStatus.VERIFIED); + }); + }); diff --git a/test/git/nodegit.test.ts b/test/git/nodegit.test.ts index 7d6df50..ec0715a 100644 --- a/test/git/nodegit.test.ts +++ b/test/git/nodegit.test.ts @@ -1,6 +1,6 @@ import { Repository } from "nodegit"; -import { TEST_DATA_FOLDER } from "../_utils/globals"; -import { createFolder } from "../_utils/utils"; +import { TEST_DATA_FOLDER } from "../utils/globals"; +import { createFolder } from "../utils/utils"; const execSync = require('child_process').execSync; describe("nodegit is correctly initialized", () => { diff --git a/test/report/reportGenerator.test.ts b/test/report/reportGenerator.test.ts new file mode 100644 index 0000000..91e1848 --- /dev/null +++ b/test/report/reportGenerator.test.ts @@ -0,0 +1,458 @@ +import { cloneMockRepositoryToFolder, commitModitication, makeUniqueFolderForTest } from "../utils/utils"; +import { GitRepository } from "../../src/Git/GitRepository"; +import { ConfigFile } from "../../src/Scope/ConfigFile"; +import { FileTagsDatabase } from "../../src/Scope/FileTagsDatabase"; +import { TagsDefinitionFile } from "../../src/Scope/TagsDefinitionFile"; +import { TSReferenceFinder } from "../../src/References/TSReferenceFinder"; +import { ReportGenerator } from "../../src/Report/ReportGenerator"; +import { RelevancyManager } from "../../src/Relevancy/RelevancyManager"; +import { Relevancy } from "../../src/Relevancy/Relevancy"; + +// Testing only ReportGenerator class, which is responsible for gathering data for each commit + +const PRINT_DEBUG_INFO = false; + +const initReportGenerator = (root: string) => { + + const repository = new GitRepository(root); + const config = new ConfigFile(root); + const database = new FileTagsDatabase(root); + const tags = new TagsDefinitionFile(root); + const tsReferenceFinder = new TSReferenceFinder(root, "/tsconfig.json"); + + config.load(); + database.load(); + tags.load(); + + return new ReportGenerator(repository, tags, database, config, [tsReferenceFinder], PRINT_DEBUG_INFO); +} + + +describe("Report generation works as expected", async () => { + it("After making a change in a tagged file, the change is correctly described by generated report", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const testFile = "src/tagged-file.js"; + + const repository = await commitModitication([testFile], REPO_PATH); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const generator = initReportGenerator(REPO_PATH); + + const relevancy = new RelevancyManager(); + const relevancyMap = relevancy.loadRelevancyMapFromCommits([commit]); + + const report = await generator.generateReportForCommit(commit, "Project", relevancyMap, true); + + expect(report).toBeDefined(); + + // src/tagged-file.js has 1 tag -> Default module / Tag + + expect(report.allModules.length).toBe(1); + expect(report.untaggedFilesAsModule.files.length).toBe(0); + + const foundModule = report.allModules[0]; + + expect(foundModule.module).toBe("Default module"); + expect(foundModule.files.length).toBe(1); + + const fileInfo = foundModule.files[0]; + + expect(fileInfo.file).toBe(testFile); + expect(fileInfo.tagIdentifiers.length).toBe(1); + expect(fileInfo.tagIdentifiers[0].module).toBe("Default module"); + expect(fileInfo.tagIdentifiers[0].tag).toBe("Tag"); + }); + + it("After making a change with only ignored files, the report does not contain any data", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const ignoredFiles = [ + "src/file-ignored-by-database.js", + "assets/image.jpg", + "assets/newimage.jpg", + ]; + + + const repository = await commitModitication(ignoredFiles, REPO_PATH); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const generator = initReportGenerator(REPO_PATH); + + const relevancy = new RelevancyManager(); + const relevancyMap = relevancy.loadRelevancyMapFromCommits([commit]); + + const report = await generator.generateReportForCommit(commit, "Project", relevancyMap, true); + + expect(report).toBeDefined(); + expect(report.allModules.length).toBe(0); + expect(report.untaggedFilesAsModule.files.length).toBe(0); + }); + + it("After making a change in an untagged file, the change is visible in untagged module", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const testFileCount = 20; + const testFiles: string[] = []; + + for (let i = 0; i < testFileCount; i++) { + testFiles.push(`src/test-file-${i}`); + } + + const repository = await commitModitication(testFiles, REPO_PATH); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const generator = initReportGenerator(REPO_PATH); + + const relevancy = new RelevancyManager(); + const relevancyMap = relevancy.loadRelevancyMapFromCommits([commit]); + + const report = await generator.generateReportForCommit(commit, "Project", relevancyMap, true); + + expect(report).toBeDefined(); + + expect(report.allModules.length).toBe(0); + + expect(report.untaggedFilesAsModule.files.length).toBe(testFileCount); + + testFiles.forEach(file => { + expect(report.untaggedFilesAsModule.files.some(moduleFile => moduleFile.file === file)).toBe(true); + }); + }); + + it("Correctly separates tagged and untagged files in the report", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const files = ["src/tagged-file.js", "src/untagged-file.js"]; + const repository = await commitModitication(files, REPO_PATH); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const generator = initReportGenerator(REPO_PATH); + + const relevancy = new RelevancyManager(); + const relevancyMap = relevancy.loadRelevancyMapFromCommits([commit]); + + const report = await generator.generateReportForCommit(commit, "Project", relevancyMap, true); + + expect(report).toBeDefined(); + expect(report.allModules.length).toBe(1); + + const taggedModule = report.allModules[0]; + expect(taggedModule.files.length).toBe(1); + expect(taggedModule.files[0].file).toBe("src/tagged-file.js"); + + expect(report.untaggedFilesAsModule.files.length).toBe(1); + expect(report.untaggedFilesAsModule.files[0].file).toBe("src/untagged-file.js"); + }); + + it("After making a change in tagged and untagged, their references are correctly reported", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + /** + * File structure description + * + * src/ + * ├─ ts/ + * │ ├─ controllers/ + * │ │ ├─ Controller.ts 3 dependencies: View.ts and Model.ts, 1 tag: Controllers / AController + * │ ├─ models/ + * │ │ ├─ Model.ts 1 dependency: View.ts, 1 tag: Models / AModel + * │ ├─ views/ + * │ │ ├─ View.ts 1 dependency: ModalWindow.ts, 1 tag: Views / AView + * │ │ ├─ ModalWindow.ts No dependencies, no tags + */ + + const filesToModify = [ + "src/ts/controllers/Controller.ts", + "src/ts/models/Model.ts", + "src/ts/views/View.ts", + "src/ts/views/ModalWindow.ts", + ]; + + const repository = await commitModitication(filesToModify, REPO_PATH); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const generator = initReportGenerator(REPO_PATH); + + const relevancy = new RelevancyManager(); + const relevancyMap = relevancy.loadRelevancyMapFromCommits([commit]); + + const report = await generator.generateReportForCommit(commit, "Project", relevancyMap, true); + + expect(report).toBeDefined(); + + expect(report.allModules.length).toBe(3); + + const controllerModule = report.allModules.find(reportModule => reportModule.module === "Controllers"); + const viewsModule = report.allModules.find(reportModule => reportModule.module === "Views"); + const modelsModule = report.allModules.find(reportModule => reportModule.module === "Models"); + + expect(controllerModule).toBeDefined(); + expect(viewsModule).toBeDefined(); + expect(modelsModule).toBeDefined(); + + if (!controllerModule || !viewsModule || !modelsModule) { + return; + } + + expect(controllerModule.files.length).toBe(1); + expect(viewsModule.files.length).toBe(1); + expect(modelsModule.files.length).toBe(1); + + // Controller.ts checks + const controllerFileInfo = controllerModule.files[0]; + + expect(controllerFileInfo.file).toBe("src/ts/controllers/Controller.ts"); + expect(controllerFileInfo.ignored).toBe(false); + expect(controllerFileInfo.tagIdentifiers.length).toBeGreaterThan(0); + expect(controllerFileInfo.tagIdentifiers.some(identifier => identifier.module === "Controllers" && identifier.tag === "AController")).toBe(true); + expect(controllerFileInfo.usedIn.length).toBe(0); + + // Model.ts checks + const modelsFileInfo = modelsModule.files[0]; + + expect(modelsFileInfo.file).toBe("src/ts/models/Model.ts"); + expect(modelsFileInfo.ignored).toBe(false); + expect(modelsFileInfo.tagIdentifiers.length).toBeGreaterThan(0); + expect(modelsFileInfo.tagIdentifiers.some(identifier => identifier.module === "Models" && identifier.tag === "AModel")).toBe(true); + + expect(modelsFileInfo.usedIn.length).toBe(1); + expect(modelsFileInfo.usedIn[0].fileInfo.filename).toBe("src/ts/controllers/Controller.ts"); + expect(modelsFileInfo.usedIn[0].fileInfo.unused).toBe(false); + + // View.ts checks + const viewFileInfo = viewsModule.files[0]; + + expect(viewFileInfo.file).toBe("src/ts/views/View.ts"); + expect(viewFileInfo.ignored).toBe(false); + expect(viewFileInfo.tagIdentifiers.length).toBeGreaterThan(0); + expect(viewFileInfo.tagIdentifiers.some(identifier => identifier.module === "Views" && identifier.tag === "AView")).toBe(true); + + expect(viewFileInfo.usedIn.length).toBe(2); + + const controllerDependency = viewFileInfo.usedIn.find(file => file.fileInfo.filename === "src/ts/controllers/Controller.ts"); + const modelDependency = viewFileInfo.usedIn.find(file => file.fileInfo.filename === "src/ts/models/Model.ts"); + + expect(controllerDependency).toBeDefined(); + expect(modelDependency).toBeDefined(); + + if (!controllerDependency || !modelDependency) { + return; + } + + expect(controllerDependency.fileInfo.filename).toBe("src/ts/controllers/Controller.ts"); + expect(controllerDependency.fileInfo.unused).toBe(false); + + expect(modelDependency.fileInfo.filename).toBe("src/ts/models/Model.ts"); + expect(modelDependency.fileInfo.unused).toBe(false); + + // Check if ModalWindow is marked as untagged + expect(report.untaggedFilesAsModule.files.length).toBe(1); + expect(report.untaggedFilesAsModule.files[0].file).toBe("src/ts/views/ModalWindow.ts"); + expect(report.untaggedFilesAsModule.files[0].ignored).toBe(false); + expect(report.untaggedFilesAsModule.files[0].tagIdentifiers.length).toBe(0); + + expect(report.untaggedFilesAsModule.files[0].usedIn.length).toBe(1); + + const viewDependency = report.untaggedFilesAsModule.files[0].usedIn[0]; + + expect(viewDependency.fileInfo.filename).toBe("src/ts/views/View.ts"); + expect(viewDependency.fileInfo.unused).toBe(false); + expect(viewDependency.tagIdentifiers.some(identifier => identifier.module === "Views" && identifier.tag === "AView")).toBe(true); + }); + + it("Circular dependencies are reported correctly", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + /** + * File structure description + * + * src/ + * ├─ ts/ + * │ ├─ circularDependency/ + * │ │ ├─ ModuleA.ts 1 dependency: ModuleB.ts, 1 tag: Circular Dependency Test (Module A) / Module A + * │ │ ├─ ModuleB.ts 1 dependency: ModuleA.ts, 1 tag: Circular Dependency Test (Module B) / Module B + */ + + const filesToModify = [ + "src/ts/circularDependency/ModuleA.ts", + "src/ts/circularDependency/ModuleB.ts", + ]; + + const repository = await commitModitication(filesToModify, REPO_PATH); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const generator = initReportGenerator(REPO_PATH); + + const relevancy = new RelevancyManager(); + const relevancyMap = relevancy.loadRelevancyMapFromCommits([commit]); + + const report = await generator.generateReportForCommit(commit, "Project", relevancyMap, true); + + expect(report).toBeDefined(); + + expect(report.allModules.length).toBe(2); + + const circularTestModuleA = report.allModules.find(reportModule => reportModule.module === "Circular Dependency Test (Module A)"); + const circularTestModuleB = report.allModules.find(reportModule => reportModule.module === "Circular Dependency Test (Module B)"); + + expect(circularTestModuleA).toBeDefined(); + expect(circularTestModuleB).toBeDefined(); + + if (!circularTestModuleA || !circularTestModuleB) { + return; + } + + expect(circularTestModuleA.files.length).toBe(1); + expect(circularTestModuleA.files[0].file).toBe("src/ts/circularDependency/ModuleA.ts"); + expect(circularTestModuleA.files[0].ignored).toBe(false); + expect(circularTestModuleA.files[0].tagIdentifiers.length).toBe(1); + expect(circularTestModuleA.files[0].tagIdentifiers.some(identifier => identifier.tag === "Module A" && identifier.module === "Circular Dependency Test (Module A)")).toBe(true); + expect(circularTestModuleA.files[0].usedIn.length).toBe(1); + expect(circularTestModuleA.files[0].usedIn[0].fileInfo.filename).toBe("src/ts/circularDependency/ModuleB.ts"); + expect(circularTestModuleA.files[0].usedIn[0].fileInfo.unused).toBe(false); + + expect(circularTestModuleB.files.length).toBe(1); + expect(circularTestModuleB.files[0].file).toBe("src/ts/circularDependency/ModuleB.ts"); + expect(circularTestModuleB.files[0].ignored).toBe(false); + expect(circularTestModuleB.files[0].tagIdentifiers.length).toBe(1); + expect(circularTestModuleB.files[0].tagIdentifiers.some(identifier => identifier.tag === "Module B" && identifier.module === "Circular Dependency Test (Module B)")).toBe(true); + expect(circularTestModuleB.files[0].usedIn.length).toBe(1); + expect(circularTestModuleB.files[0].usedIn[0].fileInfo.filename).toBe("src/ts/circularDependency/ModuleA.ts"); + expect(circularTestModuleB.files[0].usedIn[0].fileInfo.unused).toBe(false); + + // Same for module B + }); + + it("Correctly reports files with multiple tags", async () => { + + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + /** + Has following tags: + - Default module / Second + - Default module / Tag + - Default module 2 / Tag of default module 2 + */ + + const fileToModify = "src/tagged-file-with-multiple-modules.js"; + + const repository = await commitModitication([fileToModify], REPO_PATH); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const generator = initReportGenerator(REPO_PATH); + + const relevancy = new RelevancyManager(); + const relevancyMap = relevancy.loadRelevancyMapFromCommits([commit]); + + const report = await generator.generateReportForCommit(commit, "Project", relevancyMap, true); + + expect(report).toBeDefined(); + expect(report.allModules.length).toBe(2); + expect(report.untaggedFilesAsModule.files.length).toBe(0); + + const defaultModule = report.allModules.find(reportModule => reportModule.module === "Default module"); + const defaultModule2 = report.allModules.find(reportModule => reportModule.module === "Default module 2"); + + expect(defaultModule).toBeDefined(); + expect(defaultModule2).toBeDefined(); + + if (!defaultModule || !defaultModule2) { + return; + } + + expect(defaultModule.files.length).toBe(1); + expect(defaultModule.files[0].file).toBe("src/tagged-file-with-multiple-modules.js"); + expect(defaultModule.files[0].ignored).toBe(false); + expect(defaultModule.files[0].usedIn.length).toBe(0); + // 3, because same FileInfo is shared between modules + expect(defaultModule.files[0].tagIdentifiers.length).toBe(3); + expect(defaultModule.files[0].tagIdentifiers.some(identifier => identifier.tag === "Tag" && identifier.module === "Default module")).toBe(true); + expect(defaultModule.files[0].tagIdentifiers.some(identifier => identifier.tag === "Second" && identifier.module === "Default module")).toBe(true); + + expect(defaultModule2.files.length).toBe(1); + expect(defaultModule2.files[0].file).toBe("src/tagged-file-with-multiple-modules.js"); + expect(defaultModule2.files[0].ignored).toBe(false); + expect(defaultModule2.files[0].usedIn.length).toBe(0); + // 3, because same FileInfo is shared between modules + expect(defaultModule2.files[0].tagIdentifiers.length).toBe(3); + expect(defaultModule2.files[0].tagIdentifiers.some(identifier => identifier.tag === "Tag of default module 2" && identifier.module === "Default module 2")).toBe(true); + }); + + it("After making a change with defined relevancy, the relevancy is present in generated report", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const repository = await commitModitication( + [ + "src/tagged-file.js", + "src/untagged-file.js" + ], + REPO_PATH, + `Automatic commit + + __relevancy__[{"path":"src/tagged-file.js","relevancy":"HIGH","commit":"__current__"},{"path":"src/untagged-file.js","relevancy":"LOW","commit":"__current__"}]__relevancy__ + `); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const generator = initReportGenerator(REPO_PATH); + + const relevancy = new RelevancyManager(); + const relevancyMap = relevancy.loadRelevancyMapFromCommits([commit]); + + const report = await generator.generateReportForCommit(commit, "Project", relevancyMap, true); + + expect(report).toBeDefined(); + expect(report.allModules.length).toBe(1); + + const taggedModule = report.allModules[0]; + expect(taggedModule.files.length).toBe(1); + expect(taggedModule.files[0].file).toBe("src/tagged-file.js"); + expect(taggedModule.files[0].relevancy).toBe(Relevancy.HIGH); + + expect(report.untaggedFilesAsModule.files.length).toBe(1); + expect(report.untaggedFilesAsModule.files[0].file).toBe("src/untagged-file.js"); + expect(report.untaggedFilesAsModule.files[0].relevancy).toBe(Relevancy.LOW); + }); + +}); + diff --git a/test/scope/databaseFile.test.ts b/test/scope/databaseFile.test.ts new file mode 100644 index 0000000..0698cee --- /dev/null +++ b/test/scope/databaseFile.test.ts @@ -0,0 +1,104 @@ +import { GitRepository } from "../../src/Git/GitRepository"; +import { GitDeltaType } from "../../src/Git/Types"; +import { FileTagsDatabase } from "../../src/Scope/FileTagsDatabase"; +import { cloneMockRepositoryToFolder, commitFilesUsingGitNatively, deleteFiles, makeUniqueFolderForTest, renameFiles } from "../utils/utils"; + +describe("Database file", () => { + it("After files are deleted, the information about them is purged from database", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const savedDatabase = new FileTagsDatabase(REPO_PATH); + + savedDatabase.load(); + + const filesToDelete = [ + "src/tagged-file.js", + "src/file-ignored-by-database.js" + ]; + + deleteFiles(filesToDelete, REPO_PATH); + commitFilesUsingGitNatively(filesToDelete, REPO_PATH, "delete commit"); + + const repository = new GitRepository(REPO_PATH); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const fileDataArray = await repository.getFileDataForCommits([commit], true); + + expect(fileDataArray.length).toBe(2); + + expect(fileDataArray[0].change).toBe(GitDeltaType.DELETED); + expect(fileDataArray[1].change).toBe(GitDeltaType.DELETED); + + const newDatabase = new FileTagsDatabase(REPO_PATH); + + newDatabase.load(); + newDatabase.updateDatabaseBasedOnChanges(fileDataArray); + + // Check if the files were deleted + + expect(savedDatabase.isFileInDatabase("src/tagged-file.js")).toBe(true); + expect(newDatabase.isFileInDatabase("src/tagged-file.js")).toBe(false); + + expect(savedDatabase.isIgnored("src/file-ignored-by-database.js")).toBe(true); + expect(newDatabase.isIgnored("src/file-ignored-by-database.js")).toBe(false); + + expect(newDatabase.fileCount).toBe(savedDatabase.fileCount - 1); + expect(newDatabase.ignoredFilesCount).toBe(savedDatabase.ignoredFilesCount - 1); + }); + + it("After files are renamed, the information about them is purged from database", async () => { + const FOLDER_PATH = makeUniqueFolderForTest(); + const REPO_PATH = cloneMockRepositoryToFolder(FOLDER_PATH); + + const savedDatabase = new FileTagsDatabase(REPO_PATH); + + savedDatabase.load(); + + const filesToRename = [ + ["src/tagged-file.js", "src/tagged-file-renamed.js"], + ["src/file-ignored-by-database.js", "src/file-ignored-by-database-renamed.js"], + ]; + + renameFiles(filesToRename, REPO_PATH); + commitFilesUsingGitNatively(filesToRename.map(files => files[1]), REPO_PATH, "rename commit"); + + const repository = new GitRepository(REPO_PATH); + + const unpushedCommits = await repository.getUnpushedCommits(); + expect(unpushedCommits.length).toBe(1); + + const commit = unpushedCommits[0]; + + const fileDataArray = await repository.getFileDataForCommits([commit], true); + + expect(fileDataArray.length).toBe(2); + + expect(fileDataArray[0].change).toBe(GitDeltaType.RENAMED); + expect(fileDataArray[1].change).toBe(GitDeltaType.RENAMED); + + const newDatabase = new FileTagsDatabase(REPO_PATH); + + newDatabase.load(); + newDatabase.updateDatabaseBasedOnChanges(fileDataArray); + + // Check if the files were deleted + + expect(savedDatabase.isFileInDatabase("src/tagged-file.js")).toBe(true); + expect(savedDatabase.isFileInDatabase("src/tagged-file-renamed.js")).toBe(false); + expect(newDatabase.isFileInDatabase("src/tagged-file.js")).toBe(false); + expect(newDatabase.isFileInDatabase("src/tagged-file-renamed.js")).toBe(true); + + expect(savedDatabase.isIgnored("src/file-ignored-by-database.js")).toBe(true); + expect(savedDatabase.isIgnored("src/file-ignored-by-database-renamed.js")).toBe(false); + expect(newDatabase.isIgnored("src/file-ignored-by-database.js")).toBe(false); + expect(newDatabase.isIgnored("src/file-ignored-by-database-renamed.js")).toBe(true); + + expect(newDatabase.fileCount).toBe(savedDatabase.fileCount); + expect(newDatabase.ignoredFilesCount).toBe(savedDatabase.ignoredFilesCount); + }); +}); diff --git a/test/scope/scopeFolder.test.ts b/test/scope/scopeFolder.test.ts index 1d8b317..4dd8033 100644 --- a/test/scope/scopeFolder.test.ts +++ b/test/scope/scopeFolder.test.ts @@ -1,5 +1,5 @@ -import { TEST_DATA_FOLDER } from "../_utils/globals"; -import { createFolder } from "../_utils/utils"; +import { TEST_DATA_FOLDER } from "../utils/globals"; +import { createFolder } from "../utils/utils"; import { ensureScopeFolderExists, scopeFolderExists } from "../../src/FileSystem/fileSystemUtils"; import { existsSync, mkdirSync } from "fs"; import { join } from "path"; diff --git a/test/scope/tagsDefinitionFile.test.ts b/test/scope/tagsDefinitionFile.test.ts index 08505a4..106bfc6 100644 --- a/test/scope/tagsDefinitionFile.test.ts +++ b/test/scope/tagsDefinitionFile.test.ts @@ -1,4 +1,4 @@ -import { cloneMockRepositoryToFolder, makeUniqueFolderForTest } from "../_utils/utils"; +import { cloneMockRepositoryToFolder, makeUniqueFolderForTest } from "../utils/utils"; import { join } from "path"; import { TagsDatabaseType, TagsDefinitionFile } from "../../src/Scope/TagsDefinitionFile"; import { JSONFile } from "../../src/FileSystem/JSONFile"; diff --git a/test/_utils/globals.ts b/test/utils/globals.ts similarity index 100% rename from test/_utils/globals.ts rename to test/utils/globals.ts diff --git a/test/_utils/utils.ts b/test/utils/utils.ts similarity index 62% rename from test/_utils/utils.ts rename to test/utils/utils.ts index d93e820..6ffcc5e 100644 --- a/test/_utils/utils.ts +++ b/test/utils/utils.ts @@ -1,10 +1,11 @@ import { resolve, join, sep, posix } from "path"; import { appendFileSync, existsSync, mkdirSync } from "fs"; - import { MOCK_REMOTE_URL, MOCK_REPOSITORY, TEST_DATA_FOLDER } from "./globals"; import * as uuid from "uuid"; import { GitRepository } from "../../src/Git/GitRepository"; +import rimraf from "rimraf"; +import { execSync } from "child_process"; // Mocked repository @@ -29,7 +30,6 @@ export const assertTemporaryFolderExists = () => { * @returns {string} Generated repository path */ export const makeUniqueFolderForTest = (): string => { - const testID = uuid.v4(); const tempFolderPath = join(TEST_DATA_FOLDER, testID); mkdirSync(join(TEST_DATA_FOLDER, testID)); @@ -58,6 +58,21 @@ export const cloneMockRepositoryToFolder = (parentFolder: string): string => { export const getRandomUUID = () => uuid.v4(); +export const mergeBranchToCurrent = (repositoryPath: string, branchName: string): void => { + const { execSync } = require("child_process"); + + execSync( + `cd ${repositoryPath} && git merge --no-ff origin/${branchName}`, + (err: any, stdout: any, stderr: any) => { + if (err) { + console.debug(err); + return; + } + console.debug(`stdout: ${stdout}`); + console.debug(`stderr: ${stderr}`); + }); +}; + export const appendSomeTextToFile = (filePath: string) => { appendFileSync(filePath, getRandomUUID()); }; @@ -94,15 +109,57 @@ export const createFolder = (location: string): string => { return folderPath; }; -export const commitModitication = async (fileNames: string[], repositoryPath: string): Promise => { +export const commitModitication = async ( + fileNames: string[], + repositoryPath: string, + commitMessage = "test commit" +): Promise => { fileNames.forEach(fileName => { appendSomeTextToFile(join(repositoryPath, fileName)); }); const repository = new GitRepository(repositoryPath); - const oid = await repository.commitFiles("test commit", fileNames); + const oid = await repository.commitFiles(commitMessage, fileNames); console.debug(`[Modified files] Created new commit ${oid}`); return repository; }; + +export const deleteFiles = ( + fileNames: string[], + repositoryPath: string, +): void => { + for (const fileName of fileNames) { + const filePath = join(repositoryPath, fileName); + if (existsSync(filePath)) { + rimraf.sync(filePath); + } else { + console.debug(`[Delete files] File ${filePath} does not exist`); + } + } +}; + +export const renameFiles = ( + fileNames: string[][], + repositoryPath: string, +): void => { + for (const fileName of fileNames) { + const filePathBefore = join(repositoryPath, fileName[0]); + const filePathAfter = join(repositoryPath, fileName[1]); + if (existsSync(filePathBefore)) { + execSync(`cd ${repositoryPath} && git mv ${fileName[0]} ${fileName[1]}`); + } else { + console.debug(`[Rename files] File ${filePathBefore} does not exist`); + } + } +}; + +// Nodegit does not work well in Jest with deletion / renaming +export const commitFilesUsingGitNatively = ( + fileNames: string[], + repositoryPath: string, + commitMessage = "test commit" +) => { + execSync(`cd ${repositoryPath} && git add ${fileNames.join(" ")} && git commit -m "${commitMessage}"`); +}; \ No newline at end of file