diff --git a/README.md b/README.md index 5281baab..d8eb9722 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,31 @@ rules: ... ``` +Some axioms (ex. [`contributor-count`](./docs/md/axioms.md#contributor-count)) output numerical values instead of strings. For these axioms, numerical comparisons (`<`, `>`, `<=`, `>=`) can be also be specified in the `where` conditional. Note that if a numerical comparison is used for a non-numerical axiom, the comparison will always fail. +```JavaScript +{ + "axioms": { + "contributor-count": "contributors" + }, + "rules": { + "my-rule": { + "where": ["contributors>6", "contributors<200"], + // ... + } + } +} +``` +```YAML +axioms: + contributor-count: contributors +rules: + my-rule: + where: + - contributors>6 + - contributors<200 + rule: + ... +``` ## API Repolinter also includes an extensible JavaScript API: diff --git a/axioms/axioms.js b/axioms/axioms.js index 59973c0c..939a9098 100644 --- a/axioms/axioms.js +++ b/axioms/axioms.js @@ -4,5 +4,6 @@ module.exports = [ 'licensee', 'linguist', - 'packagers' + 'packagers', + 'contributor-count' ] diff --git a/axioms/contributor-count.js b/axioms/contributor-count.js new file mode 100644 index 00000000..ba003e6d --- /dev/null +++ b/axioms/contributor-count.js @@ -0,0 +1,21 @@ +// Copyright 2017 TODO Group. All rights reserved. +// Licensed under the Apache License, Version 2.0. + +const { gitlogPromise } = require('gitlog') +const Result = require('../lib/result') + +module.exports = async function (fileSystem) { + const commits = await gitlogPromise({ + repo: fileSystem.targetDir, + all: true, + number: 10000 // Fetch the last 10000 commits + }) + if (!commits) { + return new Result('GitLog axiom failed to run, is this project a git repository?', [], false) + } + // Get commit authors and filter unique values + const contributors = commits + .map((commit) => commit.authorName.toLowerCase()) + .filter((value, index, self) => self.indexOf(value) === index) + return new Result('', [{ path: contributors.length.toString(), passed: true }], true) +} diff --git a/bin/repolinter.js b/bin/repolinter.js index 6beb9415..13f56f03 100755 --- a/bin/repolinter.js +++ b/bin/repolinter.js @@ -13,7 +13,7 @@ const yaml = require('js-yaml') // eslint-disable-next-line no-unused-expressions require('yargs') - .command(['lint ', '*'], 'run repolinter on the specified directory, outputting results to STDOUT.', yargs => { + .command(['lint [directory]', '*'], 'run repolinter on the specified directory, outputting results to STDOUT.', yargs => { yargs .positional('directory', { describe: 'The target directory to lint', diff --git a/docs/md/axioms.md b/docs/md/axioms.md index ce8894ae..b1e91645 100644 --- a/docs/md/axioms.md +++ b/docs/md/axioms.md @@ -6,19 +6,40 @@ Below is a complete list of axioms that Repolinter can check. - [Contents](#contents) - [Reference](#reference) + - [`contributor-count`](#contributor-count) - [`licensee`](#licensee) - [`linguist`](#linguist) - [`packagers`](#packagers) ## Reference +### `contributor-count` + +This axiom uses [gitlog](https://github.com/domharrington/node-gitlog#readme) to count the number of contributors to the current git repository. Contributors are counted based on unique occurrences of an author name in the git log. This axiom is a numerical axiom, meaning numerical comparisons can be used. + +An example of using this axiom: + +```JavaScript +{ + "axioms": { + "contributor-count": "contributors" + }, + "rules": { + "my-rule": { + "where": ["contributors>6", "contributors<200"], + // ... + } + } +} +``` + ### `licensee` This axiom uses [licensee](https://github.com/licensee/licensee) to detect the license used in the current repository. To use this axiom licensee must be installed in your `PATH` or in the same directory as Repolinter. This axiom will return a list of [license identifiers](https://spdx.org/licenses/) associated with the current repository. An example of using this axiom: -```JSON +```JavaScript { "axioms": { "licensee": "license" @@ -26,7 +47,7 @@ An example of using this axiom: "rules": { "my-rule": { "where": ["license=Apache-2.0"], - ... + // ... } } } @@ -37,7 +58,7 @@ An example of using this axiom: This axiom uses GitHub's [linguist](https://github.com/github/linguist) to detect programming languages in the current repository. To use this axiom, linguist must be installed in the system `PATH` or in the same directory as Repolinter. This axiom will return a lowercase list of programming languages from [this list of supported languages](https://github.com/github/linguist/blob/master/lib/linguist/languages.yml). An example of using this axiom: -```JSON +```JavaScript { "axioms": { "linguist":" language" @@ -45,7 +66,7 @@ An example of using this axiom: "rules": { "my-rule": { "where": ["language=javascript"], - ... + // ... } } } diff --git a/index.d.ts b/index.d.ts index 59389f39..ad0175b8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -63,6 +63,7 @@ export declare function runRuleset(ruleset: RuleInfo[], targets: boolean|{ [key: export declare function determineTargets(axiomconfig: any, fs: FileSystem): Promise<{ [key: string]: Result }> export declare function validateConfig(config: any): Promise<{ passed: boolean, error?: string }> export declare function parseConfig(config: any): RuleInfo[] +export declare function shouldRuleRun(validTargets: string[], ruleAxioms: string[]): string[] export declare const defaultFormatter: Formatter export declare const jsonFormatter: Formatter diff --git a/index.js b/index.js index caab32c9..c10a147e 100644 --- a/index.js +++ b/index.js @@ -269,6 +269,65 @@ async function loadAxioms () { .reduce((p, [name, require]) => { p[name] = require; return p }, {}) } +/** + * Checks a rule's list of axioms against a list of valid + * targets, and determines if the rule should run or not + * based on the following rules criteria: + * * The rule's list has a direct match on a target OR + * * The rule specifies a numerical axiom (ex. >) and the target + * list contains a target that matches that axiom. + * + * Supported numerical axioms are >, <, >=, <=, and = Only + * + * @memberof repolinter + * @param {string[]} validTargets The axiom target list in "target=thing" format, including the wildcard entry ("target=*"). + * For numerical targets it is assumed that only one entry and the wildcard are present (e.g. ["target=2", "target=3", "target=*"] is invalid) + * @param {string[]} ruleAxioms The rule "where" specification to validate against. + * @returns {string[]} The list pf unsatisfied axioms, if any. Empty array indicates the rule should run. + */ +function shouldRuleRun (validTargets, ruleAxioms) { + // parse out numerical axioms, splitting them by name, operand, and number + const ruleRegex = /([\w-]+)((?:>|<)=?)(\d+)/i + const numericalRuleAxioms = [] + const regularRuleAxioms = [] + for (const ruleax of ruleAxioms) { + const match = ruleRegex.exec(ruleax) + if (match !== null && match[1] && match[2] && !isNaN(parseInt(match[3]))) { + // parse the numerical version + numericalRuleAxioms.push({ axiom: ruleax, name: match[1], operand: match[2], number: parseInt(match[3]) }) + } else { + // parse the non-numerical version + regularRuleAxioms.push(ruleax) + } + } + // test that every non-number axiom matches a target + // start a list of condidions that don't pass + const table = new Set(validTargets) + const failedRuleAxioms = regularRuleAxioms.filter(r => !table.has(r)) + // check the numbered axioms + // convert the targets into { targetName: number } for all numerical ones + const numericalTargets = validTargets + .map(r => r.split('=')) + .map(([name, maybeNumber]) => [name, parseInt(maybeNumber)]) + .filter(([name, maybeNumber]) => !isNaN(maybeNumber)) + /** @ts-ignore */ + const numericalTargetsMap = new Map(numericalTargets) + // test each numerical Rule against it's numerical axiom, return the axioms that failed + return numericalRuleAxioms + .filter(({ axiom, name, operand, number }) => { + // get the number to test against + const target = numericalTargetsMap.get(name) + if (target === undefined) return true + // test the number based on the operand + return !((operand === '<' && target < number) || + (operand === '<=' && target <= number) || + (operand === '>' && target > number) || + (operand === '>=' && target >= number)) + }) + .map(({ axiom }) => axiom) + .concat(failedRuleAxioms) +} + /** * Run all operations in a ruleset, including linting and fixing. Returns * a list of objects with the output of the linter rules @@ -282,11 +341,15 @@ async function loadAxioms () { */ async function runRuleset (ruleset, targets, fileSystem, dryRun) { // generate a flat array of axiom string identifiers + /** @ignore @type {string[]} */ let targetArray = [] if (typeof targets !== 'boolean') { targetArray = Object.entries(targets) + // restricted to only passed axioms .filter(([axiomId, res]) => res.passed) + // pair the axiom ID with the axiom target array .map(([axiomId, res]) => [axiomId, res.targets.map(t => t.path)]) + // join the target arrays together into one array of all the targets .map(([axiomId, paths]) => [`${axiomId}=*`].concat(paths.map(p => `${axiomId}=${p}`))) .reduce((a, c) => a.concat(c), []) } @@ -300,8 +363,8 @@ async function runRuleset (ruleset, targets, fileSystem, dryRun) { // check axioms and enable appropriately if (r.level === 'off') { return FormatResult.CreateIgnored(r, 'ignored because level is "off"') } // filter to only targets with no matches - if (typeof targets !== 'boolean') { - const ignoreReasons = r.where.filter(check => !targetArray.find(tar => check === tar)) + if (typeof targets !== 'boolean' && r.where && r.where.length) { + const ignoreReasons = shouldRuleRun(targetArray, r.where) if (ignoreReasons.length > 0) { return FormatResult.CreateIgnored(r, `ignored due to unsatisfied condition(s): "${ignoreReasons.join('", "')}"`) } } // check if the rule file exists @@ -444,6 +507,7 @@ module.exports.runRuleset = runRuleset module.exports.determineTargets = determineTargets module.exports.validateConfig = validateConfig module.exports.parseConfig = parseConfig +module.exports.shouldRuleRun = shouldRuleRun module.exports.lint = lint module.exports.Result = Result module.exports.RuleInfo = RuleInfo diff --git a/package-lock.json b/package-lock.json index 62fda93d..cac9892c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,24 +33,24 @@ } }, "@babel/core": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.4.tgz", - "integrity": "sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", + "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.4", - "@babel/helper-module-transforms": "^7.11.0", - "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.0", + "@babel/parser": "^7.9.0", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", "json5": "^2.1.2", - "lodash": "^4.17.19", + "lodash": "^4.17.13", "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" @@ -248,6 +248,21 @@ "dev": true, "requires": { "@babel/types": "^7.10.4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + } } }, "@babel/helper-module-transforms": { @@ -263,14 +278,6 @@ "@babel/template": "^7.10.4", "@babel/types": "^7.11.0", "lodash": "^4.17.19" - }, - "dependencies": { - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true - } } }, "@babel/helper-optimise-call-expression": { @@ -280,6 +287,13 @@ "dev": true, "requires": { "@babel/types": "^7.10.4" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + } } }, "@babel/helper-plugin-utils": { @@ -511,6 +525,13 @@ "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" + } } }, "@babel/plugin-proposal-function-bind": { @@ -734,6 +755,30 @@ "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "@babel/plugin-syntax-import-meta": { @@ -797,6 +842,16 @@ "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } } }, "@babel/plugin-syntax-optional-catch-binding": { @@ -871,6 +926,13 @@ "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + } } }, "@babel/plugin-transform-block-scoping": { @@ -989,6 +1051,50 @@ "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "requires": { + "type-fest": "^0.8.1" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + } } }, "@babel/plugin-transform-member-expression-literals": { @@ -1404,26 +1510,11 @@ "lodash": "^4.17.19" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, @@ -1443,6 +1534,19 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" } } }, @@ -3187,6 +3291,18 @@ "table": "^5.2.3", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + } } }, "eslint-config-standard": { @@ -3547,8 +3663,7 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "expand-brackets": { "version": "2.1.4", @@ -4077,6 +4192,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", + "dev": true, "requires": { "emoji-regex": ">=6.0.0 <=6.1.1" }, @@ -4084,7 +4200,27 @@ "emoji-regex": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=" + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "dev": true + } + } + }, + "gitlog": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gitlog/-/gitlog-4.0.0.tgz", + "integrity": "sha512-N6ZcvvbHsqhmM09wtzbL8v3FPwBK3EQ1xnv/2nj9JGH/YsYVn+ZkmMCxzkEjHnSFcpUk2HN2LBp76PGj3TkPag==", + "requires": { + "debug": "^4.1.1", + "tslib": "^1.11.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } } } }, @@ -4684,8 +4820,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-glob": { "version": "4.0.1", @@ -4855,8 +4990,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "2.1.0", @@ -5116,13 +5250,30 @@ } }, "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "dependencies": { + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } } }, "linkify-it": { @@ -5187,7 +5338,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -5654,18 +5804,18 @@ } }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", + "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", "dev": true, "requires": { "minimist": "^1.2.5" } }, "mocha": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.2.tgz", - "integrity": "sha512-I8FRAcuACNMLQn3lS4qeWLxXqLvGf6r2CaLstDpZmMUUSmvW6Cnm1AuHxgbc7ctZVRcfwspCRbDHymPsi3dkJw==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz", + "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==", "dev": true, "requires": { "ansi-colors": "4.1.1", @@ -6072,8 +6222,7 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "nock": { "version": "13.0.4", @@ -6359,6 +6508,18 @@ "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.3" + }, + "dependencies": { + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + } } }, "ordered-read-streams": { @@ -6393,7 +6554,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -6510,8 +6670,7 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-absolute": { "version": "1.0.1", @@ -6584,6 +6743,21 @@ "dev": true, "requires": { "pinkie": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "pkg-conf": { @@ -6691,6 +6865,16 @@ "dev": true, "requires": { "find-up": "^4.0.0" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + } } }, "posix-character-classes": { @@ -6784,6 +6968,13 @@ "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" + }, + "dependencies": { + "estraverse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz", + "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==" + } } }, "pumpify": { @@ -7269,6 +7460,90 @@ "dev": true, "requires": { "path-parse": "^1.0.6" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "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==", + "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==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + } } }, "resolve-from": { @@ -7610,6 +7885,13 @@ "dev": true, "requires": { "is-plain-obj": "^1.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + } } }, "source-map": { @@ -7665,6 +7947,13 @@ "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } } }, "spdx-exceptions": { @@ -7868,16 +8157,6 @@ "eslint-visitor-keys": "^1.1.0" } }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -8232,9 +8511,13 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } }, "emoji-regex": { "version": "7.0.3", @@ -8394,6 +8677,14 @@ "is-extendable": "^1.0.1" } }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", @@ -8402,6 +8693,16 @@ "requires": { "is-plain-object": "^2.0.4" } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==" } } }, @@ -8469,10 +8770,9 @@ "dev": true }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" }, "type-check": { "version": "0.4.0", @@ -8492,8 +8792,7 @@ "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" }, "type-is": { "version": "1.6.18", @@ -8571,6 +8870,21 @@ "requires": { "unicode-canonical-property-names-ecmascript": "^1.0.4", "unicode-property-aliases-ecmascript": "^1.0.4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } } }, "unicode-match-property-value-ecmascript": { @@ -8645,6 +8959,16 @@ "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", "through2-filter": "^3.0.0" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } } }, "unist-builder": { @@ -8664,9 +8988,13 @@ }, "unist-util-is": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", - "dev": true + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } }, "unist-util-position": { "version": "3.1.0", @@ -9189,27 +9517,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", diff --git a/package.json b/package.json index f7b5e764..12fddfb0 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "emoji-regex": "^9.0.0", "eslint-config-standard": "^14.1.1", "find-config": "^1.0.0", - "github-slugger": "^1.3.0", + "gitlog": "^4.0.0", "is-windows": "^1.0.2", "isbinaryfile": "^4.0.6", "js-yaml": "^3.14.0", @@ -70,7 +70,7 @@ "gh-pages": "^3.1.0", "markdown-toc": "^1.2.0", "markdownlint": "^0.20.4", - "mocha": "^8.1.2", + "mocha": "^8.1.3", "mock-http-server": "^1.4.2", "nyc": "^15.1.0", "standard": "^14.3.4", diff --git a/tests/api/run_ruleset_tests.js b/tests/api/run_ruleset_tests.js index b47db6ce..b18ce2af 100644 --- a/tests/api/run_ruleset_tests.js +++ b/tests/api/run_ruleset_tests.js @@ -94,6 +94,28 @@ describe('api', () => { expect(res[0].fixResult).to.equal(undefined) }) + it('runs a rule conditionally with a numerical axiom', async () => { + const mockconfig = [ + new RuleInfo('my-rule', 'error', ['number>3'], 'apache-notice', {}) + ] + const res = await repolinter.runRuleset(mockconfig, { number: new Result('', [{ passed: true, path: '4' }], true) }, realFs, false) + expect(res).to.have.length(1) + expect(res[0].status).to.equal(FormatResult.RULE_PASSED) + expect(res[0].lintResult.passed).to.equal(true) + expect(res[0].fixResult).to.equal(undefined) + }) + + it('ignores a rule conditionally with a numerical axiom', async () => { + const mockconfig = [ + new RuleInfo('my-rule', 'error', ['number>4'], 'apache-notice', {}) + ] + const res = await repolinter.runRuleset(mockconfig, { number: new Result('', [{ passed: true, path: '4' }], true) }, realFs, false) + expect(res).to.have.length(1) + expect(res[0].status).to.equal(FormatResult.IGNORED) + expect(res[0].lintResult).to.equal(undefined) + expect(res[0].fixResult).to.equal(undefined) + }) + it('ignores a fix if the rule passes', async () => { const mockconfig = [ new RuleInfo('my-rule', 'error', [], 'file-existence', { globsAny: ['README*'] }, 'garbage-fix', {}) diff --git a/tests/api/should_rule_run_tests.js b/tests/api/should_rule_run_tests.js new file mode 100644 index 00000000..2cc0f889 --- /dev/null +++ b/tests/api/should_rule_run_tests.js @@ -0,0 +1,119 @@ +// Copyright 2017 TODO Group. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +const chai = require('chai') +const { shouldRuleRun } = require('../..') +const expect = chai.expect + +describe('api', () => { + describe('validateConfig', () => { + it('should allow a rule to run if axioms match', () => { + const res = shouldRuleRun(['language=javascript', 'language=*'], ['language=javascript']) + expect(res).to.deep.equal([]) + }) + + it('should allow a rule to run if axioms wildcard match', () => { + const res = shouldRuleRun(['language=javascript', 'language=*'], ['language=*']) + expect(res).to.deep.equal([]) + }) + + it('should not allow a rule to run if no axioms match', () => { + const res = shouldRuleRun(['language=javascript', 'language=*'], ['language=cheese']) + expect(res).to.deep.equal(['language=cheese']) + }) + + it('should not allow non-numerical axioms with numerical comparisons', () => { + const res = shouldRuleRun(['language=javascript', 'language=*'], ['language>=3']) + expect(res).to.deep.equal(['language>=3']) + }) + + it('should not allow invalid operators', () => { + const res = shouldRuleRun(['language=3', 'language=*'], ['language=>3']) + expect(res).to.deep.equal(['language=>3']) + }) + + it('should handle a mix of axoims', () => { + const resPass = shouldRuleRun(['commits=hello', 'commits=*', 'contributor-count=blah', 'contributors=*'], ['commits=hello', 'contributor-count=blah']) + expect(resPass).to.deep.equal([]) + const resFail = shouldRuleRun(['commits=hello', 'commits=*', 'contributor-count=blah', 'contributors=*'], ['commits=nothello', 'contributor-count=blah']) + expect(resFail).to.deep.equal(['commits=nothello']) + }) + + it('should handle a numerical = axiom', () => { + const res = shouldRuleRun(['language=javascript', 'language=*', 'contributors=7', 'contributors=*'], ['contributors=7']) + expect(res).to.deep.equal([]) + }) + + it('should handle a numerical > axiom', () => { + const resPass = shouldRuleRun(['language=javascript', 'language=*', 'contributors=7', 'contributors=*'], ['contributors>6']) + expect(resPass).to.deep.equal([]) + const resFail = shouldRuleRun(['language=javascript', 'language=*', 'contributors=7', 'contributors=*'], ['contributors>7']) + expect(resFail).to.deep.equal(['contributors>7']) + }) + + it('should handle a numerical >= axiom', () => { + const resPass = shouldRuleRun(['language=javascript', 'language=*', 'contributors=7', 'contributors=*'], ['contributors>=7']) + expect(resPass).to.deep.equal([]) + const resFail = shouldRuleRun(['language=javascript', 'language=*', 'contributors=7', 'contributors=*'], ['contributors>=8']) + expect(resFail).to.deep.equal(['contributors>=8']) + }) + + it('should handle a numerical < axiom', () => { + const resPass = shouldRuleRun(['language=javascript', 'language=*', 'contributors=7', 'contributors=*'], ['contributors<8']) + expect(resPass).to.deep.equal([]) + const resFail = shouldRuleRun(['language=javascript', 'language=*', 'contributors=7', 'contributors=*'], ['contributors<7']) + expect(resFail).to.deep.equal(['contributors<7']) + }) + + it('should handle a numerical <= axiom', () => { + const resPass = shouldRuleRun(['language=javascript', 'language=*', 'contributors=7', 'contributors=*'], ['contributors<=7']) + expect(resPass).to.deep.equal([]) + const resFail = shouldRuleRun(['language=javascript', 'language=*', 'contributors=7', 'contributors=*'], ['contributors<=6']) + expect(resFail).to.deep.equal(['contributors<=6']) + }) + + it('should handle a mix of numerical axoims', () => { + const resPass = shouldRuleRun(['commits=700', 'commits=*', 'contributors=7', 'contributors=*'], ['contributors<=7', 'contributors>4', 'commits>500']) + expect(resPass).to.deep.equal([]) + const resFail = shouldRuleRun(['commits=700', 'commits=*', 'contributors=7', 'contributors=*'], ['contributors<=6', 'contributors>4', 'commits>900']) + expect(resFail).to.deep.equal(['contributors<=6', 'commits>900']) + }) + + it('should handle both numerical and regular axioms', () => { + const resPass = shouldRuleRun([ + 'commits=700', + 'commits=*', + 'contributors=7', + 'contributors=*', + 'language=javascript', + 'language=*', + 'git=yes', + 'git=*' + ], + [ + 'contributors<=7', + 'contributors>4', + 'commits>600', + 'git=*' + ]) + expect(resPass).to.deep.equal([]) + const resFail = shouldRuleRun([ + 'commits=700', + 'commits=*', + 'contributors=7', + 'contributors=*', + 'language=javascript', + 'language=*', + 'git=yes', + 'git=*' + ], + [ + 'contributors<=7', + 'contributors>4', + 'commits>900', + 'git=no' + ]) + expect(resFail).to.deep.equal(['commits>900', 'git=no']) + }) + }) +}) diff --git a/tests/axioms/contributor_count_test.js b/tests/axioms/contributor_count_test.js new file mode 100644 index 00000000..0b5bcd49 --- /dev/null +++ b/tests/axioms/contributor_count_test.js @@ -0,0 +1,15 @@ +const chai = require('chai') +const expect = chai.expect +const path = require('path') +const FileSystem = require('../../lib/file_system') +const contributors = require('../../axioms/contributor-count') + +describe('contributors axiom', () => { + it('repolinter contributor count greater than zero', async () => { + const fs = new FileSystem(path.resolve('.')) + const contributorCount = await contributors(fs) + expect(contributorCount.passed).to.equal(true) + expect(contributorCount.targets).to.have.length(1) + expect(parseInt(contributorCount.targets[0].path)).to.be.greaterThan(0) + }) +})