From b717aa9f47b3b780bbf93d496adc6f4c465fef82 Mon Sep 17 00:00:00 2001 From: Geoffrey Testelin Date: Mon, 8 Mar 2021 11:56:52 +0100 Subject: [PATCH] chore(logs): enhance the logs (#358) * docs(only-labels): enhance the docs and fix duplicate (#341) * docs(only-labels): remove duplicated option and improve descriptions a bad rebase happend * docs(readme): use a multi-line array and remove the optional column the option column was not helpful since each value is optional the multi-line array will allow to have a better UI in small devices and basically in GitHub too due to the max-width * style(readme): break line for the statistics * docs(readme): add a better description for the ascending option * docs(action): add missing punctuation * build(deps-dev): bump @typescript-eslint/eslint-plugin (#342) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.15.2 to 4.16.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.16.1/packages/eslint-plugin) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump @octokit/rest from 18.3.0 to 18.3.2 (#350) Bumps [@octokit/rest](https://github.com/octokit/rest.js) from 18.3.0 to 18.3.2. - [Release notes](https://github.com/octokit/rest.js/releases) - [Commits](https://github.com/octokit/rest.js/compare/v18.3.0...v18.3.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * test: add more coverage for the stale label behaviour (#352) (#15) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(logs): add logs for the milestones * chore(errors): use actions error instead of throw errors * chore(logs): enhance the logs and add some colors tl;dr: blue for values, megenta for options, white for messages, yellow light for warnings, yellow for milestones and green for success still a WIP but I wish to confirm this before continuing @hross is it ok for you? * chore(index): update the index * chore(ci): use npm ci instead of npm i * chore(logs): removed some useless logs * refactor(issues): remove useless check * chore(statistics): show the real count of fetched issues * refactor(operations): use a class to handle the operations left closes #361 * chore(logs): include the total number of issues in the log for a batch Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- README.md | 104 +- .../constants/default-processor-options.ts | 2 +- dist/index.js | 2801 ++++++++++++++++- package-lock.json | 65 +- package.json | 4 +- src/classes/assignees.ts | 4 +- src/classes/issues-processor.ts | 99 +- src/classes/loggers/issue-logger.ts | 37 +- src/classes/loggers/logger.ts | 25 +- src/classes/milestones.ts | 200 +- src/classes/operations.ts | 33 + src/classes/statistics.ts | 14 +- src/enums/option.ts | 44 + src/main.ts | 13 +- 15 files changed, 3217 insertions(+), 230 deletions(-) create mode 100644 src/classes/operations.ts create mode 100644 src/enums/option.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 699ef3c7..fcd39da7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v2 - run: | - npm install + npm ci npm run all test: # make sure the action works on a clean machine without building runs-on: ubuntu-latest diff --git a/README.md b/README.md index 3709c6ab..8409b591 100644 --- a/README.md +++ b/README.md @@ -2,54 +2,68 @@ Warns and then closes issues and PRs that have had no activity for a specified amount of time. -### Arguments +## All options + +### List of options Every argument is optional. -| Input | Description | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| `repo-token` | PAT(Personal Access Token) for authorizing repository.
_Defaults to **${{ github.token }}**_. | -| `days-before-stale` | Idle number of days before marking an issue/PR as stale.
_Defaults to **60**_. | -| `days-before-issue-stale` | Idle number of days before marking an issue as stale.
_Override `days-before-stale`_. | -| `days-before-pr-stale` | Idle number of days before marking an PR as stale.
_Override `days-before-stale`_. | -| `days-before-close` | Idle number of days before closing an stale issue/PR.
_Defaults to **7**_. | -| `days-before-issue-close` | Idle number of days before closing an stale issue.
_Override `days-before-close`_. | -| `days-before-pr-close` | Idle number of days before closing an stale PR.
_Override `days-before-close`_. | -| `stale-issue-message` | Message to post on the stale issue. | -| `stale-pr-message` | Message to post on the stale PR. | -| `close-issue-message` | Message to post on the stale issue while closing it. | -| `close-pr-message` | Message to post on the stale PR while closing it. | -| `stale-issue-label` | Label to apply on the stale issue.
_Defaults to **Stale**_. | -| `close-issue-label` | Label to apply on closing issue (automatically removed if no longer closed nor locked). | -| `stale-pr-label` | Label to apply on the stale PR.
_Defaults to **Stale**_. | -| `close-pr-label` | Label to apply on the closing PR (automatically removed if no longer closed nor locked). | -| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | -| `exempt-pr-labels` | Labels on the PR exempted from being marked as stale. | -| `only-labels` | Only issues and PRs with ALL these labels are checked. Separate multiple labels with commas (eg. "question,answered"). | -| `only-issue-labels` | Only issues with ALL these labels are checked. Separate multiple labels with commas (eg. "question,answered").
_Override `only-labels`_. | -| `only-pr-labels` | Only PRs with ALL these labels are checked. Separate multiple labels with commas (eg. "question,answered").
_Override `only-labels`_. | -| `any-of-labels` | Only issues and PRs with ANY of these labels are checked. Separate multiple labels with commas (eg. "incomplete,waiting-feedback"). | -| `operations-per-run` | Maximum number of operations per run (GitHub API CRUD related).
_Defaults to **30**_. | -| `remove-stale-when-updated` | Remove stale label from issue/PR on updates or comments.
_Defaults to **true**_. | -| `debug-only` | Dry-run on action.
_Defaults to **false**_. | -| `ascending` | Order to get issues/PR (true is ascending, false is descending).
_Defaults to **false**_. | -| `skip-stale-issue-message` | Skip adding stale message on stale issue.
_Defaults to **false**_. | -| `skip-stale-pr-message` | Skip adding stale message on stale PR.
_Defaults to **false**_. | -| `start-date` | The date used to skip the stale action on issue/PR created before it (ISO 8601 or RFC 2822). | -| `delete-branch` | Delete the git branch after closing a stale pull request.
_Defaults to **false**_. | -| `exempt-milestones` | Milestones on an issue or a PR exempted from being marked as stale. | -| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale.
_Override `exempt-milestones`_. | -| `exempt-pr-milestones` | Milestones on the PR exempted from being marked as stale.
_Override `exempt-milestones`_. | -| `exempt-all-milestones` | Exempt all issues and PRs with milestones from being marked as stale.
_Priority over `exempt-milestones` rules_. | -| `exempt-all-issue-milestones` | Exempt all issues with milestones from being marked as stale.
_Override `exempt-all-milestones`_. | -| `exempt-all-pr-milestones` | Exempt all PRs with milestones from being marked as stale.
_Override `exempt-all-milestones`_. | -| `exempt-assignees` | Assignees on an issue or a PR exempted from being marked as stale. | -| `exempt-issue-assignees` | Assignees on an issue exempted from being marked as stale.
_Override `exempt-assignees`_. | -| `exempt-pr-assignees` | Assignees on the PR exempted from being marked as stale.
_Override `exempt-assignees`_. | -| `exempt-all-assignees` | Exempt all issues and PRs with assignees from being marked as stale.
_Priority over `exempt-assignees` rules_. | -| `exempt-all-issue-assignees` | Exempt all issues with assignees from being marked as stale.
_Override `exempt-all-assignees`_. | -| `exempt-all-pr-assignees` | Exempt all PRs with assignees from being marked as stale.
_Override `exempt-all-assignees`_. | -| `enable-statistics` | Display some statistics at the end of the logs regarding the stale workflow (only when the logs are enabled).
_Defaults to **true**_. | +| Input | Description | +| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| `repo-token` | PAT(Personal Access Token) for authorizing repository.
_Defaults to **${{ github.token }}**_. | +| `days-before-stale` | Idle number of days before marking an issue/PR as stale.
_Defaults to **60**_. | +| `days-before-issue-stale` | Idle number of days before marking an issue as stale.
_Override `days-before-stale`_. | +| `days-before-pr-stale` | Idle number of days before marking an PR as stale.
_Override `days-before-stale`_. | +| `days-before-close` | Idle number of days before closing an stale issue/PR.
_Defaults to **7**_. | +| `days-before-issue-close` | Idle number of days before closing an stale issue.
_Override `days-before-close`_. | +| `days-before-pr-close` | Idle number of days before closing an stale PR.
_Override `days-before-close`_. | +| `stale-issue-message` | Message to post on the stale issue. | +| `stale-pr-message` | Message to post on the stale PR. | +| `close-issue-message` | Message to post on the stale issue while closing it. | +| `close-pr-message` | Message to post on the stale PR while closing it. | +| `stale-issue-label` | Label to apply on the stale issue.
_Defaults to **Stale**_. | +| `close-issue-label` | Label to apply on closing issue.
Automatically removed if no longer closed nor locked). | +| `stale-pr-label` | Label to apply on the stale PR.
_Defaults to **Stale**_. | +| `close-pr-label` | Label to apply on the closing PR.
Automatically removed if no longer closed nor locked). | +| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | +| `exempt-pr-labels` | Labels on the PR exempted from being marked as stale. | +| `only-labels` | Only issues and PRs with ALL these labels are checked.
Separate multiple labels with commas (eg. "question,answered"). | +| `only-issue-labels` | Only issues with ALL these labels are checked.
Separate multiple labels with commas (eg. "question,answered").
_Override `only-labels`_. | +| `only-pr-labels` | Only PRs with ALL these labels are checked.
Separate multiple labels with commas (eg. "question,answered").
_Override `only-labels`_. | +| `any-of-labels` | Only issues and PRs with ANY of these labels are checked.
Separate multiple labels with commas (eg. "incomplete,waiting-feedback"). | +| `operations-per-run` | Maximum number of operations per run.
GitHub API CRUD related.
_Defaults to **30**_. | +| `remove-stale-when-updated` | Remove stale label from issue/PR on updates or comments.
_Defaults to **true**_. | +| `debug-only` | Dry-run on action.
_Defaults to **false**_. | +| `ascending` | Order to get issues/PR.
`true` is ascending, `false` is descending.
_Defaults to **false**_. | +| `skip-stale-issue-message` | Skip adding stale message on stale issue.
_Defaults to **false**_. | +| `skip-stale-pr-message` | Skip adding stale message on stale PR.
_Defaults to **false**_. | +| `start-date` | The date used to skip the stale action on issue/PR created before it.
ISO 8601 or RFC 2822. | +| `delete-branch` | Delete the git branch after closing a stale pull request.
_Defaults to **false**_. | +| `exempt-milestones` | Milestones on an issue or a PR exempted from being marked as stale. | +| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale.
_Override `exempt-milestones`_. | +| `exempt-pr-milestones` | Milestones on the PR exempted from being marked as stale.
_Override `exempt-milestones`_. | +| `exempt-all-milestones` | Exempt all issues and PRs with milestones from being marked as stale.
_Priority over `exempt-milestones` rules_. | +| `exempt-all-issue-milestones` | Exempt all issues with milestones from being marked as stale.
_Override `exempt-all-milestones`_. | +| `exempt-all-pr-milestones` | Exempt all PRs with milestones from being marked as stale.
_Override `exempt-all-milestones`_. | +| `exempt-assignees` | Assignees on an issue or a PR exempted from being marked as stale. | +| `exempt-issue-assignees` | Assignees on an issue exempted from being marked as stale.
_Override `exempt-assignees`_. | +| `exempt-pr-assignees` | Assignees on the PR exempted from being marked as stale.
_Override `exempt-assignees`_. | +| `exempt-all-assignees` | Exempt all issues and PRs with assignees from being marked as stale.
_Priority over `exempt-assignees` rules_. | +| `exempt-all-issue-assignees` | Exempt all issues with assignees from being marked as stale.
_Override `exempt-all-assignees`_. | +| `exempt-all-pr-assignees` | Exempt all PRs with assignees from being marked as stale.
_Override `exempt-all-assignees`_. | +| `enable-statistics` | Display some statistics at the end of the logs regarding the stale workflow.
Only when the logs are enabled.
_Defaults to **true**_. | + +### Detailed options + +#### operations-per-run + +Used to limit the number of operations made with the GitHub API to avoid reaching the [rate limit](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting). +Based on your project, your GitHub business plan and the date of the cron job you set for this action, you can increase this limit to a higher number. + +When [debugging](#Debugging), you can set it to a much higher number like `1000` since there will be fewer operations made with the GitHub API. +Only the actor and the batch of issues (100 per batch) will consume the operations. + +Default value: `30` ### Usage diff --git a/__tests__/constants/default-processor-options.ts b/__tests__/constants/default-processor-options.ts index 4c897938..e89a80f6 100644 --- a/__tests__/constants/default-processor-options.ts +++ b/__tests__/constants/default-processor-options.ts @@ -42,5 +42,5 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({ exemptAllAssignees: false, exemptAllIssueAssignees: undefined, exemptAllPrAssignees: undefined, - enableStatistics: false + enableStatistics: true }); diff --git a/dist/index.js b/dist/index.js index cbe4c6af..fa6a1b1d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -36,11 +36,11 @@ class Assignees { } const exemptAssignees = this._getExemptAssignees(); if (exemptAssignees.length === 0) { - this._issueLogger.info(`No option was specified to skip the stale process for this $$type`); + this._issueLogger.info(`No assignee option was specified to skip the stale process for this $$type`); this._logSkip(); return false; } - this._issueLogger.info(`Found ${exemptAssignees.length} assignee${exemptAssignees.length > 1 ? 's' : ''} on this $$type`); + this._issueLogger.info(`Found ${exemptAssignees.length} assignee${exemptAssignees.length > 1 ? 's' : ''} that can exempt stale on this $$type`); const hasExemptAssignee = exemptAssignees.some((exemptAssignee) => this._hasAssignee(exemptAssignee)); if (!hasExemptAssignee) { this._issueLogger.info('No assignee on this $$type can exempt the stale process'); @@ -191,6 +191,25 @@ exports.Issue = Issue; "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -200,14 +219,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssuesProcessor = void 0; +const core = __importStar(__nccwpck_require__(2186)); const github_1 = __nccwpck_require__(5438); +const chalk_1 = __importDefault(__nccwpck_require__(8818)); +const option_1 = __nccwpck_require__(5931); const get_humanized_date_1 = __nccwpck_require__(965); const is_date_more_recent_than_1 = __nccwpck_require__(1473); const is_valid_date_1 = __nccwpck_require__(891); const is_labeled_1 = __nccwpck_require__(6792); -const is_pull_request_1 = __nccwpck_require__(5400); const should_mark_when_stale_1 = __nccwpck_require__(2461); const words_to_list_1 = __nccwpck_require__(1883); const assignees_1 = __nccwpck_require__(7236); @@ -215,6 +239,7 @@ const issue_1 = __nccwpck_require__(4783); const issue_logger_1 = __nccwpck_require__(2984); const logger_1 = __nccwpck_require__(6212); const milestones_1 = __nccwpck_require__(4601); +const operations_1 = __nccwpck_require__(7957); const statistics_1 = __nccwpck_require__(3334); /*** * Handle processing of issues for staleness/closure. @@ -222,19 +247,20 @@ const statistics_1 = __nccwpck_require__(3334); class IssuesProcessor { constructor(options) { this._logger = new logger_1.Logger(); - this._operationsLeft = 0; this.staleIssues = []; this.closedIssues = []; this.deletedBranchIssues = []; this.removedLabelIssues = []; this.options = options; - this._operationsLeft = this.options.operationsPerRun; this.client = github_1.getOctokit(this.options.repoToken); + this._operations = new operations_1.Operations(this.options); + this._logger.info(chalk_1.default.yellow('Starting the stale action process...')); if (this.options.debugOnly) { - this._logger.warning('Executing in debug mode. Debug output will be written but no issues will be processed.'); + this._logger.warning(chalk_1.default.yellowBright('Executing in debug mode!')); + this._logger.warning(chalk_1.default.yellowBright('The debug output will be written but no issues/PRs will be processed.')); } if (this.options.enableStatistics) { - this._statistics = new statistics_1.Statistics(this.options); + this._statistics = new statistics_1.Statistics(); } } static _updatedSince(timestamp, num_days) { @@ -249,10 +275,12 @@ class IssuesProcessor { const issues = yield this.getIssues(page); const actor = yield this.getActor(); if (issues.length <= 0) { - this._logger.info('---'); - (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.setOperationsLeft(this._operationsLeft).logStats(); - this._logger.info('No more issues found to process. Exiting.'); - return this._operationsLeft; + this._logger.info(chalk_1.default.green('No more issues found to process. Exiting...')); + (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.setOperationsLeft(this._operations.getUnconsumedOperationsCount()).logStats(); + return this._operations.getOperationsLeftCount(); + } + else { + this._logger.info(chalk_1.default.yellow(`Processing the batch of issues ${chalk_1.default.cyan(`#${page}`)} containing ${chalk_1.default.cyan(issues.length)} issue${issues.length > 1 ? 's' : ''}...`)); } for (const issue of issues.values()) { const issueLogger = new issue_logger_1.IssueLogger(issue); @@ -317,7 +345,7 @@ class IssuesProcessor { // Expecting that GitHub will always set a creation date on the issues and PRs // But you never know! if (!is_valid_date_1.isValidDate(createdAt)) { - throw new Error(`Invalid issue field: "created_at". Expected a valid date`); + core.setFailed(new Error(`Invalid issue field: "created_at". Expected a valid date`)); } issueLogger.info(`$$type created the ${get_humanized_date_1.getHumanizedDate(createdAt)} (${issue.created_at})`); if (!is_date_more_recent_than_1.isDateMoreRecentThan(createdAt, startDate)) { @@ -350,7 +378,6 @@ class IssuesProcessor { } const milestones = new milestones_1.Milestones(this.options, issue); if (milestones.shouldExemptMilestones()) { - issueLogger.info(`Skipping $$type because it has an exempted milestone`); continue; // don't process exempt milestones } const assignees = new assignees_1.Assignees(this.options, issue); @@ -374,10 +401,12 @@ class IssuesProcessor { yield this._processStaleIssue(issue, staleLabel, actor, closeMessage, closeLabel); } } - if (this._operationsLeft <= 0) { - this._logger.warning('Reached max number of operations to process. Exiting.'); + if (this._operations.hasOperationsLeft()) { + this._logger.warning(chalk_1.default.yellowBright('No more operations left! Exiting...')); + this._logger.warning(chalk_1.default.yellowBright(`If you think that not enough issues were processed you could try to increase the quantity related to the ${this._logger.createOptionLink(option_1.Option.OperationsPerRun)} option which is currently set to ${chalk_1.default.cyan(this.options.operationsPerRun)}`)); return 0; } + this._logger.info(chalk_1.default.green(`Batch ${chalk_1.default.cyan(`#${page}`)} processed.`)); // do the next batch return this.processIssues(page + 1); }); @@ -388,7 +417,7 @@ class IssuesProcessor { return __awaiter(this, void 0, void 0, function* () { // find any comments since date on the given issue try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedIssuesCommentsCount(); const comments = yield this.client.issues.listComments({ owner: github_1.context.repo.owner, @@ -409,7 +438,7 @@ class IssuesProcessor { return __awaiter(this, void 0, void 0, function* () { let actor; try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); actor = yield this.client.users.getAuthenticated(); } catch (error) { @@ -425,8 +454,7 @@ class IssuesProcessor { // generate type for response const endpoint = this.client.issues.listForRepo; try { - this._operationsLeft -= 1; - (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedIssuesCount(); + this._operations.consumeOperation(); const issueResult = yield this.client.issues.listForRepo({ owner: github_1.context.repo.owner, repo: github_1.context.repo.repo, @@ -435,6 +463,7 @@ class IssuesProcessor { direction: this.options.ascending ? 'asc' : 'desc', page }); + (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedIssuesCount(issueResult.data.length); return issueResult.data.map((issue) => new issue_1.Issue(this.options, issue)); } catch (error) { @@ -450,7 +479,7 @@ class IssuesProcessor { return __awaiter(this, void 0, void 0, function* () { const issueLogger = new issue_logger_1.IssueLogger(issue); issueLogger.info(`Checking for label on $$type`); - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedIssuesEventsCount(); const options = this.client.issues.listEvents.endpoint.merge({ owner: github_1.context.repo.owner, @@ -476,8 +505,7 @@ class IssuesProcessor { issueLogger.info(`$$type marked stale on: ${markedStaleOn}`); const issueHasComments = yield this._hasCommentsSince(issue, markedStaleOn, actor); issueLogger.info(`$$type has been commented on: ${issueHasComments}`); - const isPr = is_pull_request_1.isPullRequest(issue); - const daysBeforeClose = isPr + const daysBeforeClose = issue.isPullRequest ? this._getDaysBeforePrClose() : this._getDaysBeforeIssueClose(); issueLogger.info(`Days before $$type close: ${daysBeforeClose}`); @@ -537,7 +565,7 @@ class IssuesProcessor { } if (!skipMessage) { try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementAddedComment(); yield this.client.issues.createComment({ owner: github_1.context.repo.owner, @@ -551,7 +579,7 @@ class IssuesProcessor { } } try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_b = this._statistics) === null || _b === void 0 ? void 0 : _b.incrementAddedLabel(); (_c = this._statistics) === null || _c === void 0 ? void 0 : _c.incrementStaleIssuesCount(); yield this.client.issues.addLabels({ @@ -578,7 +606,7 @@ class IssuesProcessor { } if (closeMessage) { try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementAddedComment(); yield this.client.issues.createComment({ owner: github_1.context.repo.owner, @@ -593,7 +621,7 @@ class IssuesProcessor { } if (closeLabel) { try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_b = this._statistics) === null || _b === void 0 ? void 0 : _b.incrementAddedLabel(); yield this.client.issues.addLabels({ owner: github_1.context.repo.owner, @@ -607,7 +635,7 @@ class IssuesProcessor { } } try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_c = this._statistics) === null || _c === void 0 ? void 0 : _c.incrementClosedIssuesCount(); yield this.client.issues.update({ owner: github_1.context.repo.owner, @@ -629,7 +657,7 @@ class IssuesProcessor { return; } try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedPullRequestsCount(); const pullRequest = yield this.client.pulls.get({ owner: github_1.context.repo.owner, @@ -660,7 +688,7 @@ class IssuesProcessor { const branch = pullRequest.head.ref; issueLogger.info(`Deleting branch ${branch} from closed $$type`); try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementDeletedBranchesCount(); yield this.client.git.deleteRef({ owner: github_1.context.repo.owner, @@ -680,12 +708,11 @@ class IssuesProcessor { const issueLogger = new issue_logger_1.IssueLogger(issue); issueLogger.info(`Removing label "${label}" from $$type`); this.removedLabelIssues.push(issue); - // @todo remove the debug only to be able to test the code below if (this.options.debugOnly) { return; } try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementDeletedLabelsCount(); yield this.client.issues.removeLabel({ owner: github_1.context.repo.owner, @@ -768,28 +795,13 @@ exports.IssuesProcessor = IssuesProcessor; "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueLogger = void 0; -const core = __importStar(__nccwpck_require__(2186)); +const chalk_1 = __importDefault(__nccwpck_require__(8818)); +const logger_1 = __nccwpck_require__(6212); /** * @description * Each log will prefix the message with the issue number @@ -803,18 +815,19 @@ const core = __importStar(__nccwpck_require__(2186)); * @example * warning('The $$type will stale') => "The pull request will stale" */ -class IssueLogger { +class IssueLogger extends logger_1.Logger { constructor(issue) { + super(); this._issue = issue; } - warning(message) { - core.warning(this._format(message)); + warning(...message) { + super.warning(this._format(...message)); } - info(message) { - core.info(this._format(message)); + info(...message) { + super.info(this._format(...message)); } - error(message) { - core.error(this._format(message)); + error(...message) { + super.error(this._format(...message)); } _replaceTokens(message) { return this._replaceTypeToken(message); @@ -825,13 +838,24 @@ class IssueLogger { .replace(/\$\$type/g, this._issue.isPullRequest ? 'pull request' : 'issue'); } _prefixWithIssueNumber(message) { - return `[#${this._getIssueNumber()}] ${message}`; + return `${this._getPrefix()} ${message}`; } _getIssueNumber() { return this._issue.number; } - _format(message) { - return this._prefixWithIssueNumber(this._replaceTokens(message)); + _format(...message) { + return this._prefixWithIssueNumber(this._replaceTokens(message.join(' '))); + } + _getPrefix() { + return this._issue.isPullRequest + ? this._getPullRequestPrefix() + : this._getIssuePrefix(); + } + _getIssuePrefix() { + return chalk_1.default.red(`[#${this._getIssueNumber()}]`); + } + _getPullRequestPrefix() { + return chalk_1.default.blue(`[#${this._getIssueNumber()}]`); } } exports.IssueLogger = IssueLogger; @@ -863,18 +887,29 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Logger = void 0; const core = __importStar(__nccwpck_require__(2186)); +const chalk_1 = __importDefault(__nccwpck_require__(8818)); +const terminal_link_1 = __importDefault(__nccwpck_require__(1898)); class Logger { - warning(message) { - core.warning(message); + warning(...message) { + core.warning(chalk_1.default.whiteBright(...message)); } - info(message) { - core.info(message); + info(...message) { + core.info(chalk_1.default.whiteBright(...message)); } - error(message) { - core.error(message); + error(...message) { + core.error(chalk_1.default.whiteBright(...message)); + } + createLink(name, link) { + return terminal_link_1.default(name, link); + } + createOptionLink(option) { + return chalk_1.default.magenta(this.createLink(option, `https://github.com/actions/stale#${option}`)); } } exports.Logger = Logger; @@ -894,42 +929,89 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Milestones = void 0; const lodash_deburr_1 = __importDefault(__nccwpck_require__(1601)); const words_to_list_1 = __nccwpck_require__(1883); +const issue_logger_1 = __nccwpck_require__(2984); class Milestones { constructor(options, issue) { this._options = options; this._issue = issue; + this._issueLogger = new issue_logger_1.IssueLogger(issue); } static _cleanMilestone(milestone) { return lodash_deburr_1.default(milestone.toLowerCase()); } shouldExemptMilestones() { + if (!this._issue.milestone) { + this._issueLogger.info('This $$type has no milestone'); + this._logSkip(); + return false; + } if (this._shouldExemptAllMilestones()) { + this._issueLogger.info('Skipping $$type because it has an exempt milestone'); return true; } const exemptMilestones = this._getExemptMilestones(); - return exemptMilestones.some((exemptMilestone) => this._hasMilestone(exemptMilestone)); + if (exemptMilestones.length === 0) { + this._issueLogger.info(`No milestone option was specified to skip the stale process for this $$type`); + this._logSkip(); + return false; + } + this._issueLogger.info(`Found ${exemptMilestones.length} milestone${exemptMilestones.length > 1 ? 's' : ''} that can exempt stale on this $$type`); + const hasExemptMilestone = exemptMilestones.some((exemptMilestone) => this._hasMilestone(exemptMilestone)); + if (!hasExemptMilestone) { + this._issueLogger.info('No milestone on this $$type can exempt the stale process'); + this._logSkip(); + } + else { + this._issueLogger.info('Skipping this $$type because it has an exempt milestone'); + } + return hasExemptMilestone; } _getExemptMilestones() { - return words_to_list_1.wordsToList(this._issue.isPullRequest + return this._issue.isPullRequest ? this._getExemptPullRequestMilestones() - : this._getExemptIssueMilestones()); + : this._getExemptIssueMilestones(); } _getExemptIssueMilestones() { - return this._options.exemptIssueMilestones !== '' - ? this._options.exemptIssueMilestones - : this._options.exemptMilestones; + if (this._options.exemptIssueMilestones === '') { + this._issueLogger.info('The option "exemptIssueMilestones" is disabled. No specific milestone can skip the stale process for this $$type'); + if (this._options.exemptMilestones === '') { + this._issueLogger.info('The option "exemptMilestones" is disabled. No specific milestone can skip the stale process for this $$type'); + return []; + } + const exemptMilestones = words_to_list_1.wordsToList(this._options.exemptMilestones); + this._issueLogger.info(`The option "exemptMilestones" is set. ${exemptMilestones.length} milestone${exemptMilestones.length === 1 ? '' : 's'} can skip the stale process for this $$type`); + return exemptMilestones; + } + const exemptMilestones = words_to_list_1.wordsToList(this._options.exemptIssueMilestones); + this._issueLogger.info(`The option "exemptIssueMilestones" is set. ${exemptMilestones.length} milestone${exemptMilestones.length === 1 ? '' : 's'} can skip the stale process for this $$type`); + return exemptMilestones; } _getExemptPullRequestMilestones() { - return this._options.exemptPrMilestones !== '' - ? this._options.exemptPrMilestones - : this._options.exemptMilestones; + if (this._options.exemptPrMilestones === '') { + this._issueLogger.info('The option "exemptPrMilestones" is disabled. No specific milestone can skip the stale process for this $$type'); + if (this._options.exemptMilestones === '') { + this._issueLogger.info('The option "exemptMilestones" is disabled. No specific milestone can skip the stale process for this $$type'); + return []; + } + const exemptMilestones = words_to_list_1.wordsToList(this._options.exemptMilestones); + this._issueLogger.info(`The option "exemptMilestones" is set. ${exemptMilestones.length} milestone${exemptMilestones.length === 1 ? '' : 's'} can skip the stale process for this $$type`); + return exemptMilestones; + } + const exemptMilestones = words_to_list_1.wordsToList(this._options.exemptPrMilestones); + this._issueLogger.info(`The option "exemptPrMilestones" is set. ${exemptMilestones.length} milestone${exemptMilestones.length === 1 ? '' : 's'} can skip the stale process for this $$type`); + return exemptMilestones; } _hasMilestone(milestone) { if (!this._issue.milestone) { return false; } - return (Milestones._cleanMilestone(milestone) === - Milestones._cleanMilestone(this._issue.milestone.title)); + const cleanMilestone = Milestones._cleanMilestone(milestone); + const isSameMilestone = cleanMilestone === + Milestones._cleanMilestone(this._issue.milestone.title); + if (isSameMilestone) { + this._issueLogger.info(`The milestone "${milestone}" is set on this $$type and is an exempt milestone`); + } + return isSameMilestone; } _shouldExemptAllMilestones() { if (this._issue.milestone) { @@ -941,38 +1023,93 @@ class Milestones { } _shouldExemptAllIssueMilestones() { if (this._options.exemptAllIssueMilestones === true) { + this._issueLogger.info('The option "exemptAllIssueMilestones" is enabled. Any milestone on this $$type will skip the stale process'); return true; } else if (this._options.exemptAllIssueMilestones === false) { + this._issueLogger.info('The option "exemptAllIssueMilestones" is disabled. Only some specific milestones on this $$type will skip the stale process'); return false; } + this._logExemptAllMilestonesOption(); return this._options.exemptAllMilestones; } _shouldExemptAllPullRequestMilestones() { if (this._options.exemptAllPrMilestones === true) { + this._issueLogger.info('The option "exemptAllPrMilestones" is enabled. Any milestone on this $$type will skip the stale process'); return true; } else if (this._options.exemptAllPrMilestones === false) { + this._issueLogger.info('The option "exemptAllPrMilestones" is disabled. Only some specific milestones on this $$type will skip the stale process'); return false; } + this._logExemptAllMilestonesOption(); return this._options.exemptAllMilestones; } + _logExemptAllMilestonesOption() { + if (this._options.exemptAllMilestones) { + this._issueLogger.info('The option "exemptAllMilestones" is enabled. Any milestone on this $$type will skip the stale process'); + } + else { + this._issueLogger.info('The option "exemptAllMilestones" is disabled. Only some specific milestones on this $$type will skip the stale process'); + } + } + _logSkip() { + this._issueLogger.info('Skip the milestones checks'); + } } exports.Milestones = Milestones; /***/ }), -/***/ 3334: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { +/***/ 7957: +/***/ ((__unused_webpack_module, exports) => { "use strict"; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Operations = void 0; +class Operations { + constructor(options) { + this._options = options; + this._operationsLeft = this._options.operationsPerRun; + } + consumeOperation() { + return this.consumeOperations(1); + } + consumeOperations(quantity) { + this._operationsLeft -= quantity; + return this; + } + getUnconsumedOperationsCount() { + return this._options.operationsPerRun - this._operationsLeft; + } + hasOperationsLeft() { + return this._operationsLeft <= 0; + } + getOperationsLeftCount() { + return this._operationsLeft; + } +} +exports.Operations = Operations; + + +/***/ }), + +/***/ 3334: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Statistics = void 0; +const chalk_1 = __importDefault(__nccwpck_require__(8818)); const logger_1 = __nccwpck_require__(6212); class Statistics { - constructor(options) { + constructor() { this._logger = new logger_1.Logger(); this._processedIssuesCount = 0; this._staleIssuesCount = 0; @@ -988,7 +1125,6 @@ class Statistics { this._fetchedIssuesEventsCount = 0; this._fetchedIssuesCommentsCount = 0; this._fetchedPullRequestsCount = 0; - this._options = options; } incrementProcessedIssuesCount(increment = 1) { this._processedIssuesCount += increment; @@ -1003,7 +1139,7 @@ class Statistics { return this; } setOperationsLeft(operationsLeft) { - this._operationsCount = this._options.operationsPerRun - operationsLeft; + this._operationsCount = operationsLeft; return this; } incrementClosedIssuesCount(increment = 1) { @@ -1047,7 +1183,7 @@ class Statistics { return this; } logStats() { - this._logger.info('Statistics'); + this._logger.info(chalk_1.default.yellow.bold('Statistics:')); this._logProcessedIssuesCount(); this._logStaleIssuesCount(); this._logUndoStaleIssuesCount(); @@ -1062,7 +1198,6 @@ class Statistics { this._logFetchedIssuesEventsCount(); this._logFetchedIssuesCommentsCount(); this._logFetchedPullRequestsCount(); - this._logger.info('---'); return this; } _logProcessedIssuesCount() { @@ -1109,13 +1244,69 @@ class Statistics { } _logCount(name, count) { if (count > 0) { - this._logger.info(`${name}: ${count}`); + this._logger.info(`${name}:`, chalk_1.default.cyan(count)); } } } exports.Statistics = Statistics; +/***/ }), + +/***/ 5931: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Option = void 0; +var Option; +(function (Option) { + Option["RepoToken"] = "repo-token"; + Option["StaleIssueMessage"] = "stale-issue-message"; + Option["StalePrMessage"] = "stale-pr-message"; + Option["CloseIssueMessage"] = "close-issue-message"; + Option["ClosePrMessage"] = "close-pr-message"; + Option["DaysBeforeStale"] = "days-before-stale"; + Option["DaysBeforeIssueStale"] = "days-before-issue-stale"; + Option["DaysBeforePrStale"] = "days-before-pr-stale"; + Option["DaysBeforeClose"] = "days-before-close"; + Option["DaysBeforeIssueClose"] = "days-before-issue-close"; + Option["DaysBeforePrClose"] = "days-before-pr-close"; + Option["StaleIssueLabel"] = "stale-issue-label"; + Option["CloseIssueLabel"] = "close-issue-label"; + Option["ExemptIssueLabels"] = "exempt-issue-labels"; + Option["StalePrLabel"] = "stale-pr-label"; + Option["ClosePrLabel"] = "close-pr-label"; + Option["ExemptPrLabels"] = "exempt-pr-labels"; + Option["OnlyLabels"] = "only-labels"; + Option["OnlyIssueLabels"] = "only-issue-labels"; + Option["OnlyPrLabels"] = "only-pr-labels"; + Option["AnyOfLabels"] = "any-of-labels"; + Option["OperationsPerRun"] = "operations-per-run"; + Option["RemoveStaleWhenUpdated"] = "remove-stale-when-updated"; + Option["DebugOnly"] = "debug-only"; + Option["Ascending"] = "ascending"; + Option["SkipStaleIssueMessage"] = "skip-stale-issue-message"; + Option["SkipStalePrMessage"] = "skip-stale-pr-message"; + Option["DeleteBranch"] = "delete-branch"; + Option["StartDate"] = "start-date"; + Option["ExemptMilestones"] = "exempt-milestones"; + Option["ExemptIssueMilestones"] = "exempt-issue-milestones"; + Option["ExemptPrMilestones"] = "exempt-pr-milestones"; + Option["ExemptAllMilestones"] = "exempt-all-milestones"; + Option["ExemptAllIssueMilestones"] = "exempt-all-issue-milestones"; + Option["ExemptAllPrMilestones"] = "exempt-all-pr-milestones"; + Option["ExemptAssignees"] = "exempt-assignees"; + Option["ExemptIssueAssignees"] = "exempt-issue-assignees"; + Option["ExemptPrAssignees"] = "exempt-pr-assignees"; + Option["ExemptAllAssignees"] = "exempt-all-assignees"; + Option["ExemptAllIssueAssignees"] = "exempt-all-issue-assignees"; + Option["ExemptAllPrAssignees"] = "exempt-all-pr-assignees"; + Option["EnableStatistics"] = "enable-statistics"; +})(Option = exports.Option || (exports.Option = {})); + + /***/ }), /***/ 965: @@ -1324,8 +1515,7 @@ function _run() { return __awaiter(this, void 0, void 0, function* () { try { const args = _getAndValidateArgs(); - const processor = new issues_processor_1.IssuesProcessor(args); - yield processor.processIssues(); + yield new issues_processor_1.IssuesProcessor(args).processIssues(); } catch (error) { core.error(error); @@ -1386,14 +1576,14 @@ function _getAndValidateArgs() { 'operations-per-run' ]) { if (isNaN(parseInt(core.getInput(numberInput)))) { - throw Error(`input ${numberInput} did not parse to a valid integer`); + core.setFailed(new Error(`Option "${numberInput}" did not parse to a valid integer`)); } } for (const optionalDateInput of ['start-date']) { // Ignore empty dates because it is considered as the right type for a default value (so a valid one) if (core.getInput(optionalDateInput) !== '') { if (!is_valid_date_1.isValidDate(new Date(core.getInput(optionalDateInput)))) { - throw new Error(`input ${optionalDateInput} did not parse to a valid date`); + core.setFailed(new Error(`Option "${optionalDateInput}" did not parse to a valid date`)); } } } @@ -4914,6 +5104,171 @@ exports.request = request; //# sourceMappingURL=index.js.map +/***/ }), + +/***/ 8512: +/***/ ((module) => { + +"use strict"; + +const ansiEscapes = module.exports; +// TODO: remove this in the next major version +module.exports.default = ansiEscapes; + +const ESC = '\u001B['; +const OSC = '\u001B]'; +const BEL = '\u0007'; +const SEP = ';'; +const isTerminalApp = process.env.TERM_PROGRAM === 'Apple_Terminal'; + +ansiEscapes.cursorTo = (x, y) => { + if (typeof x !== 'number') { + throw new TypeError('The `x` argument is required'); + } + + if (typeof y !== 'number') { + return ESC + (x + 1) + 'G'; + } + + return ESC + (y + 1) + ';' + (x + 1) + 'H'; +}; + +ansiEscapes.cursorMove = (x, y) => { + if (typeof x !== 'number') { + throw new TypeError('The `x` argument is required'); + } + + let ret = ''; + + if (x < 0) { + ret += ESC + (-x) + 'D'; + } else if (x > 0) { + ret += ESC + x + 'C'; + } + + if (y < 0) { + ret += ESC + (-y) + 'A'; + } else if (y > 0) { + ret += ESC + y + 'B'; + } + + return ret; +}; + +ansiEscapes.cursorUp = (count = 1) => ESC + count + 'A'; +ansiEscapes.cursorDown = (count = 1) => ESC + count + 'B'; +ansiEscapes.cursorForward = (count = 1) => ESC + count + 'C'; +ansiEscapes.cursorBackward = (count = 1) => ESC + count + 'D'; + +ansiEscapes.cursorLeft = ESC + 'G'; +ansiEscapes.cursorSavePosition = isTerminalApp ? '\u001B7' : ESC + 's'; +ansiEscapes.cursorRestorePosition = isTerminalApp ? '\u001B8' : ESC + 'u'; +ansiEscapes.cursorGetPosition = ESC + '6n'; +ansiEscapes.cursorNextLine = ESC + 'E'; +ansiEscapes.cursorPrevLine = ESC + 'F'; +ansiEscapes.cursorHide = ESC + '?25l'; +ansiEscapes.cursorShow = ESC + '?25h'; + +ansiEscapes.eraseLines = count => { + let clear = ''; + + for (let i = 0; i < count; i++) { + clear += ansiEscapes.eraseLine + (i < count - 1 ? ansiEscapes.cursorUp() : ''); + } + + if (count) { + clear += ansiEscapes.cursorLeft; + } + + return clear; +}; + +ansiEscapes.eraseEndLine = ESC + 'K'; +ansiEscapes.eraseStartLine = ESC + '1K'; +ansiEscapes.eraseLine = ESC + '2K'; +ansiEscapes.eraseDown = ESC + 'J'; +ansiEscapes.eraseUp = ESC + '1J'; +ansiEscapes.eraseScreen = ESC + '2J'; +ansiEscapes.scrollUp = ESC + 'S'; +ansiEscapes.scrollDown = ESC + 'T'; + +ansiEscapes.clearScreen = '\u001Bc'; + +ansiEscapes.clearTerminal = process.platform === 'win32' ? + `${ansiEscapes.eraseScreen}${ESC}0f` : + // 1. Erases the screen (Only done in case `2` is not supported) + // 2. Erases the whole screen including scrollback buffer + // 3. Moves cursor to the top-left position + // More info: https://www.real-world-systems.com/docs/ANSIcode.html + `${ansiEscapes.eraseScreen}${ESC}3J${ESC}H`; + +ansiEscapes.beep = BEL; + +ansiEscapes.link = (text, url) => { + return [ + OSC, + '8', + SEP, + SEP, + url, + BEL, + text, + OSC, + '8', + SEP, + SEP, + BEL + ].join(''); +}; + +ansiEscapes.image = (buffer, options = {}) => { + let ret = `${OSC}1337;File=inline=1`; + + if (options.width) { + ret += `;width=${options.width}`; + } + + if (options.height) { + ret += `;height=${options.height}`; + } + + if (options.preserveAspectRatio === false) { + ret += ';preserveAspectRatio=0'; + } + + return ret + ':' + buffer.toString('base64') + BEL; +}; + +ansiEscapes.iTerm = { + setCwd: (cwd = process.cwd()) => `${OSC}50;CurrentDir=${cwd}${BEL}`, + + annotation: (message, options = {}) => { + let ret = `${OSC}1337;`; + + const hasX = typeof options.x !== 'undefined'; + const hasY = typeof options.y !== 'undefined'; + if ((hasX || hasY) && !(hasX && hasY && typeof options.length !== 'undefined')) { + throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined'); + } + + message = message.replace(/\|/g, ''); + + ret += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation='; + + if (options.length > 0) { + ret += + (hasX ? + [message, options.length, options.x, options.y] : + [options.length, message]).join('|'); + } else { + ret += message; + } + + return ret + BEL; + } +}; + + /***/ }), /***/ 3682: @@ -5090,6 +5445,1961 @@ function removeHook (state, name, method) { } +/***/ }), + +/***/ 6734: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; +/* module decorator */ module = __nccwpck_require__.nmd(module); + + +const wrapAnsi16 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${code + offset}m`; +}; + +const wrapAnsi256 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${38 + offset};5;${code}m`; +}; + +const wrapAnsi16m = (fn, offset) => (...args) => { + const rgb = fn(...args); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +}; + +const ansi2ansi = n => n; +const rgb2rgb = (r, g, b) => [r, g, b]; + +const setLazyProperty = (object, property, get) => { + Object.defineProperty(object, property, { + get: () => { + const value = get(); + + Object.defineProperty(object, property, { + value, + enumerable: true, + configurable: true + }); + + return value; + }, + enumerable: true, + configurable: true + }); +}; + +/** @type {typeof import('color-convert')} */ +let colorConvert; +const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { + if (colorConvert === undefined) { + colorConvert = __nccwpck_require__(5121); + } + + const offset = isBackground ? 10 : 0; + const styles = {}; + + for (const [sourceSpace, suite] of Object.entries(colorConvert)) { + const name = sourceSpace === 'ansi16' ? 'ansi' : sourceSpace; + if (sourceSpace === targetSpace) { + styles[name] = wrap(identity, offset); + } else if (typeof suite === 'object') { + styles[name] = wrap(suite[targetSpace], offset); + } + } + + return styles; +}; + +function assembleStyles() { + const codes = new Map(); + const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29] + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + + // Bright color + blackBright: [90, 39], + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39] + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + + // Bright color + bgBlackBright: [100, 49], + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49] + } + }; + + // Alias bright black as gray (and grey) + styles.color.gray = styles.color.blackBright; + styles.bgColor.bgGray = styles.bgColor.bgBlackBright; + styles.color.grey = styles.color.blackBright; + styles.bgColor.bgGrey = styles.bgColor.bgBlackBright; + + for (const [groupName, group] of Object.entries(styles)) { + for (const [styleName, style] of Object.entries(group)) { + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; + + group[styleName] = styles[styleName]; + + codes.set(style[0], style[1]); + } + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); + } + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); + + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; + + setLazyProperty(styles.color, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, false)); + setLazyProperty(styles.bgColor, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, true)); + + return styles; +} + +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); + + +/***/ }), + +/***/ 8159: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +/* MIT license */ +/* eslint-disable no-mixed-operators */ +const cssKeywords = __nccwpck_require__(4057); + +// NOTE: conversions should only return primitive values (i.e. arrays, or +// values that give correct `typeof` results). +// do not use box values types (i.e. Number(), String(), etc.) + +const reverseKeywords = {}; +for (const key of Object.keys(cssKeywords)) { + reverseKeywords[cssKeywords[key]] = key; +} + +const convert = { + rgb: {channels: 3, labels: 'rgb'}, + hsl: {channels: 3, labels: 'hsl'}, + hsv: {channels: 3, labels: 'hsv'}, + hwb: {channels: 3, labels: 'hwb'}, + cmyk: {channels: 4, labels: 'cmyk'}, + xyz: {channels: 3, labels: 'xyz'}, + lab: {channels: 3, labels: 'lab'}, + lch: {channels: 3, labels: 'lch'}, + hex: {channels: 1, labels: ['hex']}, + keyword: {channels: 1, labels: ['keyword']}, + ansi16: {channels: 1, labels: ['ansi16']}, + ansi256: {channels: 1, labels: ['ansi256']}, + hcg: {channels: 3, labels: ['h', 'c', 'g']}, + apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, + gray: {channels: 1, labels: ['gray']} +}; + +module.exports = convert; + +// Hide .channels and .labels properties +for (const model of Object.keys(convert)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } + + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } + + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } + + const {channels, labels} = convert[model]; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); +} + +convert.rgb.hsl = function (rgb) { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const min = Math.min(r, g, b); + const max = Math.max(r, g, b); + const delta = max - min; + let h; + let s; + + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } + + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + + const l = (min + max) / 2; + + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); + } + + return [h, s * 100, l * 100]; +}; + +convert.rgb.hsv = function (rgb) { + let rdif; + let gdif; + let bdif; + let h; + let s; + + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const v = Math.max(r, g, b); + const diff = v - Math.min(r, g, b); + const diffc = function (c) { + return (v - c) / 6 / diff + 1 / 2; + }; + + if (diff === 0) { + h = 0; + s = 0; + } else { + s = diff / v; + rdif = diffc(r); + gdif = diffc(g); + bdif = diffc(b); + + if (r === v) { + h = bdif - gdif; + } else if (g === v) { + h = (1 / 3) + rdif - bdif; + } else if (b === v) { + h = (2 / 3) + gdif - rdif; + } + + if (h < 0) { + h += 1; + } else if (h > 1) { + h -= 1; + } + } + + return [ + h * 360, + s * 100, + v * 100 + ]; +}; + +convert.rgb.hwb = function (rgb) { + const r = rgb[0]; + const g = rgb[1]; + let b = rgb[2]; + const h = convert.rgb.hsl(rgb)[0]; + const w = 1 / 255 * Math.min(r, Math.min(g, b)); + + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + + return [h, w * 100, b * 100]; +}; + +convert.rgb.cmyk = function (rgb) { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + + const k = Math.min(1 - r, 1 - g, 1 - b); + const c = (1 - r - k) / (1 - k) || 0; + const m = (1 - g - k) / (1 - k) || 0; + const y = (1 - b - k) / (1 - k) || 0; + + return [c * 100, m * 100, y * 100, k * 100]; +}; + +function comparativeDistance(x, y) { + /* + See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + */ + return ( + ((x[0] - y[0]) ** 2) + + ((x[1] - y[1]) ** 2) + + ((x[2] - y[2]) ** 2) + ); +} + +convert.rgb.keyword = function (rgb) { + const reversed = reverseKeywords[rgb]; + if (reversed) { + return reversed; + } + + let currentClosestDistance = Infinity; + let currentClosestKeyword; + + for (const keyword of Object.keys(cssKeywords)) { + const value = cssKeywords[keyword]; + + // Compute comparative distance + const distance = comparativeDistance(rgb, value); + + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } + } + + return currentClosestKeyword; +}; + +convert.keyword.rgb = function (keyword) { + return cssKeywords[keyword]; +}; + +convert.rgb.xyz = function (rgb) { + let r = rgb[0] / 255; + let g = rgb[1] / 255; + let b = rgb[2] / 255; + + // Assume sRGB + r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92); + g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92); + b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92); + + const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + return [x * 100, y * 100, z * 100]; +}; + +convert.rgb.lab = function (rgb) { + const xyz = convert.rgb.xyz(rgb); + let x = xyz[0]; + let y = xyz[1]; + let z = xyz[2]; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); + + const l = (116 * y) - 16; + const a = 500 * (x - y); + const b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.hsl.rgb = function (hsl) { + const h = hsl[0] / 360; + const s = hsl[1] / 100; + const l = hsl[2] / 100; + let t2; + let t3; + let val; + + if (s === 0) { + val = l * 255; + return [val, val, val]; + } + + if (l < 0.5) { + t2 = l * (1 + s); + } else { + t2 = l + s - l * s; + } + + const t1 = 2 * l - t2; + + const rgb = [0, 0, 0]; + for (let i = 0; i < 3; i++) { + t3 = h + 1 / 3 * -(i - 1); + if (t3 < 0) { + t3++; + } + + if (t3 > 1) { + t3--; + } + + if (6 * t3 < 1) { + val = t1 + (t2 - t1) * 6 * t3; + } else if (2 * t3 < 1) { + val = t2; + } else if (3 * t3 < 2) { + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + } else { + val = t1; + } + + rgb[i] = val * 255; + } + + return rgb; +}; + +convert.hsl.hsv = function (hsl) { + const h = hsl[0]; + let s = hsl[1] / 100; + let l = hsl[2] / 100; + let smin = s; + const lmin = Math.max(l, 0.01); + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + smin *= lmin <= 1 ? lmin : 2 - lmin; + const v = (l + s) / 2; + const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + + return [h, sv * 100, v * 100]; +}; + +convert.hsv.rgb = function (hsv) { + const h = hsv[0] / 60; + const s = hsv[1] / 100; + let v = hsv[2] / 100; + const hi = Math.floor(h) % 6; + + const f = h - Math.floor(h); + const p = 255 * v * (1 - s); + const q = 255 * v * (1 - (s * f)); + const t = 255 * v * (1 - (s * (1 - f))); + v *= 255; + + switch (hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +}; + +convert.hsv.hsl = function (hsv) { + const h = hsv[0]; + const s = hsv[1] / 100; + const v = hsv[2] / 100; + const vmin = Math.max(v, 0.01); + let sl; + let l; + + l = (2 - s) * v; + const lmin = (2 - s) * vmin; + sl = s * vmin; + sl /= (lmin <= 1) ? lmin : 2 - lmin; + sl = sl || 0; + l /= 2; + + return [h, sl * 100, l * 100]; +}; + +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +convert.hwb.rgb = function (hwb) { + const h = hwb[0] / 360; + let wh = hwb[1] / 100; + let bl = hwb[2] / 100; + const ratio = wh + bl; + let f; + + // Wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } + + const i = Math.floor(6 * h); + const v = 1 - bl; + f = 6 * h - i; + + if ((i & 0x01) !== 0) { + f = 1 - f; + } + + const n = wh + f * (v - wh); // Linear interpolation + + let r; + let g; + let b; + /* eslint-disable max-statements-per-line,no-multi-spaces */ + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } + /* eslint-enable max-statements-per-line,no-multi-spaces */ + + return [r * 255, g * 255, b * 255]; +}; + +convert.cmyk.rgb = function (cmyk) { + const c = cmyk[0] / 100; + const m = cmyk[1] / 100; + const y = cmyk[2] / 100; + const k = cmyk[3] / 100; + + const r = 1 - Math.min(1, c * (1 - k) + k); + const g = 1 - Math.min(1, m * (1 - k) + k); + const b = 1 - Math.min(1, y * (1 - k) + k); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.rgb = function (xyz) { + const x = xyz[0] / 100; + const y = xyz[1] / 100; + const z = xyz[2] / 100; + let r; + let g; + let b; + + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + // Assume sRGB + r = r > 0.0031308 + ? ((1.055 * (r ** (1.0 / 2.4))) - 0.055) + : r * 12.92; + + g = g > 0.0031308 + ? ((1.055 * (g ** (1.0 / 2.4))) - 0.055) + : g * 12.92; + + b = b > 0.0031308 + ? ((1.055 * (b ** (1.0 / 2.4))) - 0.055) + : b * 12.92; + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.lab = function (xyz) { + let x = xyz[0]; + let y = xyz[1]; + let z = xyz[2]; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); + + const l = (116 * y) - 16; + const a = 500 * (x - y); + const b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.lab.xyz = function (lab) { + const l = lab[0]; + const a = lab[1]; + const b = lab[2]; + let x; + let y; + let z; + + y = (l + 16) / 116; + x = a / 500 + y; + z = y - b / 200; + + const y2 = y ** 3; + const x2 = x ** 3; + const z2 = z ** 3; + y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; + x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; + z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; + + x *= 95.047; + y *= 100; + z *= 108.883; + + return [x, y, z]; +}; + +convert.lab.lch = function (lab) { + const l = lab[0]; + const a = lab[1]; + const b = lab[2]; + let h; + + const hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; + + if (h < 0) { + h += 360; + } + + const c = Math.sqrt(a * a + b * b); + + return [l, c, h]; +}; + +convert.lch.lab = function (lch) { + const l = lch[0]; + const c = lch[1]; + const h = lch[2]; + + const hr = h / 360 * 2 * Math.PI; + const a = c * Math.cos(hr); + const b = c * Math.sin(hr); + + return [l, a, b]; +}; + +convert.rgb.ansi16 = function (args, saturation = null) { + const [r, g, b] = args; + let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization + + value = Math.round(value / 50); + + if (value === 0) { + return 30; + } + + let ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); + + if (value === 2) { + ansi += 60; + } + + return ansi; +}; + +convert.hsv.ansi16 = function (args) { + // Optimization here; we already know the value and don't need to get + // it converted for us. + return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); +}; + +convert.rgb.ansi256 = function (args) { + const r = args[0]; + const g = args[1]; + const b = args[2]; + + // We use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (r === g && g === b) { + if (r < 8) { + return 16; + } + + if (r > 248) { + return 231; + } + + return Math.round(((r - 8) / 247) * 24) + 232; + } + + const ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); + + return ansi; +}; + +convert.ansi16.rgb = function (args) { + let color = args % 10; + + // Handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; + } + + color = color / 10.5 * 255; + + return [color, color, color]; + } + + const mult = (~~(args > 50) + 1) * 0.5; + const r = ((color & 1) * mult) * 255; + const g = (((color >> 1) & 1) * mult) * 255; + const b = (((color >> 2) & 1) * mult) * 255; + + return [r, g, b]; +}; + +convert.ansi256.rgb = function (args) { + // Handle greyscale + if (args >= 232) { + const c = (args - 232) * 10 + 8; + return [c, c, c]; + } + + args -= 16; + + let rem; + const r = Math.floor(args / 36) / 5 * 255; + const g = Math.floor((rem = args % 36) / 6) / 5 * 255; + const b = (rem % 6) / 5 * 255; + + return [r, g, b]; +}; + +convert.rgb.hex = function (args) { + const integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); + + const string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.hex.rgb = function (args) { + const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + if (!match) { + return [0, 0, 0]; + } + + let colorString = match[0]; + + if (match[0].length === 3) { + colorString = colorString.split('').map(char => { + return char + char; + }).join(''); + } + + const integer = parseInt(colorString, 16); + const r = (integer >> 16) & 0xFF; + const g = (integer >> 8) & 0xFF; + const b = integer & 0xFF; + + return [r, g, b]; +}; + +convert.rgb.hcg = function (rgb) { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const max = Math.max(Math.max(r, g), b); + const min = Math.min(Math.min(r, g), b); + const chroma = (max - min); + let grayscale; + let hue; + + if (chroma < 1) { + grayscale = min / (1 - chroma); + } else { + grayscale = 0; + } + + if (chroma <= 0) { + hue = 0; + } else + if (max === r) { + hue = ((g - b) / chroma) % 6; + } else + if (max === g) { + hue = 2 + (b - r) / chroma; + } else { + hue = 4 + (r - g) / chroma; + } + + hue /= 6; + hue %= 1; + + return [hue * 360, chroma * 100, grayscale * 100]; +}; + +convert.hsl.hcg = function (hsl) { + const s = hsl[1] / 100; + const l = hsl[2] / 100; + + const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l)); + + let f = 0; + if (c < 1.0) { + f = (l - 0.5 * c) / (1.0 - c); + } + + return [hsl[0], c * 100, f * 100]; +}; + +convert.hsv.hcg = function (hsv) { + const s = hsv[1] / 100; + const v = hsv[2] / 100; + + const c = s * v; + let f = 0; + + if (c < 1.0) { + f = (v - c) / (1 - c); + } + + return [hsv[0], c * 100, f * 100]; +}; + +convert.hcg.rgb = function (hcg) { + const h = hcg[0] / 360; + const c = hcg[1] / 100; + const g = hcg[2] / 100; + + if (c === 0.0) { + return [g * 255, g * 255, g * 255]; + } + + const pure = [0, 0, 0]; + const hi = (h % 1) * 6; + const v = hi % 1; + const w = 1 - v; + let mg = 0; + + /* eslint-disable max-statements-per-line */ + switch (Math.floor(hi)) { + case 0: + pure[0] = 1; pure[1] = v; pure[2] = 0; break; + case 1: + pure[0] = w; pure[1] = 1; pure[2] = 0; break; + case 2: + pure[0] = 0; pure[1] = 1; pure[2] = v; break; + case 3: + pure[0] = 0; pure[1] = w; pure[2] = 1; break; + case 4: + pure[0] = v; pure[1] = 0; pure[2] = 1; break; + default: + pure[0] = 1; pure[1] = 0; pure[2] = w; + } + /* eslint-enable max-statements-per-line */ + + mg = (1.0 - c) * g; + + return [ + (c * pure[0] + mg) * 255, + (c * pure[1] + mg) * 255, + (c * pure[2] + mg) * 255 + ]; +}; + +convert.hcg.hsv = function (hcg) { + const c = hcg[1] / 100; + const g = hcg[2] / 100; + + const v = c + g * (1.0 - c); + let f = 0; + + if (v > 0.0) { + f = c / v; + } + + return [hcg[0], f * 100, v * 100]; +}; + +convert.hcg.hsl = function (hcg) { + const c = hcg[1] / 100; + const g = hcg[2] / 100; + + const l = g * (1.0 - c) + 0.5 * c; + let s = 0; + + if (l > 0.0 && l < 0.5) { + s = c / (2 * l); + } else + if (l >= 0.5 && l < 1.0) { + s = c / (2 * (1 - l)); + } + + return [hcg[0], s * 100, l * 100]; +}; + +convert.hcg.hwb = function (hcg) { + const c = hcg[1] / 100; + const g = hcg[2] / 100; + const v = c + g * (1.0 - c); + return [hcg[0], (v - c) * 100, (1 - v) * 100]; +}; + +convert.hwb.hcg = function (hwb) { + const w = hwb[1] / 100; + const b = hwb[2] / 100; + const v = 1 - b; + const c = v - w; + let g = 0; + + if (c < 1) { + g = (v - c) / (1 - c); + } + + return [hwb[0], c * 100, g * 100]; +}; + +convert.apple.rgb = function (apple) { + return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; +}; + +convert.rgb.apple = function (rgb) { + return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; +}; + +convert.gray.rgb = function (args) { + return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; +}; + +convert.gray.hsl = function (args) { + return [0, 0, args[0]]; +}; + +convert.gray.hsv = convert.gray.hsl; + +convert.gray.hwb = function (gray) { + return [0, 100, gray[0]]; +}; + +convert.gray.cmyk = function (gray) { + return [0, 0, 0, gray[0]]; +}; + +convert.gray.lab = function (gray) { + return [gray[0], 0, 0]; +}; + +convert.gray.hex = function (gray) { + const val = Math.round(gray[0] / 100 * 255) & 0xFF; + const integer = (val << 16) + (val << 8) + val; + + const string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.rgb.gray = function (rgb) { + const val = (rgb[0] + rgb[1] + rgb[2]) / 3; + return [val / 255 * 100]; +}; + + +/***/ }), + +/***/ 5121: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const conversions = __nccwpck_require__(8159); +const route = __nccwpck_require__(4663); + +const convert = {}; + +const models = Object.keys(conversions); + +function wrapRaw(fn) { + const wrappedFn = function (...args) { + const arg0 = args[0]; + if (arg0 === undefined || arg0 === null) { + return arg0; + } + + if (arg0.length > 1) { + args = arg0; + } + + return fn(args); + }; + + // Preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +function wrapRounded(fn) { + const wrappedFn = function (...args) { + const arg0 = args[0]; + + if (arg0 === undefined || arg0 === null) { + return arg0; + } + + if (arg0.length > 1) { + args = arg0; + } + + const result = fn(args); + + // We're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (let len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } + + return result; + }; + + // Preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +models.forEach(fromModel => { + convert[fromModel] = {}; + + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); + + const routes = route(fromModel); + const routeModels = Object.keys(routes); + + routeModels.forEach(toModel => { + const fn = routes[toModel]; + + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); + +module.exports = convert; + + +/***/ }), + +/***/ 4663: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const conversions = __nccwpck_require__(8159); + +/* + This function routes a model to all other models. + + all functions that are routed have a property `.conversion` attached + to the returned synthetic function. This property is an array + of strings, each with the steps in between the 'from' and 'to' + color models (inclusive). + + conversions that are not possible simply are not included. +*/ + +function buildGraph() { + const graph = {}; + // https://jsperf.com/object-keys-vs-for-in-with-closure/3 + const models = Object.keys(conversions); + + for (let len = models.length, i = 0; i < len; i++) { + graph[models[i]] = { + // http://jsperf.com/1-vs-infinity + // micro-opt, but this is simple. + distance: -1, + parent: null + }; + } + + return graph; +} + +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + const graph = buildGraph(); + const queue = [fromModel]; // Unshift -> queue -> pop + + graph[fromModel].distance = 0; + + while (queue.length) { + const current = queue.pop(); + const adjacents = Object.keys(conversions[current]); + + for (let len = adjacents.length, i = 0; i < len; i++) { + const adjacent = adjacents[i]; + const node = graph[adjacent]; + + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } + } + } + + return graph; +} + +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} + +function wrapConversion(toModel, graph) { + const path = [graph[toModel].parent, toModel]; + let fn = conversions[graph[toModel].parent][toModel]; + + let cur = graph[toModel].parent; + while (graph[cur].parent) { + path.unshift(graph[cur].parent); + fn = link(conversions[graph[cur].parent][cur], fn); + cur = graph[cur].parent; + } + + fn.conversion = path; + return fn; +} + +module.exports = function (fromModel) { + const graph = deriveBFS(fromModel); + const conversion = {}; + + const models = Object.keys(graph); + for (let len = models.length, i = 0; i < len; i++) { + const toModel = models[i]; + const node = graph[toModel]; + + if (node.parent === null) { + // No possible conversion, or this node is the source model. + continue; + } + + conversion[toModel] = wrapConversion(toModel, graph); + } + + return conversion; +}; + + + +/***/ }), + +/***/ 4057: +/***/ ((module) => { + +"use strict"; + + +module.exports = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; + + +/***/ }), + +/***/ 1538: +/***/ ((module) => { + +"use strict"; + + +module.exports = (flag, argv = process.argv) => { + const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); + const position = argv.indexOf(prefix + flag); + const terminatorPosition = argv.indexOf('--'); + return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); +}; + + +/***/ }), + +/***/ 4955: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +const os = __nccwpck_require__(2087); +const tty = __nccwpck_require__(3867); +const hasFlag = __nccwpck_require__(1538); + +const {env} = process; + +let forceColor; +if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false') || + hasFlag('color=never')) { + forceColor = 0; +} else if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + forceColor = 1; +} + +if ('FORCE_COLOR' in env) { + if (env.FORCE_COLOR === 'true') { + forceColor = 1; + } else if (env.FORCE_COLOR === 'false') { + forceColor = 0; + } else { + forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); + } +} + +function translateLevel(level) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +} + +function supportsColor(haveStream, streamIsTTY) { + if (forceColor === 0) { + return 0; + } + + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } + + if (hasFlag('color=256')) { + return 2; + } + + if (haveStream && !streamIsTTY && forceColor === undefined) { + return 0; + } + + const min = forceColor || 0; + + if (env.TERM === 'dumb') { + return min; + } + + if (process.platform === 'win32') { + // Windows 10 build 10586 is the first Windows release that supports 256 colors. + // Windows 10 build 14931 is the first release that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(osRelease[0]) >= 10 && + Number(osRelease[2]) >= 10586 + ) { + return Number(osRelease[2]) >= 14931 ? 3 : 2; + } + + return 1; + } + + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } + + return min; + } + + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } + + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default + } + } + + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } + + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } + + if ('COLORTERM' in env) { + return 1; + } + + return min; +} + +function getSupportLevel(stream) { + const level = supportsColor(stream, stream && stream.isTTY); + return translateLevel(level); +} + +module.exports = { + supportsColor: getSupportLevel, + stdout: translateLevel(supportsColor(true, tty.isatty(1))), + stderr: translateLevel(supportsColor(true, tty.isatty(2))) +}; + + +/***/ }), + +/***/ 8818: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +const ansiStyles = __nccwpck_require__(6734); +const {stdout: stdoutColor, stderr: stderrColor} = __nccwpck_require__(4955); +const { + stringReplaceAll, + stringEncaseCRLFWithFirstIndex +} = __nccwpck_require__(2415); + +const {isArray} = Array; + +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = [ + 'ansi', + 'ansi', + 'ansi256', + 'ansi16m' +]; + +const styles = Object.create(null); + +const applyOptions = (object, options = {}) => { + if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { + throw new Error('The `level` option should be an integer from 0 to 3'); + } + + // Detect level if not set manually + const colorLevel = stdoutColor ? stdoutColor.level : 0; + object.level = options.level === undefined ? colorLevel : options.level; +}; + +class ChalkClass { + constructor(options) { + // eslint-disable-next-line no-constructor-return + return chalkFactory(options); + } +} + +const chalkFactory = options => { + const chalk = {}; + applyOptions(chalk, options); + + chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_); + + Object.setPrototypeOf(chalk, Chalk.prototype); + Object.setPrototypeOf(chalk.template, chalk); + + chalk.template.constructor = () => { + throw new Error('`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.'); + }; + + chalk.template.Instance = ChalkClass; + + return chalk.template; +}; + +function Chalk(options) { + return chalkFactory(options); +} + +for (const [styleName, style] of Object.entries(ansiStyles)) { + styles[styleName] = { + get() { + const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty); + Object.defineProperty(this, styleName, {value: builder}); + return builder; + } + }; +} + +styles.visible = { + get() { + const builder = createBuilder(this, this._styler, true); + Object.defineProperty(this, 'visible', {value: builder}); + return builder; + } +}; + +const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256']; + +for (const model of usedModels) { + styles[model] = { + get() { + const {level} = this; + return function (...arguments_) { + const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler); + return createBuilder(this, styler, this._isEmpty); + }; + } + }; +} + +for (const model of usedModels) { + const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); + styles[bgModel] = { + get() { + const {level} = this; + return function (...arguments_) { + const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler); + return createBuilder(this, styler, this._isEmpty); + }; + } + }; +} + +const proto = Object.defineProperties(() => {}, { + ...styles, + level: { + enumerable: true, + get() { + return this._generator.level; + }, + set(level) { + this._generator.level = level; + } + } +}); + +const createStyler = (open, close, parent) => { + let openAll; + let closeAll; + if (parent === undefined) { + openAll = open; + closeAll = close; + } else { + openAll = parent.openAll + open; + closeAll = close + parent.closeAll; + } + + return { + open, + close, + openAll, + closeAll, + parent + }; +}; + +const createBuilder = (self, _styler, _isEmpty) => { + const builder = (...arguments_) => { + if (isArray(arguments_[0]) && isArray(arguments_[0].raw)) { + // Called as a template literal, for example: chalk.red`2 + 3 = {bold ${2+3}}` + return applyStyle(builder, chalkTag(builder, ...arguments_)); + } + + // Single argument is hot path, implicit coercion is faster than anything + // eslint-disable-next-line no-implicit-coercion + return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' ')); + }; + + // We alter the prototype because we must return a function, but there is + // no way to create a function with a different prototype + Object.setPrototypeOf(builder, proto); + + builder._generator = self; + builder._styler = _styler; + builder._isEmpty = _isEmpty; + + return builder; +}; + +const applyStyle = (self, string) => { + if (self.level <= 0 || !string) { + return self._isEmpty ? '' : string; + } + + let styler = self._styler; + + if (styler === undefined) { + return string; + } + + const {openAll, closeAll} = styler; + if (string.indexOf('\u001B') !== -1) { + while (styler !== undefined) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + string = stringReplaceAll(string, styler.close, styler.open); + + styler = styler.parent; + } + } + + // We can move both next actions out of loop, because remaining actions in loop won't have + // any/visible effect on parts we add here. Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92 + const lfIndex = string.indexOf('\n'); + if (lfIndex !== -1) { + string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); + } + + return openAll + string + closeAll; +}; + +let template; +const chalkTag = (chalk, ...strings) => { + const [firstString] = strings; + + if (!isArray(firstString) || !isArray(firstString.raw)) { + // If chalk() was called by itself or with a string, + // return the string itself as a string. + return strings.join(' '); + } + + const arguments_ = strings.slice(1); + const parts = [firstString.raw[0]]; + + for (let i = 1; i < firstString.length; i++) { + parts.push( + String(arguments_[i - 1]).replace(/[{}\\]/g, '\\$&'), + String(firstString.raw[i]) + ); + } + + if (template === undefined) { + template = __nccwpck_require__(500); + } + + return template(chalk, parts.join('')); +}; + +Object.defineProperties(Chalk.prototype, styles); + +const chalk = Chalk(); // eslint-disable-line new-cap +chalk.supportsColor = stdoutColor; +chalk.stderr = Chalk({level: stderrColor ? stderrColor.level : 0}); // eslint-disable-line new-cap +chalk.stderr.supportsColor = stderrColor; + +module.exports = chalk; + + +/***/ }), + +/***/ 500: +/***/ ((module) => { + +"use strict"; + +const TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; +const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; +const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; +const ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi; + +const ESCAPES = new Map([ + ['n', '\n'], + ['r', '\r'], + ['t', '\t'], + ['b', '\b'], + ['f', '\f'], + ['v', '\v'], + ['0', '\0'], + ['\\', '\\'], + ['e', '\u001B'], + ['a', '\u0007'] +]); + +function unescape(c) { + const u = c[0] === 'u'; + const bracket = c[1] === '{'; + + if ((u && !bracket && c.length === 5) || (c[0] === 'x' && c.length === 3)) { + return String.fromCharCode(parseInt(c.slice(1), 16)); + } + + if (u && bracket) { + return String.fromCodePoint(parseInt(c.slice(2, -1), 16)); + } + + return ESCAPES.get(c) || c; +} + +function parseArguments(name, arguments_) { + const results = []; + const chunks = arguments_.trim().split(/\s*,\s*/g); + let matches; + + for (const chunk of chunks) { + const number = Number(chunk); + if (!Number.isNaN(number)) { + results.push(number); + } else if ((matches = chunk.match(STRING_REGEX))) { + results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, character) => escape ? unescape(escape) : character)); + } else { + throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); + } + } + + return results; +} + +function parseStyle(style) { + STYLE_REGEX.lastIndex = 0; + + const results = []; + let matches; + + while ((matches = STYLE_REGEX.exec(style)) !== null) { + const name = matches[1]; + + if (matches[2]) { + const args = parseArguments(name, matches[2]); + results.push([name].concat(args)); + } else { + results.push([name]); + } + } + + return results; +} + +function buildStyle(chalk, styles) { + const enabled = {}; + + for (const layer of styles) { + for (const style of layer.styles) { + enabled[style[0]] = layer.inverse ? null : style.slice(1); + } + } + + let current = chalk; + for (const [styleName, styles] of Object.entries(enabled)) { + if (!Array.isArray(styles)) { + continue; + } + + if (!(styleName in current)) { + throw new Error(`Unknown Chalk style: ${styleName}`); + } + + current = styles.length > 0 ? current[styleName](...styles) : current[styleName]; + } + + return current; +} + +module.exports = (chalk, temporary) => { + const styles = []; + const chunks = []; + let chunk = []; + + // eslint-disable-next-line max-params + temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => { + if (escapeCharacter) { + chunk.push(unescape(escapeCharacter)); + } else if (style) { + const string = chunk.join(''); + chunk = []; + chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string)); + styles.push({inverse, styles: parseStyle(style)}); + } else if (close) { + if (styles.length === 0) { + throw new Error('Found extraneous } in Chalk template literal'); + } + + chunks.push(buildStyle(chalk, styles)(chunk.join(''))); + chunk = []; + styles.pop(); + } else { + chunk.push(character); + } + }); + + chunks.push(chunk.join('')); + + if (styles.length > 0) { + const errMessage = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; + throw new Error(errMessage); + } + + return chunks.join(''); +}; + + +/***/ }), + +/***/ 2415: +/***/ ((module) => { + +"use strict"; + + +const stringReplaceAll = (string, substring, replacer) => { + let index = string.indexOf(substring); + if (index === -1) { + return string; + } + + const substringLength = substring.length; + let endIndex = 0; + let returnValue = ''; + do { + returnValue += string.substr(endIndex, index - endIndex) + substring + replacer; + endIndex = index + substringLength; + index = string.indexOf(substring, endIndex); + } while (index !== -1); + + returnValue += string.substr(endIndex); + return returnValue; +}; + +const stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => { + let endIndex = 0; + let returnValue = ''; + do { + const gotCR = string[index - 1] === '\r'; + returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? '\r\n' : '\n') + postfix; + endIndex = index + 1; + index = string.indexOf('\n', endIndex); + } while (index !== -1); + + returnValue += string.substr(endIndex); + return returnValue; +}; + +module.exports = { + stringReplaceAll, + stringEncaseCRLFWithFirstIndex +}; + + /***/ }), /***/ 8932: @@ -7132,6 +9442,295 @@ function onceStrict (fn) { } +/***/ }), + +/***/ 8824: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +const supportsColor = __nccwpck_require__(1507); +const hasFlag = __nccwpck_require__(7362); + +function parseVersion(versionString) { + if (/^\d{3,4}$/.test(versionString)) { + // Env var doesn't always use dots. example: 4601 => 46.1.0 + const m = /(\d{1,2})(\d{2})/.exec(versionString); + return { + major: 0, + minor: parseInt(m[1], 10), + patch: parseInt(m[2], 10) + }; + } + + const versions = (versionString || '').split('.').map(n => parseInt(n, 10)); + return { + major: versions[0], + minor: versions[1], + patch: versions[2] + }; +} + +function supportsHyperlink(stream) { + const {env} = process; + + if ('FORCE_HYPERLINK' in env) { + return !(env.FORCE_HYPERLINK.length > 0 && parseInt(env.FORCE_HYPERLINK, 10) === 0); + } + + if (hasFlag('no-hyperlink') || hasFlag('no-hyperlinks') || hasFlag('hyperlink=false') || hasFlag('hyperlink=never')) { + return false; + } + + if (hasFlag('hyperlink=true') || hasFlag('hyperlink=always')) { + return true; + } + + // If they specify no colors, they probably don't want hyperlinks. + if (!supportsColor.supportsColor(stream)) { + return false; + } + + if (stream && !stream.isTTY) { + return false; + } + + if (process.platform === 'win32') { + return false; + } + + if ('CI' in env) { + return false; + } + + if ('TEAMCITY_VERSION' in env) { + return false; + } + + if ('TERM_PROGRAM' in env) { + const version = parseVersion(env.TERM_PROGRAM_VERSION); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + if (version.major === 3) { + return version.minor >= 1; + } + + return version.major > 3; + // No default + } + } + + if ('VTE_VERSION' in env) { + // 0.50.0 was supposed to support hyperlinks, but throws a segfault + if (env.VTE_VERSION === '0.50.0') { + return false; + } + + const version = parseVersion(env.VTE_VERSION); + return version.major > 0 || version.minor >= 50; + } + + return false; +} + +module.exports = { + supportsHyperlink, + stdout: supportsHyperlink(process.stdout), + stderr: supportsHyperlink(process.stderr) +}; + + +/***/ }), + +/***/ 7362: +/***/ ((module) => { + +"use strict"; + + +module.exports = (flag, argv = process.argv) => { + const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); + const position = argv.indexOf(prefix + flag); + const terminatorPosition = argv.indexOf('--'); + return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); +}; + + +/***/ }), + +/***/ 1507: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +const os = __nccwpck_require__(2087); +const tty = __nccwpck_require__(3867); +const hasFlag = __nccwpck_require__(7362); + +const {env} = process; + +let forceColor; +if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false') || + hasFlag('color=never')) { + forceColor = 0; +} else if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + forceColor = 1; +} + +if ('FORCE_COLOR' in env) { + if (env.FORCE_COLOR === 'true') { + forceColor = 1; + } else if (env.FORCE_COLOR === 'false') { + forceColor = 0; + } else { + forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); + } +} + +function translateLevel(level) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +} + +function supportsColor(haveStream, streamIsTTY) { + if (forceColor === 0) { + return 0; + } + + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } + + if (hasFlag('color=256')) { + return 2; + } + + if (haveStream && !streamIsTTY && forceColor === undefined) { + return 0; + } + + const min = forceColor || 0; + + if (env.TERM === 'dumb') { + return min; + } + + if (process.platform === 'win32') { + // Windows 10 build 10586 is the first Windows release that supports 256 colors. + // Windows 10 build 14931 is the first release that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(osRelease[0]) >= 10 && + Number(osRelease[2]) >= 10586 + ) { + return Number(osRelease[2]) >= 14931 ? 3 : 2; + } + + return 1; + } + + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } + + return min; + } + + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } + + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default + } + } + + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } + + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } + + if ('COLORTERM' in env) { + return 1; + } + + return min; +} + +function getSupportLevel(stream) { + const level = supportsColor(stream, stream && stream.isTTY); + return translateLevel(level); +} + +module.exports = { + supportsColor: getSupportLevel, + stdout: translateLevel(supportsColor(true, tty.isatty(1))), + stderr: translateLevel(supportsColor(true, tty.isatty(2))) +}; + + +/***/ }), + +/***/ 1898: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +const ansiEscapes = __nccwpck_require__(8512); +const supportsHyperlinks = __nccwpck_require__(8824); + +const terminalLink = (text, url, {target = 'stdout', ...options} = {}) => { + if (!supportsHyperlinks[target]) { + // If the fallback has been explicitly disabled, don't modify the text itself. + if (options.fallback === false) { + return text; + } + + return typeof options.fallback === 'function' ? options.fallback(text, url) : `${text} (\u200B${url}\u200B)`; + } + + return ansiEscapes.link(text, url); +}; + +module.exports = (text, url, options = {}) => terminalLink(text, url, options); + +module.exports.stderr = (text, url, options = {}) => terminalLink(text, url, {target: 'stderr', ...options}); + +module.exports.isSupported = supportsHyperlinks.stdout; +module.exports.stderr.isSupported = supportsHyperlinks.stderr; + + /***/ }), /***/ 4294: @@ -7568,6 +10167,14 @@ module.exports = require("tls");; /***/ }), +/***/ 3867: +/***/ ((module) => { + +"use strict"; +module.exports = require("tty");; + +/***/ }), + /***/ 8835: /***/ ((module) => { @@ -7605,8 +10212,8 @@ module.exports = require("zlib");; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed +/******/ id: moduleId, +/******/ loaded: false, /******/ exports: {} /******/ }; /******/ @@ -7619,11 +10226,23 @@ module.exports = require("zlib");; /******/ if(threw) delete __webpack_module_cache__[moduleId]; /******/ } /******/ +/******/ // Flag the module as loaded +/******/ module.loaded = true; +/******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ +/******/ /* webpack/runtime/node module decorator */ +/******/ (() => { +/******/ __nccwpck_require__.nmd = (module) => { +/******/ module.paths = []; +/******/ if (!module.children) module.children = []; +/******/ return module; +/******/ }; +/******/ })(); +/******/ /******/ /* webpack/runtime/compat */ /******/ /******/ __nccwpck_require__.ab = __dirname + "/";/************************************************************************/ diff --git a/package-lock.json b/package-lock.json index f55af56f..c3c90380 100644 --- a/package-lock.json +++ b/package-lock.json @@ -206,6 +206,19 @@ "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } } }, "@babel/parser": { @@ -2643,14 +2656,54 @@ "dev": true }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "char-regex": { diff --git a/package.json b/package.json index 445b7615..9220e873 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "pack": "ncc build", "test": "jest", "test:only-errors": "jest --reporters jest-silent-reporter --silent", - "test:watch": "jest --watch --notify --expandf", + "test:watch": "jest --watch --notify --expand", "all": "npm run build && npm run format && npm run lint && npm run pack && npm test" }, "repository": { @@ -44,6 +44,7 @@ "@typescript-eslint/eslint-plugin": "^4.16.1", "@typescript-eslint/parser": "^4.16.1", "@vercel/ncc": "^0.27.0", + "chalk": "^4.1.0", "eslint": "^7.21.0", "eslint-plugin-github": "^4.1.2", "eslint-plugin-jest": "^24.1.5", @@ -52,6 +53,7 @@ "jest-silent-reporter": "^0.4.0", "js-yaml": "^4.0.0", "prettier": "^2.2.1", + "terminal-link": "^2.1.1", "ts-jest": "^26.5.3", "typescript": "^4.2.3" } diff --git a/src/classes/assignees.ts b/src/classes/assignees.ts index af844dcd..5c0c8b2d 100644 --- a/src/classes/assignees.ts +++ b/src/classes/assignees.ts @@ -42,7 +42,7 @@ export class Assignees { if (exemptAssignees.length === 0) { this._issueLogger.info( - `No option was specified to skip the stale process for this $$type` + `No assignee option was specified to skip the stale process for this $$type` ); this._logSkip(); @@ -52,7 +52,7 @@ export class Assignees { this._issueLogger.info( `Found ${exemptAssignees.length} assignee${ exemptAssignees.length > 1 ? 's' : '' - } on this $$type` + } that can exempt stale on this $$type` ); const hasExemptAssignee: boolean = exemptAssignees.some( diff --git a/src/classes/issues-processor.ts b/src/classes/issues-processor.ts index 70a704a1..311b1827 100644 --- a/src/classes/issues-processor.ts +++ b/src/classes/issues-processor.ts @@ -1,11 +1,13 @@ +import * as core from '@actions/core'; import {context, getOctokit} from '@actions/github'; import {GitHub} from '@actions/github/lib/utils'; import {GetResponseTypeFromEndpointMethod} from '@octokit/types'; +import chalk from 'chalk'; +import {Option} from '../enums/option'; import {getHumanizedDate} from '../functions/dates/get-humanized-date'; import {isDateMoreRecentThan} from '../functions/dates/is-date-more-recent-than'; import {isValidDate} from '../functions/dates/is-valid-date'; import {isLabeled} from '../functions/is-labeled'; -import {isPullRequest} from '../functions/is-pull-request'; import {shouldMarkWhenStale} from '../functions/should-mark-when-stale'; import {wordsToList} from '../functions/words-to-list'; import {IComment} from '../interfaces/comment'; @@ -18,6 +20,7 @@ import {Issue} from './issue'; import {IssueLogger} from './loggers/issue-logger'; import {Logger} from './loggers/logger'; import {Milestones} from './milestones'; +import {Operations} from './operations'; import {Statistics} from './statistics'; /*** @@ -33,8 +36,8 @@ export class IssuesProcessor { } private readonly _logger: Logger = new Logger(); + private readonly _operations: Operations; private readonly _statistics: Statistics | undefined; - private _operationsLeft = 0; readonly client: InstanceType; readonly options: IIssuesProcessorOptions; readonly staleIssues: Issue[] = []; @@ -44,31 +47,49 @@ export class IssuesProcessor { constructor(options: IIssuesProcessorOptions) { this.options = options; - this._operationsLeft = this.options.operationsPerRun; this.client = getOctokit(this.options.repoToken); + this._operations = new Operations(this.options); + + this._logger.info(chalk.yellow('Starting the stale action process...')); if (this.options.debugOnly) { + this._logger.warning(chalk.yellowBright('Executing in debug mode!')); this._logger.warning( - 'Executing in debug mode. Debug output will be written but no issues will be processed.' + chalk.yellowBright( + 'The debug output will be written but no issues/PRs will be processed.' + ) ); } if (this.options.enableStatistics) { - this._statistics = new Statistics(this.options); + this._statistics = new Statistics(); } } - async processIssues(page = 1): Promise { + async processIssues(page: Readonly = 1): Promise { // get the next batch of issues const issues: Issue[] = await this.getIssues(page); const actor: string = await this.getActor(); if (issues.length <= 0) { - this._logger.info('---'); - this._statistics?.setOperationsLeft(this._operationsLeft).logStats(); - this._logger.info('No more issues found to process. Exiting.'); + this._logger.info( + chalk.green('No more issues found to process. Exiting...') + ); + this._statistics + ?.setOperationsLeft(this._operations.getUnconsumedOperationsCount()) + .logStats(); - return this._operationsLeft; + return this._operations.getOperationsLeftCount(); + } else { + this._logger.info( + chalk.yellow( + `Processing the batch of issues ${chalk.cyan( + `#${page}` + )} containing ${chalk.cyan(issues.length)} issue${ + issues.length > 1 ? 's' : '' + }...` + ) + ); } for (const issue of issues.values()) { @@ -160,8 +181,10 @@ export class IssuesProcessor { // Expecting that GitHub will always set a creation date on the issues and PRs // But you never know! if (!isValidDate(createdAt)) { - throw new Error( - `Invalid issue field: "created_at". Expected a valid date` + core.setFailed( + new Error( + `Invalid issue field: "created_at". Expected a valid date` + ) ); } @@ -207,6 +230,7 @@ export class IssuesProcessor { } const anyOfLabels: string[] = wordsToList(this.options.anyOfLabels); + if ( anyOfLabels.length && !anyOfLabels.some((label: Readonly): boolean => @@ -222,9 +246,6 @@ export class IssuesProcessor { const milestones: Milestones = new Milestones(this.options, issue); if (milestones.shouldExemptMilestones()) { - issueLogger.info( - `Skipping $$type because it has an exempted milestone` - ); continue; // don't process exempt milestones } @@ -266,13 +287,27 @@ export class IssuesProcessor { } } - if (this._operationsLeft <= 0) { + if (this._operations.hasOperationsLeft()) { this._logger.warning( - 'Reached max number of operations to process. Exiting.' + chalk.yellowBright('No more operations left! Exiting...') ); + this._logger.warning( + chalk.yellowBright( + `If you think that not enough issues were processed you could try to increase the quantity related to the ${this._logger.createOptionLink( + Option.OperationsPerRun + )} option which is currently set to ${chalk.cyan( + this.options.operationsPerRun + )}` + ) + ); + return 0; } + this._logger.info( + chalk.green(`Batch ${chalk.cyan(`#${page}`)} processed.`) + ); + // do the next batch return this.processIssues(page + 1); } @@ -284,7 +319,7 @@ export class IssuesProcessor { ): Promise { // find any comments since date on the given issue try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementFetchedIssuesCommentsCount(); const comments = await this.client.issues.listComments({ owner: context.repo.owner, @@ -304,7 +339,7 @@ export class IssuesProcessor { let actor; try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); actor = await this.client.users.getAuthenticated(); } catch (error) { return context.actor; @@ -320,8 +355,7 @@ export class IssuesProcessor { type OctoKitIssueList = GetResponseTypeFromEndpointMethod; try { - this._operationsLeft -= 1; - this._statistics?.incrementFetchedIssuesCount(); + this._operations.consumeOperation(); const issueResult: OctoKitIssueList = await this.client.issues.listForRepo( { owner: context.repo.owner, @@ -332,6 +366,7 @@ export class IssuesProcessor { page } ); + this._statistics?.incrementFetchedIssuesCount(issueResult.data.length); return issueResult.data.map( (issue: Readonly): Issue => new Issue(this.options, issue) @@ -352,7 +387,7 @@ export class IssuesProcessor { issueLogger.info(`Checking for label on $$type`); - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementFetchedIssuesEventsCount(); const options = this.client.issues.listEvents.endpoint.merge({ owner: context.repo.owner, @@ -396,8 +431,7 @@ export class IssuesProcessor { ); issueLogger.info(`$$type has been commented on: ${issueHasComments}`); - const isPr: boolean = isPullRequest(issue); - const daysBeforeClose: number = isPr + const daysBeforeClose: number = issue.isPullRequest ? this._getDaysBeforePrClose() : this._getDaysBeforeIssueClose(); @@ -491,7 +525,7 @@ export class IssuesProcessor { if (!skipMessage) { try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementAddedComment(); await this.client.issues.createComment({ owner: context.repo.owner, @@ -505,7 +539,7 @@ export class IssuesProcessor { } try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementAddedLabel(); this._statistics?.incrementStaleIssuesCount(); await this.client.issues.addLabels({ @@ -536,7 +570,7 @@ export class IssuesProcessor { if (closeMessage) { try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementAddedComment(); await this.client.issues.createComment({ owner: context.repo.owner, @@ -551,7 +585,7 @@ export class IssuesProcessor { if (closeLabel) { try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementAddedLabel(); await this.client.issues.addLabels({ owner: context.repo.owner, @@ -565,7 +599,7 @@ export class IssuesProcessor { } try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementClosedIssuesCount(); await this.client.issues.update({ owner: context.repo.owner, @@ -588,7 +622,7 @@ export class IssuesProcessor { } try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementFetchedPullRequestsCount(); const pullRequest = await this.client.pulls.get({ owner: context.repo.owner, @@ -625,7 +659,7 @@ export class IssuesProcessor { issueLogger.info(`Deleting branch ${branch} from closed $$type`); try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementDeletedBranchesCount(); await this.client.git.deleteRef({ owner: context.repo.owner, @@ -646,13 +680,12 @@ export class IssuesProcessor { issueLogger.info(`Removing label "${label}" from $$type`); this.removedLabelIssues.push(issue); - // @todo remove the debug only to be able to test the code below if (this.options.debugOnly) { return; } try { - this._operationsLeft -= 1; + this._operations.consumeOperation(); this._statistics?.incrementDeletedLabelsCount(); await this.client.issues.removeLabel({ owner: context.repo.owner, diff --git a/src/classes/loggers/issue-logger.ts b/src/classes/loggers/issue-logger.ts index ef4cd1a9..5db11301 100644 --- a/src/classes/loggers/issue-logger.ts +++ b/src/classes/loggers/issue-logger.ts @@ -1,4 +1,4 @@ -import * as core from '@actions/core'; +import chalk from 'chalk'; import {Issue} from '../issue'; import {Logger} from './logger'; @@ -15,23 +15,24 @@ import {Logger} from './logger'; * @example * warning('The $$type will stale') => "The pull request will stale" */ -export class IssueLogger implements Logger { +export class IssueLogger extends Logger { private readonly _issue: Issue; constructor(issue: Issue) { + super(); this._issue = issue; } - warning(message: Readonly): void { - core.warning(this._format(message)); + warning(...message: string[]): void { + super.warning(this._format(...message)); } - info(message: Readonly): void { - core.info(this._format(message)); + info(...message: string[]): void { + super.info(this._format(...message)); } - error(message: Readonly): void { - core.error(this._format(message)); + error(...message: string[]): void { + super.error(this._format(...message)); } private _replaceTokens(message: Readonly): string { @@ -51,14 +52,28 @@ export class IssueLogger implements Logger { } private _prefixWithIssueNumber(message: Readonly): string { - return `[#${this._getIssueNumber()}] ${message}`; + return `${this._getPrefix()} ${message}`; } private _getIssueNumber(): number { return this._issue.number; } - private _format(message: Readonly): string { - return this._prefixWithIssueNumber(this._replaceTokens(message)); + private _format(...message: string[]): string { + return this._prefixWithIssueNumber(this._replaceTokens(message.join(' '))); + } + + private _getPrefix(): string { + return this._issue.isPullRequest + ? this._getPullRequestPrefix() + : this._getIssuePrefix(); + } + + private _getIssuePrefix(): string { + return chalk.red(`[#${this._getIssueNumber()}]`); + } + + private _getPullRequestPrefix(): string { + return chalk.blue(`[#${this._getIssueNumber()}]`); } } diff --git a/src/classes/loggers/logger.ts b/src/classes/loggers/logger.ts index 8dce898e..65bb83af 100644 --- a/src/classes/loggers/logger.ts +++ b/src/classes/loggers/logger.ts @@ -1,15 +1,28 @@ import * as core from '@actions/core'; +import chalk from 'chalk'; +import terminalLink from 'terminal-link'; +import {Option} from '../../enums/option'; export class Logger { - warning(message: Readonly): void { - core.warning(message); + warning(...message: string[]): void { + core.warning(chalk.whiteBright(...message)); } - info(message: Readonly): void { - core.info(message); + info(...message: string[]): void { + core.info(chalk.whiteBright(...message)); } - error(message: Readonly): void { - core.error(message); + error(...message: string[]): void { + core.error(chalk.whiteBright(...message)); + } + + createLink(name: Readonly, link: Readonly): string { + return terminalLink(name, link); + } + + createOptionLink(option: Readonly