diff --git a/.eslintrc.json b/.eslintrc.json index b4aa1e90..c6fcbc4b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -23,7 +23,7 @@ { "selector": "interface", "format": ["PascalCase"], - "custom": { "regex": "^I", "match": false } }, + "custom": { "regex": "^I[A-Z0-9]", "match": false } }, { "selector": "class", "format": ["PascalCase"] diff --git a/README.md b/README.md index 19a99ecd..31d1e8ca 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,9 @@ A monorepo for formerly disparate DefinitelyTyped-related tools: - [definitions-parser](packages/definitions-parser): the part of [microsoft/types-publisher](https://github.com/microsoft/types-publisher) that reads DefinitelyTyped repository data +- [dtslint](packages/dtslint): [microsoft/dtslint](https://github.com/microsoft/dtslint) - [dtslint-runner](packages/dtslint-runner): [DefinitelyTyped/dtslint-runner](https://github.com/DefinitelyTyped/dtslint-runner) +- [dts-critic](packages/dts-critic): [DefinitelyTyped/dts-critic](https://github.com/DefinitelyTyped/dts-critic) - [header-parser](packages/header-parser): [microsoft/definitelytyped-header-parser](https://github.com/microsoft/definitelytyped-header-parser) - [perf](packages/perf): [andrewbranch/definitely-not-slow](https://github.com/andrewbranch/definitely-not-slow) - [publisher](packages/publisher): the rest of [microsoft/types-publisher](https://github.com/microsoft/types-publisher) diff --git a/packages/dts-critic/.gitignore b/packages/dts-critic/.gitignore new file mode 100644 index 00000000..a198115b --- /dev/null +++ b/packages/dts-critic/.gitignore @@ -0,0 +1,69 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +.vscode + +# TypeScript compiler output +dist/ + +# Files downloaded during development +sources/ diff --git a/packages/dts-critic/LICENSE b/packages/dts-critic/LICENSE new file mode 100644 index 00000000..432d0c15 --- /dev/null +++ b/packages/dts-critic/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Nathan Shively-Sanders + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/dts-critic/README.md b/packages/dts-critic/README.md new file mode 100644 index 00000000..bedc2d94 --- /dev/null +++ b/packages/dts-critic/README.md @@ -0,0 +1,88 @@ +# dts-critic + +Checks a new dts against the Javascript sources and tells you what +problems it has. + +# Usage + +Build the program: +```sh +$ npm run build +``` + +Run the program using node: +```sh +$ node dist/index.js --dts=path-to-d.ts [--js=path-to-source] [--mode=mode] [--debug] +``` + +If the d.ts path is to a file named `index.d.ts`, the name of the directory +will be used as the package name instead. For example +`~/dt/types/jquery/index.d.ts` will use `jquery` as the name. + +`path-to-source` is optional; if you leave it off, the code will +check npm for a package with the same name as the d.ts. + +## Mode + +You can run dts-critic in different modes that affect which checks will be performed: +1. `name-only`: dts-critic will check your package name and [DefinitelyTyped header] +(https://github.com/Microsoft/definitelytyped-header-parser) (if present) against npm packages. +For example, if your declaration is for an npm package called 'cool-js-package', it will check if a +package named 'cool-js-package' actually exists in npm. + +2. `code`: in addition to the checks performed in `name-only` mode, dts-critic will check if your +declaration exports match the source JavaScript module exports. +For example, if your declaration has a default export, it will check if the JavaScript module also +has a default export. + +# Current checks + +## Npm declaration +If your declaration is for an npm package: + +1. An npm package with the same name of your declaration's package must exist. +2. If your declaration has a [Definitely Typed header](https://github.com/Microsoft/definitelytyped-header-parser) +and the header specifies a target version, the npm package must have +a matching version. +3. If you are running under `code` mode, your declaration must also match the source JavaScript module. + +## Non-npm declaration + +If your declaration is for a non-npm package (in other words, if your declaration has a +[Definitely Typed header](https://github.com/Microsoft/definitelytyped-header-parser) *and* +the header specifies that the declaration file is for a non-npm package): + +1. An npm package with the same name of your declaration's package **cannot** exist. +3. If you are running under `code` mode *and* a path to the JavaScript source file was provided, your +declaration must also match the source JavaScript module. + +# Planned work + +1. Make sure your module structure fits the source. +2. Make sure your exported symbols match the source. +3. Make sure your types match the source types??? +6. Download source based on npm homepage (if it is github). + +Note that for real use on Definitely Typed, a lot of these checks need to be pretty loose. + +# Also + +```sh +$ node dist/dt.js +``` + +Will run dts-critic on every directory inside `../DefinitelyTyped` and +print errors. + +# Contributing + +## Testing + +The tests use the [Jest](https://jestjs.io/) framework. To build and execute the tests, run: + +```sh +$ npm run test +``` + +This will build the program and run jest. diff --git a/packages/dts-critic/develop.ts b/packages/dts-critic/develop.ts new file mode 100644 index 00000000..51ce1132 --- /dev/null +++ b/packages/dts-critic/develop.ts @@ -0,0 +1,412 @@ +import fs = require("fs"); +import yargs = require("yargs"); +import headerParser = require("@definitelytyped/header-parser"); +import path = require("path"); +import cp = require("child_process"); +import { + dtsCritic, + dtToNpmName, + getNpmInfo, + parseExportErrorKind, + CriticError, + ExportErrorKind, + Mode, + checkSource, + findDtsName, + CheckOptions, + parseMode} from "./index"; + +const sourcesDir = "sources"; +const downloadsPath = path.join(sourcesDir, "dts-critic-internal/downloads.json"); +const isNpmPath = path.join(sourcesDir, "dts-critic-internal/npm.json"); + +function getPackageDownloads(dtName: string): number { + const npmName = dtToNpmName(dtName); + const url = `https://api.npmjs.org/downloads/point/last-month/${npmName}`; + const result = JSON.parse( + cp.execFileSync( + "curl", + ["--silent", "-L", url], + { encoding: "utf8" })) as { downloads?: number }; + return result.downloads || 0; +} + +interface DownloadsJson { [key: string]: number | undefined } + +function getAllPackageDownloads(dtPath: string): DownloadsJson { + if (fs.existsSync(downloadsPath)) { + return JSON.parse(fs.readFileSync(downloadsPath, { encoding: "utf8" })) as DownloadsJson; + } + + initDir(path.dirname(downloadsPath)); + const downloads: DownloadsJson = {}; + const dtTypesPath = getDtTypesPath(dtPath); + for (const item of fs.readdirSync(dtTypesPath)) { + const d = getPackageDownloads(item); + downloads[item] = d; + } + fs.writeFileSync(downloadsPath, JSON.stringify(downloads), { encoding: "utf8" }); + + return downloads; +} + +function initDir(path: string): void { + if (!fs.existsSync(path)) { + fs.mkdirSync(path); + } +} + +function getDtTypesPath(dtBasePath: string): string { + return path.join(dtBasePath, "types"); +} + +function compareDownloads(downloads: DownloadsJson, package1: string, package2: string): number { + const count1 = downloads[package1] || 0; + const count2 = downloads[package2] || 0; + return count1 - count2; +} + +interface IsNpmJson { [key: string]: boolean | undefined } + +function getAllIsNpm(dtPath: string): IsNpmJson { + if (fs.existsSync(isNpmPath)) { + return JSON.parse(fs.readFileSync(isNpmPath, { encoding: "utf8" })) as IsNpmJson; + } + initDir(path.dirname(isNpmPath)); + const isNpm: IsNpmJson = {}; + const dtTypesPath = getDtTypesPath(dtPath); + for (const item of fs.readdirSync(dtTypesPath)) { + isNpm[item] = getNpmInfo(item).isNpm; + } + fs.writeFileSync(isNpmPath, JSON.stringify(isNpm), { encoding: "utf8" }); + return isNpm; +} + +function getPopularNpmPackages(count: number, dtPath: string): string[] { + const dtPackages = getDtNpmPackages(dtPath); + const downloads = getAllPackageDownloads(dtPath); + dtPackages.sort((a, b) => compareDownloads(downloads, a, b)); + return dtPackages.slice(dtPackages.length - count); +} + +function getUnpopularNpmPackages(count: number, dtPath: string): string[] { + const dtPackages = getDtNpmPackages(dtPath); + const downloads = getAllPackageDownloads(dtPath); + dtPackages.sort((a, b) => compareDownloads(downloads, a, b)); + return dtPackages.slice(0, count); +} + +function getDtNpmPackages(dtPath: string): string[] { + const dtPackages = fs.readdirSync(getDtTypesPath(dtPath)); + const isNpmJson = getAllIsNpm(dtPath); + return dtPackages.filter(pkg => isNpmPackage(pkg, /* header */ undefined, isNpmJson)); +} + +function getNonNpm(args: { dtPath: string }): void { + const nonNpm: string[] = []; + const dtTypesPath = getDtTypesPath(args.dtPath); + const isNpmJson = getAllIsNpm(args.dtPath); + for (const item of fs.readdirSync(dtTypesPath)) { + const entry = path.join(dtTypesPath, item); + const dts = fs.readFileSync(entry + "/index.d.ts", "utf8"); + let header; + try { + header = headerParser.parseHeaderOrFail(dts); + } + catch (e) { + header = undefined; + } + if (!isNpmPackage(item, header, isNpmJson)) { + nonNpm.push(item); + } + } + console.log(`List of non-npm packages on DT:\n${nonNpm.map(name => `DT name: ${name}\n`).join("")}`); +} + +interface CommonArgs { + dtPath: string, + mode: string, + enableError: string[] | undefined, + debug: boolean, + json: boolean, +} + +function checkAll(args: CommonArgs): void { + const dtPackages = fs.readdirSync(getDtTypesPath(args.dtPath)); + checkPackages({ packages: dtPackages, ...args }); +} + +function checkPopular(args: { count: number } & CommonArgs): void { + checkPackages({ packages: getPopularNpmPackages(args.count, args.dtPath), ...args }); +} + +function checkUnpopular(args: { count: number } & CommonArgs): void { + checkPackages({ packages: getUnpopularNpmPackages(args.count, args.dtPath), ...args }); +} + +function checkPackages(args: { packages: string[] } & CommonArgs): void { + const results = args.packages.map(pkg => doCheck({ package: pkg, ...args })); + printResults(results, args.json); +} + +function checkPackage(args: { package: string } & CommonArgs): void { + printResults([doCheck(args)], args.json); +} + +function doCheck(args: { package: string, dtPath: string, mode: string, enableError: string[] | undefined, debug: boolean }): Result { + const dtPackage = args.package; + const opts = getOptions(args.mode, args.enableError || []); + try { + const dtsPath = path.join(getDtTypesPath(args.dtPath), dtPackage, "index.d.ts"); + const errors = dtsCritic(dtsPath, /* sourcePath */ undefined, opts, args.debug); + return { package: args.package, output: errors }; + } + catch (e) { + return { package: args.package, output: e.toString() }; + } +} + +function getOptions(modeArg: string, enabledErrors: string[]): CheckOptions { + const mode = parseMode(modeArg); + if (!mode) { + throw new Error(`Could not find mode named '${modeArg}'.`); + } + switch (mode) { + case Mode.NameOnly: + return { mode }; + case Mode.Code: + const errors = getEnabledErrors(enabledErrors); + return { mode, errors }; + } +} + +function getEnabledErrors(errorNames: string[]): Map { + const errors: ExportErrorKind[] = []; + for (const name of errorNames) { + const error = parseExportErrorKind(name); + if (error === undefined) { + throw new Error(`Could not find error named '${name}'.`); + } + errors.push(error); + } + return new Map(errors.map(err => [err, true])); +} + +function checkFile(args: { jsFile: string, dtsFile: string, debug: boolean }): void { + console.log(`\tChecking JS file ${args.jsFile} and declaration file ${args.dtsFile}`); + try { + const errors = checkSource(findDtsName(args.dtsFile), args.dtsFile, args.jsFile, new Map(), args.debug); + console.log(formatErrors(errors)); + } + catch (e) { + console.log(e); + } +} + +interface Result { + package: string, + output: CriticError[] | string, +} + +function printResults(results: Result[], json: boolean): void { + if (json) { + console.log(JSON.stringify(results)); + return; + } + + for (const result of results) { + console.log(`\tChecking package ${result.package} ...`); + if (typeof result.output === "string") { + console.log(`Exception:\n${result.output}`); + } + else { + console.log(formatErrors(result.output)); + } + } +} + +function formatErrors(errors: CriticError[]): string { + const lines: string[] = []; + for (const error of errors) { + lines.push("Error: " + error.message); + } + if (errors.length === 0) { + lines.push("No errors found! :)"); + } + return lines.join("\n"); +} + +function isNpmPackage(name: string, header?: headerParser.Header, isNpmJson: IsNpmJson = {}): boolean { + if (header && header.nonNpm) return false; + const isNpm = isNpmJson[name]; + if (isNpm !== undefined) { + return isNpm; + } + return getNpmInfo(name).isNpm; +} + +function main() { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + yargs + .usage("$0 ") + .command("check-all", "Check source and declaration of all DT packages that are on NPM.", { + dtPath: { + type: "string", + default: "../DefinitelyTyped", + describe: "Path of DT repository cloned locally.", + }, + mode: { + type: "string", + required: true, + choices: [Mode.NameOnly, Mode.Code], + describe: "Mode that defines which group of checks will be made.", + }, + enableError: { + type: "array", + string: true, + describe: "Enable checking for a specific export error." + }, + debug: { + type: "boolean", + default: false, + describe: "Turn debug logging on.", + }, + json: { + type: "boolean", + default: false, + describe: "Format output result as json." + }, + }, checkAll) + .command("check-popular", "Check source and declaration of most popular DT packages that are on NPM.", { + count: { + alias: "c", + type: "number", + required: true, + describe: "Number of packages to be checked.", + }, + dtPath: { + type: "string", + default: "../DefinitelyTyped", + describe: "Path of DT repository cloned locally.", + }, + mode: { + type: "string", + required: true, + choices: [Mode.NameOnly, Mode.Code], + describe: "Mode that defines which group of checks will be made.", + }, + enableError: { + type: "array", + string: true, + describe: "Enable checking for a specific export error." + }, + debug: { + type: "boolean", + default: false, + describe: "Turn debug logging on.", + }, + json: { + type: "boolean", + default: false, + describe: "Format output result as json." + }, + }, checkPopular) + .command("check-unpopular", "Check source and declaration of least popular DT packages that are on NPM.", { + count: { + alias: "c", + type: "number", + required: true, + describe: "Number of packages to be checked.", + }, + dtPath: { + type: "string", + default: "../DefinitelyTyped", + describe: "Path of DT repository cloned locally.", + }, + mode: { + type: "string", + required: true, + choices: [Mode.NameOnly, Mode.Code], + describe: "Mode that defines which group of checks will be made.", + }, + enableError: { + type: "array", + string: true, + describe: "Enable checking for a specific export error." + }, + debug: { + type: "boolean", + default: false, + describe: "Turn debug logging on.", + }, + json: { + type: "boolean", + default: false, + describe: "Format output result as json." + }, + }, checkUnpopular) + .command("check-package", "Check source and declaration of a DT package.", { + package: { + alias: "p", + type: "string", + required: true, + describe: "DT name of a package." + }, + dtPath: { + type: "string", + default: "../DefinitelyTyped", + describe: "Path of DT repository cloned locally.", + }, + mode: { + type: "string", + required: true, + choices: [Mode.NameOnly, Mode.Code], + describe: "Mode that defines which group of checks will be made.", + }, + enableError: { + type: "array", + string: true, + describe: "Enable checking for a specific export error." + }, + debug: { + type: "boolean", + default: false, + describe: "Turn debug logging on.", + }, + json: { + type: "boolean", + default: false, + describe: "Format output result as json." + }, + }, checkPackage) + .command("check-file", "Check a JavaScript file and its matching declaration file.", { + jsFile: { + alias: "j", + type: "string", + required: true, + describe: "Path of JavaScript file.", + }, + dtsFile: { + alias: "d", + type: "string", + required: true, + describe: "Path of declaration file.", + }, + debug: { + type: "boolean", + default: false, + describe: "Turn debug logging on.", + }, + }, checkFile) + .command("get-non-npm", "Get list of DT packages whose source package is not on NPM", { + dtPath: { + type: "string", + default: "../DefinitelyTyped", + describe: "Path of DT repository cloned locally.", + }, + }, getNonNpm) + .demandCommand(1) + .help() + .argv; +} +main(); diff --git a/packages/dts-critic/dt.ts b/packages/dts-critic/dt.ts new file mode 100644 index 00000000..d53465ba --- /dev/null +++ b/packages/dts-critic/dt.ts @@ -0,0 +1,79 @@ +import { dtsCritic as critic, ErrorKind } from "./index"; +import fs = require("fs"); +import stripJsonComments = require("strip-json-comments"); + +function hasNpmNamingLintRule(tslintPath: string): boolean { + if (fs.existsSync(tslintPath)) { + const tslint = JSON.parse(stripJsonComments(fs.readFileSync(tslintPath, "utf-8"))); + if(tslint.rules && tslint.rules["npm-naming"] !== undefined) { + return !!tslint.rules["npm-naming"]; + } + return true; + } + return false; +} + +function addNpmNamingLintRule(tslintPath: string): void { + if (fs.existsSync(tslintPath)) { + const tslint = JSON.parse(stripJsonComments(fs.readFileSync(tslintPath, "utf-8"))); + if (tslint.rules) { + tslint.rules["npm-naming"] = false; + } + else { + tslint.rules = { "npm-naming": false }; + } + fs.writeFileSync(tslintPath, JSON.stringify(tslint, undefined, 4), "utf-8"); + } +} + +function main() { + for (const item of fs.readdirSync("../DefinitelyTyped/types")) { + const entry = "../DefinitelyTyped/types/" + item; + try { + if (hasNpmNamingLintRule(entry + "/tslint.json")) { + const errors = critic(entry + "/index.d.ts"); + for (const error of errors) { + switch (error.kind) { + case ErrorKind.NoMatchingNpmPackage: + console.log(`No matching npm package found for ` + item); + // const re = /\/\/ Type definitions for/; + // const s = fs.readFileSync(entry + '/index.d.ts', 'utf-8') + // fs.writeFileSync(entry + '/index.d.ts', s.replace(re, '// Type definitions for non-npm package'), 'utf-8') + break; + case ErrorKind.NoDefaultExport: + console.log("converting", item, "to export = ..."); + const named = /export default function\s+(\w+\s*)\(/; + const anon = /export default function\s*\(/; + const id = /export default(\s+\w+);/; + let s = fs.readFileSync(entry + "/index.d.ts", "utf-8"); + s = s.replace(named, "export = $1;\ndeclare function $1("); + s = s.replace(anon, "export = _default;\ndeclare function _default("); + s = s.replace(id, "export =$1;"); + fs.writeFileSync(entry + "/index.d.ts", s, "utf-8"); + break; + case ErrorKind.NoMatchingNpmVersion: + const m = error.message.match(/in the header, ([0-9.]+),[\s\S]to match one on npm: ([0-9., ]+)\./); + if (m) { + const headerver = parseFloat(m[1]); + const npmvers = m[2].split(",").map((s: string) => parseFloat(s.trim())); + const fixto = npmvers.every((v: number) => headerver > v) ? -1.0 : Math.max(...npmvers); + console.log(`npm-version:${item}:${m[1]}:${m[2]}:${fixto}`); + addNpmNamingLintRule(entry + "/tslint.json"); + } + else { + console.log("could not parse error message: ", error.message); + } + break; + default: + console.log(error.message); + } + } + } + } + catch (e) { + console.log("*** ERROR for " + item + " ***"); + console.log(e); + } + } +} +main(); diff --git a/packages/dts-critic/index.test.ts b/packages/dts-critic/index.test.ts new file mode 100644 index 00000000..7db0fe06 --- /dev/null +++ b/packages/dts-critic/index.test.ts @@ -0,0 +1,259 @@ +import { + findDtsName, + getNpmInfo, + dtToNpmName, + parseExportErrorKind, + dtsCritic, + checkSource, + ErrorKind, + ExportErrorKind } from "./index"; + +function suite(description: string, tests: { [s: string]: () => void; }) { + describe(description, () => { + for (const k in tests) { + test(k, tests[k], 10 * 1000); + } + }); +} + +suite("findDtsName", { + absolutePath() { + expect(findDtsName("~/dt/types/jquery/index.d.ts")).toBe("jquery"); + }, + relativePath() { + expect(findDtsName("jquery/index.d.ts")).toBe("jquery"); + }, + currentDirectory() { + expect(findDtsName("index.d.ts")).toBe("dts-critic"); + }, + relativeCurrentDirectory() { + expect(findDtsName("./index.d.ts")).toBe("dts-critic"); + }, + emptyDirectory() { + expect(findDtsName("")).toBe("dts-critic"); + }, +}); +suite("getNpmInfo", { + nonNpm() { + expect(getNpmInfo("parseltongue")).toEqual({ isNpm: false }); + }, + npm() { + expect(getNpmInfo("typescript")).toEqual({ + isNpm: true, + versions: expect.arrayContaining(["3.7.5"]), + tags: expect.objectContaining({ latest: expect.stringContaining("") }), + }); + }, +}); +suite("dtToNpmName", { + nonScoped() { + expect(dtToNpmName("content-type")).toBe("content-type"); + }, + scoped() { + expect(dtToNpmName("babel__core")).toBe("@babel/core"); + }, +}); +suite("parseExportErrorKind", { + existent() { + expect(parseExportErrorKind("NoDefaultExport")).toBe(ErrorKind.NoDefaultExport); + }, + existentDifferentCase() { + expect(parseExportErrorKind("JspropertyNotinDTS")).toBe(ErrorKind.JsPropertyNotInDts); + }, + nonexistent() { + expect(parseExportErrorKind("FakeError")).toBe(undefined); + } +}); + +const allErrors: Map = new Map([ + [ErrorKind.NeedsExportEquals, true], + [ErrorKind.NoDefaultExport, true], + [ErrorKind.JsSignatureNotInDts, true], + [ErrorKind.DtsSignatureNotInJs, true], + [ErrorKind.DtsPropertyNotInJs, true], + [ErrorKind.JsPropertyNotInDts, true], +]); + +suite("checkSource", { + noErrors() { + expect(checkSource( + "noErrors", + "testsource/noErrors.d.ts", + "testsource/noErrors.js", + allErrors, + false, + )).toEqual([]); + }, + missingJsProperty() { + expect(checkSource( + "missingJsProperty", + "testsource/missingJsProperty.d.ts", + "testsource/missingJsProperty.js", + allErrors, + false, + )).toEqual(expect.arrayContaining([ + { + kind: ErrorKind.JsPropertyNotInDts, + message: `The declaration doesn't match the JavaScript module 'missingJsProperty'. Reason: +The JavaScript module exports a property named 'foo', which is missing from the declaration module.` + } + ])); + }, + noMissingWebpackProperty() { + expect(checkSource( + "missingJsProperty", + "testsource/webpackPropertyNames.d.ts", + "testsource/webpackPropertyNames.js", + allErrors, + false, + )).toHaveLength(0); + }, + missingDtsProperty() { + expect(checkSource( + "missingDtsProperty", + "testsource/missingDtsProperty.d.ts", + "testsource/missingDtsProperty.js", + allErrors, + false, + )).toEqual(expect.arrayContaining([ + { + kind: ErrorKind.DtsPropertyNotInJs, + message: `The declaration doesn't match the JavaScript module 'missingDtsProperty'. Reason: +The declaration module exports a property named 'foo', which is missing from the JavaScript module.`, + position: { + start: 65, + length: 11, + }, + } + ])); + }, + missingDefaultExport() { + expect(checkSource( + "missingDefault", + "testsource/missingDefault.d.ts", + "testsource/missingDefault.js", + allErrors, + false, + )).toEqual(expect.arrayContaining([ + { + kind: ErrorKind.NoDefaultExport, + message: `The declaration doesn't match the JavaScript module 'missingDefault'. Reason: +The declaration specifies 'export default' but the JavaScript source does not mention 'default' anywhere. + +The most common way to resolve this error is to use 'export =' syntax instead of 'export default'. +To learn more about 'export =' syntax, see https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require.`, + position: { + start: 0, + length: 32, + }, + } + ])); + }, + missingJsSignatureExportEquals() { + expect(checkSource( + "missingJsSignatureExportEquals", + "testsource/missingJsSignatureExportEquals.d.ts", + "testsource/missingJsSignatureExportEquals.js", + allErrors, + false, + )).toEqual(expect.arrayContaining([ + { + kind: ErrorKind.JsSignatureNotInDts, + message: `The declaration doesn't match the JavaScript module 'missingJsSignatureExportEquals'. Reason: +The JavaScript module can be called or constructed, but the declaration module cannot.`, + } + ])); + }, + missingJsSignatureNoExportEquals() { + expect(checkSource( + "missingJsSignatureNoExportEquals", + "testsource/missingJsSignatureNoExportEquals.d.ts", + "testsource/missingJsSignatureNoExportEquals.js", + allErrors, + false, + )).toEqual(expect.arrayContaining([ + { + kind: ErrorKind.JsSignatureNotInDts, + message: `The declaration doesn't match the JavaScript module 'missingJsSignatureNoExportEquals'. Reason: +The JavaScript module can be called or constructed, but the declaration module cannot. + +The most common way to resolve this error is to use 'export =' syntax. +To learn more about 'export =' syntax, see https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require.`, + } + ])); + }, + missingDtsSignature() { + expect(checkSource( + "missingDtsSignature", + "testsource/missingDtsSignature.d.ts", + "testsource/missingDtsSignature.js", + allErrors, + false, + )).toEqual(expect.arrayContaining([ + { + kind: ErrorKind.DtsSignatureNotInJs, + message: `The declaration doesn't match the JavaScript module 'missingDtsSignature'. Reason: +The declaration module can be called or constructed, but the JavaScript module cannot.`, + } + ])); + }, + missingExportEquals() { + expect(checkSource( + "missingExportEquals", + "testsource/missingExportEquals.d.ts", + "testsource/missingExportEquals.js", + allErrors, + false, + )).toEqual(expect.arrayContaining([ + { + kind: ErrorKind.NeedsExportEquals, + message: `The declaration doesn't match the JavaScript module 'missingExportEquals'. Reason: +The declaration should use 'export =' syntax because the JavaScript source uses 'module.exports =' syntax and 'module.exports' can be called or constructed. + +To learn more about 'export =' syntax, see https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require.`, + } + ])); + }, +}); +suite("dtsCritic", { + noErrors() { + expect(dtsCritic("testsource/dts-critic.d.ts", "testsource/dts-critic.js")).toEqual([]); + }, + noMatchingNpmPackage() { + expect(dtsCritic("testsource/parseltongue.d.ts")).toEqual([ + { + kind: ErrorKind.NoMatchingNpmPackage, + message: `Declaration file must have a matching npm package. +To resolve this error, either: +1. Change the name to match an npm package. +2. Add a Definitely Typed header with the first line + + +// Type definitions for non-npm package parseltongue-browser + +Add -browser to the end of your name to make sure it doesn't conflict with existing npm packages.`, + }, + ]); + }, + noMatchingNpmVersion() { + expect(dtsCritic("testsource/typescript.d.ts")).toEqual([ + { + kind: ErrorKind.NoMatchingNpmVersion, + message: expect.stringContaining(`The types for 'typescript' must match a version that exists on npm. +You should copy the major and minor version from the package on npm.`), + }, + ]); + }, + nonNpmHasMatchingPackage() { + expect(dtsCritic("testsource/tslib.d.ts")).toEqual([ + { + kind: ErrorKind.NonNpmHasMatchingPackage, + message: `The non-npm package 'tslib' conflicts with the existing npm package 'tslib'. +Try adding -browser to the end of the name to get + + tslib-browser +`, + }, + ]); + } +}); diff --git a/packages/dts-critic/index.ts b/packages/dts-critic/index.ts new file mode 100644 index 00000000..7e6932a2 --- /dev/null +++ b/packages/dts-critic/index.ts @@ -0,0 +1,1031 @@ +import yargs = require("yargs"); +import headerParser = require("@definitelytyped/header-parser"); +import fs = require("fs"); +import os = require("os") +import cp = require("child_process"); +import path = require("path"); +import semver = require("semver"); +import rimraf = require("rimraf"); +import { sync as commandExistsSync } from "command-exists"; +import ts from "typescript"; +import * as tmp from "tmp"; + +export enum ErrorKind { + /** Declaration is marked as npm in header and has no matching npm package. */ + NoMatchingNpmPackage = "NoMatchingNpmPackage", + /** Declaration has no npm package matching specified version. */ + NoMatchingNpmVersion = "NoMatchingNpmVersion", + /** Declaration is not for an npm package, but has a name that conflicts with an existing npm package. */ + NonNpmHasMatchingPackage = "NonNpmHasMatchingPackage", + /** Declaration needs to use `export =` to match the JavaScript module's behavior. */ + NeedsExportEquals = "NeedsExportEquals", + /** Declaration has a default export, but JavaScript module does not have a default export. */ + NoDefaultExport = "NoDefaultExport", + /** JavaScript exports property not found in declaration exports. */ + JsPropertyNotInDts = "JsPropertyNotInDts", + /** Declaration exports property not found in JavaScript exports. */ + DtsPropertyNotInJs = "DtsPropertyNotInJs", + /** JavaScript module has signatures, but declaration module does not. */ + JsSignatureNotInDts = "JsSignatureNotInDts", + /** Declaration module has signatures, but JavaScript module does not. */ + DtsSignatureNotInJs = "DtsSignatureNotInJs", +} + +export enum Mode { + /** Checks based only on the package name and on the declaration's DefinitelyTyped header. */ + NameOnly = "name-only", + /** Checks based on the source JavaScript code, in addition to the checks performed in name-only mode. */ + Code = "code", +} + +export function parseMode(mode: string): Mode | undefined { + switch (mode) { + case Mode.NameOnly: + return Mode.NameOnly; + case Mode.Code: + return Mode.Code; + } + return undefined; +} + +export type CheckOptions = NameOnlyOptions | CodeOptions; +export interface NameOnlyOptions { + mode: Mode.NameOnly, +} +export interface CodeOptions { + mode: Mode.Code, + errors: Map, +} + +export type ExportErrorKind = ExportError["kind"]; + +const defaultOpts: CheckOptions = { mode: Mode.NameOnly }; + +export function dtsCritic(dtsPath: string, sourcePath?: string, options: CheckOptions = defaultOpts, debug = false): CriticError[] { + if (!commandExistsSync("tar")) { + throw new Error("You need to have tar installed to run dts-critic, you can get it from https://www.gnu.org/software/tar"); + } + if (!commandExistsSync("npm")) { + throw new Error("You need to have npm installed to run dts-critic, you can get it from https://www.npmjs.com/get-npm"); + } + + const dts = fs.readFileSync(dtsPath, "utf-8"); + const header = parseDtHeader(dts); + + const name = findDtsName(dtsPath); + const npmInfo = getNpmInfo(name); + + if (isNonNpm(header)) { + const errors: CriticError[] = []; + const nonNpmError = checkNonNpm(name, npmInfo); + if (nonNpmError) { + errors.push(nonNpmError); + } + + if (sourcePath) { + if (options.mode === Mode.Code) { + errors.push(...checkSource(name, dtsPath, sourcePath, options.errors, debug)); + } + } + else if (!module.parent) { + console.log(`Warning: declaration provided is for a non-npm package. +If you want to check the declaration against the JavaScript source code, you must provide a path to the source file.`); + } + + return errors; + } + else { + const npmVersion = checkNpm(name, npmInfo, header); + if (typeof npmVersion !== "string") { + return [npmVersion]; + } + + if (options.mode === Mode.Code) { + let sourceEntry; + let packagePath; + if (sourcePath) { + sourceEntry = sourcePath; + } + else { + const tempDirName = tmp.dirSync({ unsafeCleanup: true }).name + packagePath = downloadNpmPackage(name, npmVersion, tempDirName) + sourceEntry = require.resolve(path.resolve(packagePath)); + } + const errors = checkSource(name, dtsPath, sourceEntry, options.errors, debug); + if (packagePath) { + // Delete the source afterward to avoid running out of space + rimraf.sync(packagePath) + } + return errors; + } + + return []; + } +} + +function parseDtHeader(dts: string): headerParser.Header | undefined { + try { + return headerParser.parseHeaderOrFail(dts); + } + catch (e) { + return undefined; + } +} + +function isNonNpm(header: headerParser.Header | undefined): boolean { + return !!header && header.nonNpm; +} + +export const defaultErrors: ExportErrorKind[] = [ErrorKind.NeedsExportEquals, ErrorKind.NoDefaultExport]; + +function main() { + const argv = yargs. + usage("$0 --dts path-to-d.ts [--js path-to-source] [--mode mode] [--debug]\n\nIf source-folder is not provided, I will look for a matching package on npm."). + option("dts", { + describe: "Path of declaration file to be critiqued.", + type: "string", + }). + demandOption("dts", "Please provide a path to a d.ts file for me to critique."). + option("js", { + describe: "Path of JavaScript file to be used as source.", + type: "string", + }). + option("mode", { + describe: "Mode defines what checks will be performed.", + type: "string", + default: Mode.NameOnly, + choices: [Mode.NameOnly, Mode.Code], + }). + option("debug", { + describe: "Turn debug logging on.", + type: "boolean", + default: false, + }). + help(). + argv; + + let opts; + switch (argv.mode) { + case Mode.NameOnly: + opts = { mode: argv.mode }; + break; + case Mode.Code: + opts = { mode: argv.mode, errors: new Map() }; + } + const errors = dtsCritic(argv.dts, argv.js, opts, argv.debug); + if (errors.length === 0) { + console.log("No errors!"); + } + else { + for (const error of errors) { + console.log("Error: " + error.message); + } + } +} + +const npmNotFound = "E404"; + +export function getNpmInfo(name: string): NpmInfo { + const npmName = dtToNpmName(name); + const infoResult = cp.spawnSync( + "npm", + ["info", npmName, "--json", "--silent", "versions", "dist-tags"], + { encoding: "utf8" }); + const info = JSON.parse(infoResult.stdout || infoResult.stderr); + if (info.error !== undefined) { + const error = info.error as { code?: string, summary?: string }; + if (error.code === npmNotFound) { + return { isNpm: false }; + } + else { + throw new Error(`Command 'npm info' for package ${npmName} returned an error. Reason: ${error.summary}.`); + } + } + else if (infoResult.status !== 0) { + throw new Error(`Command 'npm info' failed for package ${npmName} with status ${infoResult.status}.`); + } + return { + isNpm: true, + versions: info.versions as string[], + tags: info["dist-tags"] as { [tag: string]: string | undefined } + }; +} + +/** + * Checks DefinitelyTyped non-npm package. + */ +function checkNonNpm(name: string, npmInfo: NpmInfo): NonNpmError | undefined { + if (npmInfo.isNpm && !isExistingSquatter(name)) { + return { + kind: ErrorKind.NonNpmHasMatchingPackage, + message: `The non-npm package '${name}' conflicts with the existing npm package '${dtToNpmName(name)}'. +Try adding -browser to the end of the name to get + + ${name}-browser +` + }; + } + return undefined; +} + +/** + * Checks DefinitelyTyped npm package. + * If all checks are successful, returns the npm version that matches the header. + */ +function checkNpm(name: string, npmInfo: NpmInfo, header: headerParser.Header | undefined): NpmError | string { + if (!npmInfo.isNpm) { + return { + kind: ErrorKind.NoMatchingNpmPackage, + message: `Declaration file must have a matching npm package. +To resolve this error, either: +1. Change the name to match an npm package. +2. Add a Definitely Typed header with the first line + + +// Type definitions for non-npm package ${name}-browser + +Add -browser to the end of your name to make sure it doesn't conflict with existing npm packages.` + }; + } + const target = getHeaderVersion(header); + const npmVersion = getMatchingVersion(target, npmInfo); + if (!npmVersion) { + const versions = npmInfo.versions; + const verstring = versions.join(", "); + const lateststring = versions[versions.length - 1]; + const headerstring = target || "NO HEADER VERSION FOUND"; + return { + kind: ErrorKind.NoMatchingNpmVersion, + message: `The types for '${name}' must match a version that exists on npm. +You should copy the major and minor version from the package on npm. + +To resolve this error, change the version in the header, ${headerstring}, +to match one on npm: ${verstring}. + +For example, if you're trying to match the latest version, use ${lateststring}.`, + }; + + } + return npmVersion; +} + +function getHeaderVersion(header: headerParser.Header | undefined): string | undefined { + if (!header) { + return undefined; + } + if (header.libraryMajorVersion === 0 && header.libraryMinorVersion === 0) { + return undefined; + } + return `${header.libraryMajorVersion}.${header.libraryMinorVersion}`; +} + +/** + * Finds an npm version that matches the target version specified, if it exists. + * If the target version is undefined, returns the latest version. + * The npm version returned might be a prerelease version. + */ +function getMatchingVersion(target: string | undefined, npmInfo: Npm): string | undefined { + const versions = npmInfo.versions; + if (target) { + const matchingVersion = semver.maxSatisfying(versions, target, { includePrerelease: true }); + return matchingVersion || undefined; + } + if (npmInfo.tags.latest) { + return npmInfo.tags.latest; + } + return versions[versions.length - 1]; +} + +/** + * If dtsName is 'index' (as with DT) then look to the parent directory for the name. + */ +export function findDtsName(dtsPath: string) { + const resolved = path.resolve(dtsPath); + const baseName = path.basename(resolved, ".d.ts"); + if (baseName && baseName !== "index") { + return baseName; + } + return path.basename(path.dirname(resolved)); +} + +/** Default path to store packages downloaded from npm. */ +const sourceDir = path.resolve(path.join(__dirname, "..", "sources")); + +/** Returns path of downloaded npm package. */ +function downloadNpmPackage(name: string, version: string, outDir: string): string { + const npmName = dtToNpmName(name); + const fullName = `${npmName}@${version}`; + const cpOpts = { encoding: "utf8", maxBuffer: 100 * 1024 * 1024 } as const; + const npmPack = cp.execFileSync("npm", ["pack", fullName, "--json", "--silent"], cpOpts).trim(); + const tarballName = npmPack.endsWith(".tgz") ? npmPack : JSON.parse(npmPack)[0].filename as string; + const outPath = path.join(outDir, name); + initDir(outPath); + const args = os.platform() === "darwin" + ? ["-xz", "-f", tarballName, "-C", outPath] + : ["-xz", "-f", tarballName, "-C", outPath, "--warning=none"]; + cp.execFileSync("tar", args, cpOpts); + fs.unlinkSync(tarballName); + return path.join(outPath, getPackageDir(outPath)); +} + +function getPackageDir(outPath: string): string { + const dirs = fs.readdirSync(outPath, { encoding: "utf8", withFileTypes: true }); + for (const dirent of dirs) { + if (dirent.isDirectory()) { + return dirent.name; + } + } + return "package"; +} + +function initDir(dirPath: string): void { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } +} + +export function checkSource( + name: string, + dtsPath: string, + srcPath: string, + enabledErrors: Map, + debug: boolean): ExportError[] { + const diagnostics = checkExports(name, dtsPath, srcPath); + if (debug) { + console.log(formatDebug(name, diagnostics)); + } + + return diagnostics.errors.filter(err => enabledErrors.get(err.kind) ?? defaultErrors.includes(err.kind)); +} + +function formatDebug(name: string, diagnostics: ExportsDiagnostics): string { + const lines: string[] = []; + lines.push(`\tDiagnostics for package ${name}.`); + lines.push("\tInferred source module structure:"); + if (isSuccess(diagnostics.jsExportKind)) { + lines.push(diagnostics.jsExportKind.result); + } + else { + lines.push(`Could not infer type of JavaScript exports. Reason: ${diagnostics.jsExportKind.reason}`); + } + lines.push("\tInferred source export type:"); + if (isSuccess(diagnostics.jsExportType)) { + lines.push(formatType(diagnostics.jsExportType.result)); + } + else { + lines.push(`Could not infer type of JavaScript exports. Reason: ${diagnostics.jsExportType.reason}`); + } + if (diagnostics.dtsExportKind) { + lines.push("\tInferred declaration module structure:"); + if (isSuccess(diagnostics.dtsExportKind)) { + lines.push(diagnostics.dtsExportKind.result); + } + else { + lines.push(`Could not infer type of declaration exports. Reason: ${diagnostics.dtsExportKind.reason}`); + } + } + if (diagnostics.dtsExportType) { + lines.push("\tInferred declaration export type:"); + if (isSuccess(diagnostics.dtsExportType)) { + lines.push(formatType(diagnostics.dtsExportType.result)); + } + else { + lines.push(`Could not infer type of declaration exports. Reason: ${diagnostics.dtsExportType.reason}`); + } + } + return lines.join("\n"); +} + +function formatType(type: ts.Type): string { + const lines: string[] = []; + //@ts-ignore property `checker` of `ts.Type` is marked internal. The alternative is to have a TypeChecker parameter. + const checker: ts.TypeChecker = type.checker; + + const properties = type.getProperties(); + if (properties.length > 0) { + lines.push("Type's properties:"); + lines.push(...properties.map(p => p.getName())); + } + + const signatures = type.getConstructSignatures().concat(type.getCallSignatures()); + if (signatures.length > 0) { + lines.push("Type's signatures:"); + lines.push(...signatures.map(s => checker.signatureToString(s))); + } + lines.push(`Type string: ${checker.typeToString(type)}`); + return lines.join("\n"); +} + +const exportEqualsLink = "https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require"; + +/** + * Checks exports of a declaration file against its JavaScript source. + */ +function checkExports(name: string, dtsPath: string, sourcePath: string): ExportsDiagnostics { + const tscOpts = { + allowJs: true, + }; + + const jsProgram = ts.createProgram([sourcePath], tscOpts); + const jsFileNode = jsProgram.getSourceFile(sourcePath); + if (!jsFileNode) { + throw new Error(`TS compiler could not find source file ${sourcePath}.`); + } + const jsChecker = jsProgram.getTypeChecker(); + + const errors: ExportError[] = []; + const sourceDiagnostics = inspectJs(jsFileNode, jsChecker, name); + + const dtsDiagnostics = inspectDts(dtsPath, name); + + if (isSuccess(sourceDiagnostics.exportEquals) + && sourceDiagnostics.exportEquals.result.judgement === ExportEqualsJudgement.Required + && isSuccess(dtsDiagnostics.exportKind) + && dtsDiagnostics.exportKind.result !== DtsExportKind.ExportEquals) { + const error = { + kind: ErrorKind.NeedsExportEquals, + message: `The declaration doesn't match the JavaScript module '${name}'. Reason: +The declaration should use 'export =' syntax because the JavaScript source uses 'module.exports =' syntax and ${sourceDiagnostics.exportEquals.result.reason}. + +To learn more about 'export =' syntax, see ${exportEqualsLink}.`, + } as const; + errors.push(error); + } + + const compatibility = + exportTypesCompatibility( + name, + sourceDiagnostics.exportType, + dtsDiagnostics.exportType, + dtsDiagnostics.exportKind); + + if (isSuccess(compatibility)) { + errors.push(...compatibility.result); + } + + if (dtsDiagnostics.defaultExport && !sourceDiagnostics.exportsDefault) { + errors.push({ + kind: ErrorKind.NoDefaultExport, + position: dtsDiagnostics.defaultExport, + message: `The declaration doesn't match the JavaScript module '${name}'. Reason: +The declaration specifies 'export default' but the JavaScript source does not mention 'default' anywhere. + +The most common way to resolve this error is to use 'export =' syntax instead of 'export default'. +To learn more about 'export =' syntax, see ${exportEqualsLink}.`, + }); + } + + return { + jsExportKind: sourceDiagnostics.exportKind, + jsExportType: sourceDiagnostics.exportType, + dtsExportKind: dtsDiagnostics.exportKind, + dtsExportType: dtsDiagnostics.exportType, + errors }; +} + +function inspectJs(sourceFile: ts.SourceFile, checker: ts.TypeChecker, packageName: string): JsExportsInfo { + const exportKind = getJsExportKind(sourceFile); + const exportType = getJSExportType(sourceFile, checker, exportKind); + const exportsDefault = sourceExportsDefault(sourceFile, packageName); + + let exportEquals; + if (isSuccess(exportType) && isSuccess(exportKind) && exportKind.result === JsExportKind.CommonJs) { + exportEquals = moduleTypeNeedsExportEquals(exportType.result, checker); + } + else { + exportEquals = mergeErrors(exportType, exportKind); + } + + return { exportKind, exportType, exportEquals, exportsDefault }; +} + +function getJsExportKind(sourceFile: ts.SourceFile): InferenceResult { + // @ts-ignore property `commonJsModuleIndicator` of `ts.SourceFile` is marked internal. + if (sourceFile.commonJsModuleIndicator) { + return inferenceSuccess(JsExportKind.CommonJs); + } + // @ts-ignore property `externalModuleIndicator` of `ts.SourceFile` is marked internal. + if (sourceFile.externalModuleIndicator) { + return inferenceSuccess(JsExportKind.ES6); + } + return inferenceError("Could not infer export kind of source file."); +} + +function getJSExportType( + sourceFile: ts.SourceFile, + checker: ts.TypeChecker, + exportKind: InferenceResult): InferenceResult { + if (isSuccess(exportKind)) { + switch (exportKind.result) { + case JsExportKind.CommonJs: { + checker.getSymbolAtLocation(sourceFile); // TODO: get symbol in a safer way? + //@ts-ignore property `symbol` of `ts.Node` is marked internal. + const fileSymbol: ts.Symbol | undefined = sourceFile.symbol; + if (!fileSymbol) { + return inferenceError(`TS compiler could not find symbol for file node '${sourceFile.fileName}'.`); + } + const exportType = checker.getTypeOfSymbolAtLocation(fileSymbol, sourceFile); + return inferenceSuccess(exportType); + } + case JsExportKind.ES6: { + const fileSymbol = checker.getSymbolAtLocation(sourceFile); + if (!fileSymbol) { + return inferenceError(`TS compiler could not find symbol for file node '${sourceFile.fileName}'.`); + } + const exportType = checker.getTypeOfSymbolAtLocation(fileSymbol, sourceFile); + return inferenceSuccess(exportType); + } + } + } + return inferenceError(`Could not infer type of exports because exports kind is undefined.`); +} + +/** + * Decide if a JavaScript source module could have a default export. + */ +function sourceExportsDefault(sourceFile: ts.SourceFile, name: string): boolean { + const src = sourceFile.getFullText(sourceFile); + return isRealExportDefault(name) + || src.indexOf("default") > -1 + || src.indexOf("__esModule") > -1 + || src.indexOf("react-side-effect") > -1 + || src.indexOf("@flow") > -1 + || src.indexOf("module.exports = require") > -1; +} + +function moduleTypeNeedsExportEquals(type: ts.Type, checker: ts.TypeChecker): InferenceResult { + if (isBadType(type)) { + return inferenceError(`Inferred type '${checker.typeToString(type)}' is not good enough to be analyzed.`); + } + + const isObject = type.getFlags() & ts.TypeFlags.Object; + // @ts-ignore property `isArrayLikeType` of `ts.TypeChecker` is marked internal. + if (isObject && !hasSignatures(type) && !checker.isArrayLikeType(type)) { + const judgement = ExportEqualsJudgement.NotRequired; + const reason = "'module.exports' is an object which is neither a function, class, or array"; + return inferenceSuccess({ judgement, reason }); + } + + if (hasSignatures(type)) { + const judgement = ExportEqualsJudgement.Required; + const reason = "'module.exports' can be called or constructed"; + return inferenceSuccess({ judgement, reason }); + } + + const primitive = ts.TypeFlags.Boolean | ts.TypeFlags.String | ts.TypeFlags.Number; + if (type.getFlags() & primitive) { + const judgement = ExportEqualsJudgement.Required; + const reason = `'module.exports' has primitive type ${checker.typeToString(type)}`; + return inferenceSuccess({ judgement, reason }); + } + + // @ts-ignore property `isArrayLikeType` of `ts.TypeChecker` is marked internal. + if (checker.isArrayLikeType(type)) { + const judgement = ExportEqualsJudgement.Required; + const reason = `'module.exports' has array-like type ${checker.typeToString(type)}`; + return inferenceSuccess({ judgement, reason }); + } + + return inferenceError(`Could not analyze type '${checker.typeToString(type)}'.`); +} + +function hasSignatures(type: ts.Type): boolean { + return type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0; +} + +function inspectDts(dtsPath: string, name: string): DtsExportDiagnostics { + dtsPath = path.resolve(dtsPath); + const program = createDtProgram(dtsPath); + const sourceFile = program.getSourceFile(path.resolve(dtsPath)); + if (!sourceFile) { + throw new Error(`TS compiler could not find source file '${dtsPath}'.`); + } + const checker = program.getTypeChecker(); + const symbolResult = getDtsModuleSymbol(sourceFile, checker, name); + const exportKindResult = getDtsExportKind(sourceFile); + const exportType = getDtsExportType(sourceFile, checker, symbolResult, exportKindResult); + const defaultExport = getDtsDefaultExport(sourceFile, exportType); + + return { exportKind: exportKindResult, exportType, defaultExport }; +} + +function createDtProgram(dtsPath: string): ts.Program { + const dtsDir = path.dirname(dtsPath); + const configPath = path.join(dtsDir, "tsconfig.json"); + const { config } = ts.readConfigFile(configPath, p => fs.readFileSync(p, { encoding: "utf8" })); + const parseConfigHost: ts.ParseConfigHost = { + fileExists: fs.existsSync, + readDirectory: ts.sys.readDirectory, + readFile: file => fs.readFileSync(file, { encoding: "utf8" }), + useCaseSensitiveFileNames: true, + }; + const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, path.resolve(dtsDir)); + const host = ts.createCompilerHost(parsed.options, true); + return ts.createProgram([path.resolve(dtsPath)], parsed.options, host); +} + +function getDtsModuleSymbol(sourceFile: ts.SourceFile, checker: ts.TypeChecker, name: string): InferenceResult { + if (matches(sourceFile, node => ts.isModuleDeclaration(node))) { + const npmName = dtToNpmName(name); + const moduleSymbol = checker.getAmbientModules().find(symbol => symbol.getName() === `"${npmName}"`); + if (moduleSymbol) { + return inferenceSuccess(moduleSymbol); + } + } + + const fileSymbol = checker.getSymbolAtLocation(sourceFile); + if (fileSymbol && (fileSymbol.getFlags() & ts.SymbolFlags.ValueModule)) { + return inferenceSuccess(fileSymbol); + } + + return inferenceError(`Could not find module symbol for source file node.`); +} + +function getDtsExportKind(sourceFile: ts.SourceFile): InferenceResult { + if (matches(sourceFile, isExportEquals)) { + return inferenceSuccess(DtsExportKind.ExportEquals); + } + if (matches(sourceFile, isExportConstruct)) { + return inferenceSuccess(DtsExportKind.ES6Like); + } + return inferenceError("Could not infer export kind of declaration file."); +} + +const exportEqualsSymbolName = "export="; + +function getDtsExportType( + sourceFile: ts.SourceFile, + checker: ts.TypeChecker, + symbolResult: InferenceResult, + exportKindResult: InferenceResult): InferenceResult { + if (isSuccess(symbolResult) && isSuccess(exportKindResult)) { + const symbol = symbolResult.result; + const exportKind = exportKindResult.result; + switch (exportKind) { + case (DtsExportKind.ExportEquals): { + const exportSymbol = symbol.exports!.get(exportEqualsSymbolName as ts.__String); + if (!exportSymbol) { + return inferenceError(`TS compiler could not find \`export=\` symbol.`); + } + const exportType = checker.getTypeOfSymbolAtLocation(exportSymbol, sourceFile); + return inferenceSuccess(exportType); + } + case (DtsExportKind.ES6Like): { + const exportType = checker.getTypeOfSymbolAtLocation(symbol, sourceFile); + return inferenceSuccess(exportType); + } + } + } + + return mergeErrors(symbolResult, exportKindResult); +} + +/** + * Returns the position of the default export, if it exists. + */ +function getDtsDefaultExport(sourceFile: ts.SourceFile, moduleType: InferenceResult): Position | undefined { + if (isError(moduleType)) { + const src = sourceFile.getFullText(sourceFile); + const exportDefault = src.indexOf("export default"); + if (exportDefault > -1 + && src.indexOf("export =") === -1 + && !/declare module ['"]/.test(src)) { + return { + start: exportDefault, + length: "export default".length, + }; + } + return undefined; + } + + const exportDefault = moduleType.result.getProperty("default"); + if (exportDefault?.declarations) { + return { + start: exportDefault.declarations[0].getStart(), + length: exportDefault.declarations[0].getWidth(), + }; + } + return undefined; +} + +const ignoredProperties = ["__esModule", "prototype", "default", "F", "G", "S", "P", "B", "W", "U", "R"]; + +function ignoreProperty(property: ts.Symbol): boolean { + const name = property.getName(); + return name.startsWith("_") || ignoredProperties.includes(name); +} + +/* + * Given the inferred type of the exports of both source and declaration, we make the following checks: + * 1. If source type has call or construct signatures, then declaration type should also have call or construct signatures. + * 2. If declaration type has call or construct signatures, then source type should also have call or construct signatures. + * 3. If source type has a property named "foo", then declaration type should also have a property named "foo". + * 4. If declaration type has a property named "foo", then source type should also have a property named "foo". + * Checks (2) and (4) don't work well in practice and should not be used for linting/verification purposes, because + * most of the times the error originates because the inferred type of the JavaScript source has missing information. + * Those checks are useful for finding examples where JavaScript type inference could be improved. + */ +function exportTypesCompatibility( + name: string, + sourceType: InferenceResult, + dtsType: InferenceResult, + dtsExportKind: InferenceResult): InferenceResult { + if (isError(sourceType)) { + return inferenceError("Could not get type of exports of source module."); + } + if (isError(dtsType)) { + return inferenceError("Could not get type of exports of declaration module."); + } + if (isBadType(sourceType.result)) { + return inferenceError("Could not infer meaningful type of exports of source module."); + } + if (isBadType(dtsType.result)) { + return inferenceError("Could not infer meaningful type of exports of declaration module."); + } + + const errors: MissingExport[] = []; + if (hasSignatures(sourceType.result) && !hasSignatures(dtsType.result)) { + if (isSuccess(dtsExportKind) && dtsExportKind.result === DtsExportKind.ExportEquals) { + errors.push({ + kind: ErrorKind.JsSignatureNotInDts, + message: `The declaration doesn't match the JavaScript module '${name}'. Reason: +The JavaScript module can be called or constructed, but the declaration module cannot.`, + }); + } + else { + errors.push({ + kind: ErrorKind.JsSignatureNotInDts, + message: `The declaration doesn't match the JavaScript module '${name}'. Reason: +The JavaScript module can be called or constructed, but the declaration module cannot. + +The most common way to resolve this error is to use 'export =' syntax. +To learn more about 'export =' syntax, see ${exportEqualsLink}.`, + }); + } + } + + if (hasSignatures(dtsType.result) && !hasSignatures(sourceType.result)) { + errors.push({ + kind: ErrorKind.DtsSignatureNotInJs, + message: `The declaration doesn't match the JavaScript module '${name}'. Reason: +The declaration module can be called or constructed, but the JavaScript module cannot.`, + }); + } + + const sourceProperties = sourceType.result.getProperties(); + const dtsProperties = dtsType.result.getProperties(); + for (const sourceProperty of sourceProperties) { + // TODO: check `prototype` properties. + if (ignoreProperty(sourceProperty)) continue; + if (!dtsProperties.find(s => s.getName() === sourceProperty.getName())) { + errors.push({ + kind: ErrorKind.JsPropertyNotInDts, + message: `The declaration doesn't match the JavaScript module '${name}'. Reason: +The JavaScript module exports a property named '${sourceProperty.getName()}', which is missing from the declaration module.` + }); + } + } + + for (const dtsProperty of dtsProperties) { + // TODO: check `prototype` properties. + if (ignoreProperty(dtsProperty)) continue; + if (!sourceProperties.find(s => s.getName() === dtsProperty.getName())) { + const error: MissingExport = { + kind: ErrorKind.DtsPropertyNotInJs, + message: `The declaration doesn't match the JavaScript module '${name}'. Reason: +The declaration module exports a property named '${dtsProperty.getName()}', which is missing from the JavaScript module.` + }; + const declaration = dtsProperty.declarations && dtsProperty.declarations.length > 0 ? + dtsProperty.declarations[0] : undefined; + if (declaration) { + error.position = { + start: declaration.getStart(), + length: declaration.getWidth(), + }; + } + errors.push(error); + } + } + + return inferenceSuccess(errors); +} + +function isBadType(type: ts.Type): boolean { + return !!(type.getFlags() + & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Undefined | ts.TypeFlags.Null)); +} + +function isExportEquals(node: ts.Node): boolean { + return ts.isExportAssignment(node) && !!node.isExportEquals; +} + +function isExportConstruct(node: ts.Node): boolean { + return ts.isExportAssignment(node) + || ts.isExportDeclaration(node) + || hasExportModifier(node); +} + +function hasExportModifier(node: ts.Node): boolean { + if (node.modifiers) { + return node.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword); + } + return false; +} + +function matches(srcFile: ts.SourceFile, predicate: (n: ts.Node) => boolean): boolean { + function matchesNode(node: ts.Node): boolean { + if (predicate(node)) return true; + const children = node.getChildren(srcFile); + for (const child of children) { + if (matchesNode(child)) return true; + } + return false; + } + return matchesNode(srcFile); +} + +function isExistingSquatter(name: string) { + return name === "atom" || + name === "ember__string" || + name === "fancybox" || + name === "jsqrcode" || + name === "node" || + name === "geojson" || + name === "titanium"; +} + +function isRealExportDefault(name: string) { + return name.indexOf("react-native") > -1 || + name === "ember-feature-flags" || + name === "material-ui-datatables"; +} + +/** + * Converts a package name from the name used in DT repository to the name used in npm. + * @param baseName DT name of a package + */ +export function dtToNpmName(baseName: string) { + if (/__/.test(baseName)) { + return "@" + baseName.replace("__", "/"); + } + return baseName; +} + +/** + * @param error case-insensitive name of the error + */ +export function parseExportErrorKind(error: string): ExportErrorKind | undefined { + error = error.toLowerCase(); + switch (error) { + case "needsexportequals": + return ErrorKind.NeedsExportEquals; + case "nodefaultexport": + return ErrorKind.NoDefaultExport; + case "jspropertynotindts": + return ErrorKind.JsPropertyNotInDts; + case "dtspropertynotinjs": + return ErrorKind.DtsPropertyNotInJs; + case "jssignaturenotindts": + return ErrorKind.JsSignatureNotInDts; + case "dtssignaturenotinjs": + return ErrorKind.DtsSignatureNotInJs; + } + return undefined; +} + +export interface CriticError { + kind: ErrorKind, + message: string, + position?: Position, +} + +interface NpmError extends CriticError { + kind: ErrorKind.NoMatchingNpmPackage | ErrorKind.NoMatchingNpmVersion, +} + +interface NonNpmError extends CriticError { + kind: ErrorKind.NonNpmHasMatchingPackage, +} + +interface ExportEqualsError extends CriticError { + kind: ErrorKind.NeedsExportEquals, +} + +interface DefaultExportError extends CriticError { + kind: ErrorKind.NoDefaultExport, + position: Position, +} + +interface MissingExport extends CriticError { + kind: ErrorKind.JsPropertyNotInDts| ErrorKind.DtsPropertyNotInJs | ErrorKind.JsSignatureNotInDts | ErrorKind.DtsSignatureNotInJs, +} + +interface Position { + start: number, + length: number, +} + +interface ExportsDiagnostics { + jsExportKind: InferenceResult, + jsExportType: InferenceResult, + dtsExportKind: InferenceResult, + dtsExportType: InferenceResult, + errors: ExportError[], +} + +type ExportError = ExportEqualsError | DefaultExportError | MissingExport; + +interface JsExportsInfo { + exportKind: InferenceResult, + exportType: InferenceResult, + exportEquals: InferenceResult, + exportsDefault: boolean, +} + +enum JsExportKind { + CommonJs = "CommonJs", + ES6 = "ES6", +}; + +interface ExportEqualsDiagnostics { + judgement: ExportEqualsJudgement; + reason: string; +} + +enum ExportEqualsJudgement { + Required = "Required", + NotRequired = "Not required", +} + +enum DtsExportKind { + ExportEquals = "export =", + ES6Like = "ES6-like", +} + +interface DtsExportDiagnostics { + exportKind: InferenceResult, + exportType: InferenceResult, + defaultExport?: Position, +} + +type NpmInfo = NonNpm | Npm; + +interface NonNpm { + isNpm: false +} + +interface Npm { + isNpm: true, + versions: string[], + tags: { [tag: string]: string | undefined }, +} + +type InferenceResult = InferenceError | InferenceSuccess; + +enum InferenceResultKind { + Error, + Success, +} + +interface InferenceError { + kind: InferenceResultKind.Error; + reason?: string, +} + +interface InferenceSuccess { + kind: InferenceResultKind.Success; + result: T; +} + +function inferenceError(reason?: string): InferenceError { + return { kind: InferenceResultKind.Error, reason }; +} + +function inferenceSuccess(result: T): InferenceSuccess { + return { kind: InferenceResultKind.Success, result }; +} + +function isSuccess(inference: InferenceResult): inference is InferenceSuccess { + return inference.kind === InferenceResultKind.Success; +} + +function isError(inference: InferenceResult): inference is InferenceError { + return inference.kind === InferenceResultKind.Error; +} + +function mergeErrors(...results: (InferenceResult | string)[]): InferenceError { + const reasons: string[] = []; + for (const result of results) { + if (typeof result === "string") { + reasons.push(result); + } + else if (isError(result) && result.reason) { + reasons.push(result.reason); + } + } + return inferenceError(reasons.join(" ")); +} + +if (!module.parent) { + main(); +} diff --git a/packages/dts-critic/jest.config.js b/packages/dts-critic/jest.config.js new file mode 100644 index 00000000..9c361ae8 --- /dev/null +++ b/packages/dts-critic/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + rootDir: "dist", + moduleFileExtensions: [ + "js", + "jsx", + "json", + "node" + ], +}; diff --git a/packages/dts-critic/package.json b/packages/dts-critic/package.json new file mode 100644 index 00000000..2b0856be --- /dev/null +++ b/packages/dts-critic/package.json @@ -0,0 +1,56 @@ +{ + "name": "@definitelytyped/dts-critic", + "version": "0.0.94", + "author": "Nathan Shively-Sanders", + "description": "Checks a new .d.ts against the Javascript source and tells you what problems it has", + "dependencies": { + "@definitelytyped/header-parser": "latest", + "command-exists": "^1.2.8", + "rimraf": "^3.0.2", + "semver": "^6.2.0", + "tmp": "^0.2.1", + "yargs": "^15.3.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "devDependencies": { + "@types/command-exists": "^1.2.0", + "@types/jest": "^24.0.0", + "@types/node": "~10.17.0", + "@types/rimraf": "^3.0.0", + "@types/semver": "^6.0.1", + "@types/strip-json-comments": "0.0.30", + "@types/tmp": "^0.2.0", + "@types/yargs": "^12.0.8", + "strip-json-comments": "^2.0.1", + "typescript": "*" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "test": "npm run build && jest", + "build": "tsc", + "dt": "node dist/dt.js", + "prepublishOnly": "npm run build && npm run test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/microsoft/DefinitelyTyped-tools.git" + }, + "keywords": [ + "definitely", + "typed", + "refresh", + "npm", + "tag" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/microsoft/DefinitelyTyped-tools/issues" + }, + "homepage": "https://github.com/microsoft/DefinitelyTyped-tools#readme", + "engines": { + "node": ">=10.17.0" + } +} diff --git a/packages/dts-critic/testsource/dts-critic.d.ts b/packages/dts-critic/testsource/dts-critic.d.ts new file mode 100644 index 00000000..1698f060 --- /dev/null +++ b/packages/dts-critic/testsource/dts-critic.d.ts @@ -0,0 +1,8 @@ +// Type definitions for package dts-critic 2.0 +// Project: https://github.com/microsoft/TypeScript +// Definitions by: TypeScript Bot +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare function _default(): void; + +export = _default; \ No newline at end of file diff --git a/packages/dts-critic/testsource/dts-critic.js b/packages/dts-critic/testsource/dts-critic.js new file mode 100644 index 00000000..c3b42bc5 --- /dev/null +++ b/packages/dts-critic/testsource/dts-critic.js @@ -0,0 +1 @@ +module.exports = function() {}; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingDefault.d.ts b/packages/dts-critic/testsource/missingDefault.d.ts new file mode 100644 index 00000000..08a13903 --- /dev/null +++ b/packages/dts-critic/testsource/missingDefault.d.ts @@ -0,0 +1 @@ +export default function(): void; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingDefault.js b/packages/dts-critic/testsource/missingDefault.js new file mode 100644 index 00000000..c3b42bc5 --- /dev/null +++ b/packages/dts-critic/testsource/missingDefault.js @@ -0,0 +1 @@ +module.exports = function() {}; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingDtsProperty.d.ts b/packages/dts-critic/testsource/missingDtsProperty.d.ts new file mode 100644 index 00000000..cdff3a14 --- /dev/null +++ b/packages/dts-critic/testsource/missingDtsProperty.d.ts @@ -0,0 +1,3 @@ +export const a: () => void; +export const b: number; +export const foo: string; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingDtsProperty.js b/packages/dts-critic/testsource/missingDtsProperty.js new file mode 100644 index 00000000..17d4ebe9 --- /dev/null +++ b/packages/dts-critic/testsource/missingDtsProperty.js @@ -0,0 +1,4 @@ +module.exports = { + a: () => {}, + b: 0, +}; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingDtsSignature.d.ts b/packages/dts-critic/testsource/missingDtsSignature.d.ts new file mode 100644 index 00000000..9f1cff54 --- /dev/null +++ b/packages/dts-critic/testsource/missingDtsSignature.d.ts @@ -0,0 +1,7 @@ +interface Exports { + (): void, + foo: () => {}, +} + +declare const exp: Exports; +export = exp; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingDtsSignature.js b/packages/dts-critic/testsource/missingDtsSignature.js new file mode 100644 index 00000000..89162127 --- /dev/null +++ b/packages/dts-critic/testsource/missingDtsSignature.js @@ -0,0 +1,3 @@ +module.exports = { + foo: () => {}, +}; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingExportEquals.d.ts b/packages/dts-critic/testsource/missingExportEquals.d.ts new file mode 100644 index 00000000..422fdbcb --- /dev/null +++ b/packages/dts-critic/testsource/missingExportEquals.d.ts @@ -0,0 +1 @@ +export function foo(a: number): number; diff --git a/packages/dts-critic/testsource/missingExportEquals.js b/packages/dts-critic/testsource/missingExportEquals.js new file mode 100644 index 00000000..f2144b12 --- /dev/null +++ b/packages/dts-critic/testsource/missingExportEquals.js @@ -0,0 +1,5 @@ +function foo(a) { + return a; +} + +module.exports = foo; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingJsProperty.d.ts b/packages/dts-critic/testsource/missingJsProperty.d.ts new file mode 100644 index 00000000..00c1c37d --- /dev/null +++ b/packages/dts-critic/testsource/missingJsProperty.d.ts @@ -0,0 +1,2 @@ +export const a: () => void; +export const b: number; diff --git a/packages/dts-critic/testsource/missingJsProperty.js b/packages/dts-critic/testsource/missingJsProperty.js new file mode 100644 index 00000000..52045ee4 --- /dev/null +++ b/packages/dts-critic/testsource/missingJsProperty.js @@ -0,0 +1,5 @@ +module.exports = { + a: () => {}, + b: 0, + foo: "missing", +}; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingJsSignatureExportEquals.d.ts b/packages/dts-critic/testsource/missingJsSignatureExportEquals.d.ts new file mode 100644 index 00000000..04e7b19b --- /dev/null +++ b/packages/dts-critic/testsource/missingJsSignatureExportEquals.d.ts @@ -0,0 +1,6 @@ +interface Foo { + bar: () => void, +} +declare const foo: Foo; + +export = foo; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingJsSignatureExportEquals.js b/packages/dts-critic/testsource/missingJsSignatureExportEquals.js new file mode 100644 index 00000000..ffdf5465 --- /dev/null +++ b/packages/dts-critic/testsource/missingJsSignatureExportEquals.js @@ -0,0 +1,3 @@ +module.exports = class Foo { + bar() {} +}; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingJsSignatureNoExportEquals.d.ts b/packages/dts-critic/testsource/missingJsSignatureNoExportEquals.d.ts new file mode 100644 index 00000000..08a13903 --- /dev/null +++ b/packages/dts-critic/testsource/missingJsSignatureNoExportEquals.d.ts @@ -0,0 +1 @@ +export default function(): void; \ No newline at end of file diff --git a/packages/dts-critic/testsource/missingJsSignatureNoExportEquals.js b/packages/dts-critic/testsource/missingJsSignatureNoExportEquals.js new file mode 100644 index 00000000..06b94622 --- /dev/null +++ b/packages/dts-critic/testsource/missingJsSignatureNoExportEquals.js @@ -0,0 +1 @@ +module.exports = () => {}; \ No newline at end of file diff --git a/packages/dts-critic/testsource/noErrors.d.ts b/packages/dts-critic/testsource/noErrors.d.ts new file mode 100644 index 00000000..75eb55b4 --- /dev/null +++ b/packages/dts-critic/testsource/noErrors.d.ts @@ -0,0 +1,2 @@ +export const a: number; +export const b: string; \ No newline at end of file diff --git a/packages/dts-critic/testsource/noErrors.js b/packages/dts-critic/testsource/noErrors.js new file mode 100644 index 00000000..f597a1c0 --- /dev/null +++ b/packages/dts-critic/testsource/noErrors.js @@ -0,0 +1,2 @@ +exports.a = 42; +exports.b = "forty-two"; \ No newline at end of file diff --git a/packages/dts-critic/testsource/parseltongue.d.ts b/packages/dts-critic/testsource/parseltongue.d.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/dts-critic/testsource/tslib.d.ts b/packages/dts-critic/testsource/tslib.d.ts new file mode 100644 index 00000000..9f5a0f45 --- /dev/null +++ b/packages/dts-critic/testsource/tslib.d.ts @@ -0,0 +1,4 @@ +// Type definitions for non-npm package tslib +// Project: https://github.com/microsoft/TypeScript +// Definitions by: TypeScript Bot +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped \ No newline at end of file diff --git a/packages/dts-critic/testsource/typescript.d.ts b/packages/dts-critic/testsource/typescript.d.ts new file mode 100644 index 00000000..0e5d844c --- /dev/null +++ b/packages/dts-critic/testsource/typescript.d.ts @@ -0,0 +1,4 @@ +// Type definitions for typescript 1200000.5 +// Project: https://github.com/microsoft/TypeScript +// Definitions by: TypeScript Bot +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped \ No newline at end of file diff --git a/packages/dts-critic/testsource/webpackPropertyNames.d.ts b/packages/dts-critic/testsource/webpackPropertyNames.d.ts new file mode 100644 index 00000000..a12fe6df --- /dev/null +++ b/packages/dts-critic/testsource/webpackPropertyNames.d.ts @@ -0,0 +1 @@ +export var normal: string; diff --git a/packages/dts-critic/testsource/webpackPropertyNames.js b/packages/dts-critic/testsource/webpackPropertyNames.js new file mode 100644 index 00000000..bdd0a848 --- /dev/null +++ b/packages/dts-critic/testsource/webpackPropertyNames.js @@ -0,0 +1,12 @@ +var $export = {}; +// type bitmap +$export.F = 1; // forced +$export.G = 2; // global +$export.S = 4; // static +$export.P = 8; // proto +$export.B = 16; // bind +$export.W = 32; // wrap +$export.U = 64; // safe +$export.R = 128; // real proto method for `library` +$export.normal = "hi"; +module.exports = $export; diff --git a/packages/dts-critic/tsconfig.json b/packages/dts-critic/tsconfig.json new file mode 100644 index 00000000..6504178c --- /dev/null +++ b/packages/dts-critic/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "target": "es2019", + "module": "commonjs", + "resolveJsonModule": true, + "strict": true, + "sourceMap": true, + "outDir": "dist", + "declaration": true, + "esModuleInterop": true, + "noImplicitReturns": true, + }, + "exclude": [ + "dist/*", + "sources/*", + "testsource/*", + ] +} diff --git a/packages/dtslint-runner/package.json b/packages/dtslint-runner/package.json index 7ca8b60e..ee11dcdf 100644 --- a/packages/dtslint-runner/package.json +++ b/packages/dtslint-runner/package.json @@ -30,9 +30,6 @@ "devDependencies": { "@types/fs-extra": "^8.1.0", "@types/stats-lite": "^2.2.0", - "dtslint": "^4.0.6" - }, - "peerDependencies": { - "dtslint": "*" + "@definitelytyped/dtslint": "^0.0.94" } } diff --git a/packages/dtslint-runner/src/main.ts b/packages/dtslint-runner/src/main.ts index be136605..983e9d06 100644 --- a/packages/dtslint-runner/src/main.ts +++ b/packages/dtslint-runner/src/main.ts @@ -74,7 +74,7 @@ export async function runDTSLint({ expectOnly: expectOnly || !packageNames.includes(path) })), commandLineArgs: dtslintArgs, - workerFile: require.resolve("dtslint"), + workerFile: require.resolve("@definitelytyped/dtslint"), nProcesses, cwd: typesPath, crashRecovery: true, diff --git a/packages/dtslint/.github/workflows/CI.yml b/packages/dtslint/.github/workflows/CI.yml new file mode 100644 index 00000000..d80dfe48 --- /dev/null +++ b/packages/dtslint/.github/workflows/CI.yml @@ -0,0 +1,18 @@ +name: CI + +on: + pull_request + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + registry-url: "https://registry.npmjs.org" + + - run: "npm install" + - run: "npm test" + - run: "npm run lint" diff --git a/packages/dtslint/.github/workflows/Deploy.yml b/packages/dtslint/.github/workflows/Deploy.yml new file mode 100644 index 00000000..c502ab8f --- /dev/null +++ b/packages/dtslint/.github/workflows/Deploy.yml @@ -0,0 +1,29 @@ +name: Deploy to npm + +on: + push: + branches: + - main + - master + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + registry-url: "https://registry.npmjs.org" + + # Ensure everything is set up right + - run: "npm install" + - run: "npm test" + + - uses: orta/npm-should-deploy-action@main + id: check + + - run: "npm publish" + if: ${{ steps.check.outputs.deploy == 'true' }} + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/packages/dtslint/.gitignore b/packages/dtslint/.gitignore new file mode 100644 index 00000000..6e36a8e8 --- /dev/null +++ b/packages/dtslint/.gitignore @@ -0,0 +1,3 @@ +bin +node_modules +typescript-installs diff --git a/packages/dtslint/CODEOWNERS b/packages/dtslint/CODEOWNERS new file mode 100644 index 00000000..e273c459 --- /dev/null +++ b/packages/dtslint/CODEOWNERS @@ -0,0 +1 @@ +* @sandersn \ No newline at end of file diff --git a/packages/dtslint/LICENSE b/packages/dtslint/LICENSE new file mode 100644 index 00000000..21071075 --- /dev/null +++ b/packages/dtslint/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/packages/dtslint/README.md b/packages/dtslint/README.md new file mode 100644 index 00000000..6f1ebf53 --- /dev/null +++ b/packages/dtslint/README.md @@ -0,0 +1,177 @@ +`dtslint` tests a TypeScript declaration file for style and correctness. +It will install `typescript` and `tslint` for you, so this is the only tool you need to test a type definition. + +Lint rules new to dtslint are documented in the [docs](docs) directory. + +# Just looking for ExpectType and ExpectError? + +[Use tsd instead](https://github.com/SamVerschueren/tsd). + +# Setup + +If you are working on DefinitelyTyped, read the [DefinitelyTyped README](https://github.com/DefinitelyTyped/DefinitelyTyped#readme). + +If you are writing the library in TypeScript, don't use `dtslint`. +Use [`--declaration`](http://www.typescriptlang.org/docs/handbook/compiler-options.html) to have type definitions generated for you. + +If you are a library author, read below. + + +## Add types for a library (not on DefinitelyTyped) + +[`dts-gen`](https://github.com/Microsoft/dts-gen#readme) may help, but is not required. + +Create a `types` directory. (Name is arbitrary.) +Add `"types": "types"` to your `package.json`. +Read more on bundling types [here](http://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html). + + +#### `types/index.d.ts` + +Only `index.d.ts` needs to be published to NPM. Other files are just for testing. +Write your type definitions here. +Refer to the [handbook](http://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html) or `dts-gen`'s templates for how to do this. + + +#### `types/tsconfig.json` + +```json5 +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["es6"], + "noImplicitAny": true, + "noImplicitThis": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true, + + // If the library is an external module (uses `export`), this allows your test file to import "mylib" instead of "./index". + // If the library is global (cannot be imported via `import` or `require`), leave this out. + "baseUrl": ".", + "paths": { "mylib": ["."] } + } +} +``` + +You may extend `"lib"` to, for example, `["es6", "dom"]` if you need those typings. +You may also have to add `"target": "es6"` if using certain language features. + + +#### `types/tslint.json` + +If you are using the default rules, this is optional. + +If present, this will override `dtslint`'s [default](https://github.com/Microsoft/dtslint/blob/master/dtslint.json) settings. +You can specify new lint [rules](https://palantir.github.io/tslint/rules/), or disable some. An example: + +```json5 +{ + "extends": "dtslint/dtslint.json", // Or "dtslint/dt.json" if on DefinitelyTyped + "rules": { + "semicolon": false, + "indent": [true, "tabs"] + } +} +``` + + +#### `types/test.ts` + +You can have any number of test files you want, with any names. See below on what to put in them. + + + +## Write tests + +A test file should be a piece of sample code that tests using the library. Tests are type-checked, but not run. +To assert that an expression is of a given type, use `$ExpectType`. +To assert that an expression causes a compile error, use `$ExpectError`. +(Assertions will be checked by the `expect` lint rule.) + +```ts +import { f } from "my-lib"; // f is(n: number) => void + +// $ExpectType void +f(1); + +// Can also write the assertion on the same line. +f(2); // $ExpectType void + +// $ExpectError +f("one"); +``` + + +## Specify a TypeScript version + +Normally packages will be tested using TypeScript 2.0. +To use a newer version, specify it by including a comment like so: + +```ts +// Minimum TypeScript Version: 2.1 +``` + +For DefinitelyTyped packages, this should go just under the header (on line 5). +For bundled typings, this can go on any line (but should be near the top). + + +## Run tests + +- `npm install --save-dev dtslint` +- Add to your `package.json` `scripts`: `"dtslint": "dtslint types"` +- `npm run dtslint` + +### Options + +- `--localTs` + +Use your locally installed version of TypeScript. + +```sh +dtslint --localTs node_modules/typescript/lib types +``` +- `--expectOnly` + +Disable all the lint rules except the one that checks for type correctness. + +```sh +dtslint --expectOnly types +``` + + +# Contributing + +## Build + +```sh +npm link . # Global 'dts-lint' should now refer to this. +npm run watch +``` + +## Test + +Use `npm run test` to run all tests. +To run a single test: `node node_modules/tslint/bin/tslint --rules-dir bin/rules --test test/expect`. + +## Publish + +1. Change the version in the `package.json` +2. Push to master + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## FAQ +I'm getting an error about a missing typescript install. +``` +Error: Cannot find module '/node_modules/dtslint/typescript-installs/3.1/node_modules/typescript` +``` +Your dependencies may be out of date. +[@definitelytyped/typescript-versions](https://github.com/microsoft/DefinitelyTyped-tools/tree/master/packages/typescript-versions) is the package that contains the list of TypeScript versions to install. + +Alternatively this error can be caused by concurrent dtslint invocations trampling each other's TypeScript installations, especially in the context of continuous integration, if dtslint is installed from scratch in each run. +If for example you use [Lerna](https://github.com/lerna/lerna/tree/main/commands/run#readme), try running dtslint with [`lerna --concurrency 1 run ...`](https://github.com/lerna/lerna/tree/main/core/global-options#--concurrency). diff --git a/packages/dtslint/docs/dt-header.md b/packages/dtslint/docs/dt-header.md new file mode 100644 index 00000000..29ef89c8 --- /dev/null +++ b/packages/dtslint/docs/dt-header.md @@ -0,0 +1,88 @@ +# dt-header + +(This rule is specific to DefinitelyTyped.) + +Checks the format of DefinitelyTyped header comments. + +--- + +**Bad**: + +```ts +// Type definitions for foo v1.2.3 +``` + +* Don't include `v` +* Don't include a patch version + +**Good**: + +```ts +// Type definitions for foo 1.2 +``` + +--- + +**Bad**: + +```ts +// Definitions by: My Name +``` + +**Good**: + +```ts +// Definitions by: My Name +``` + +* Prefer a GitHub username, not a personal web site. + +--- + +**Bad**: + +`foo/index.d.ts`: + +```ts +// Type definitions for abs 1.2 +// Project: https://github.com/foo/foo +// Definitions by: My Name +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +export { f } from "./subModule"; +``` + +`foo/subModule.d.ts`: + +```ts +// Type definitions for abs 1.2 +// Project: https://github.com/foo/foo +// Definitions by: My Name +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +export function f(): number; +``` + +`foo/ts3.1/index.d.ts`: +```ts +// Type definitions for abs 1.2 +// Project: https://github.com/foo/foo +// Definitions by: My Name +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +export function f(): number; +``` + + +**Good**: + +`foo/index.d.ts`: Same + +`foo/subModule.d.ts`: +```ts +export function f(): number; +``` + +`foo/ts3.1/index.d.ts`: +```ts +export function f(): number; +``` + +Don't repeat the header -- only do it in the index of the root. diff --git a/packages/dtslint/docs/export-just-namespace.md b/packages/dtslint/docs/export-just-namespace.md new file mode 100644 index 00000000..7c72d4b2 --- /dev/null +++ b/packages/dtslint/docs/export-just-namespace.md @@ -0,0 +1,29 @@ +# export-just-namespace + +Declaring a namespace is unnecessary if that is the module's only content; just use ES6 export syntax instead. + +**Bad**: + +```ts +namespace MyLib { + export function f(): number; +} +export = MyLib; +``` + +**Good**: + +```ts +export function f(): number; +``` + +**Also good**: + +```ts +namespace MyLib { + export function f(): number; +} +function MyLib(): number; +export = MyLib; +``` + diff --git a/packages/dtslint/docs/no-any-union.md b/packages/dtslint/docs/no-any-union.md new file mode 100644 index 00000000..859e6d82 --- /dev/null +++ b/packages/dtslint/docs/no-any-union.md @@ -0,0 +1,27 @@ +# no-any-union + +Forbids to include `any` in a union. When `any` is used in a union type, the resulting type is still `any`. + +**Bad**: + +```ts +function f(x: string | any): void; +``` + +**Good**: + +```ts +function f(x: string): void; +``` + +Or: +```ts +function f(x: any): void; +``` + +Or: +```ts +function f(x: string | object): void; +``` + +While the `string` portion of this type annotation may _look_ useful, it in fact offers no additional typechecking over simply using `any`. diff --git a/packages/dtslint/docs/no-bad-reference.md b/packages/dtslint/docs/no-bad-reference.md new file mode 100644 index 00000000..69329bdd --- /dev/null +++ b/packages/dtslint/docs/no-bad-reference.md @@ -0,0 +1,28 @@ +# no-bad-reference + +(This rule is specific to DefinitelyTyped.) +Avoid using ``. + +**Bad**: + +```ts +/// +import * as foo from "foo"; +``` + +**Good**: + +If "foo" is written in external module style (see `no-single-declare-module`), the import alone should work thanks to [module resolution](http://www.typescriptlang.org/docs/handbook/module-resolution.html): + +```ts +// TypeScript will look for a definition for "foo" using module resolution +import * as foo from "foo"; +``` + +If not, use `` instead: + +```ts +/// +``` + +The only time `` should be necessary if for global (not module) libraries that are separated into multiple files; the index file must include references to the others to bring them into the compilation. diff --git a/packages/dtslint/docs/no-const-enum.md b/packages/dtslint/docs/no-const-enum.md new file mode 100644 index 00000000..3d75db52 --- /dev/null +++ b/packages/dtslint/docs/no-const-enum.md @@ -0,0 +1,16 @@ +# no-const-enum + +Avoid using `const enum`s. These can't be used by JavaScript users or by TypeScript users with [`--isolatedModules`](https://www.typescriptlang.org/docs/handbook/compiler-options.html) enabled. + +**Bad**: + +```ts +const enum Bit { Off, On } +export function f(b: Bit): void; +``` + +**Good**: + +```ts +export function f(b: 0 | 1): void; +``` diff --git a/packages/dtslint/docs/no-dead-reference.md b/packages/dtslint/docs/no-dead-reference.md new file mode 100644 index 00000000..7cd50353 --- /dev/null +++ b/packages/dtslint/docs/no-dead-reference.md @@ -0,0 +1,17 @@ +# no-dead-reference + +A `` comment should go at the top of a file -- otherwise it is just a normal comment. + +**Bad**: + +```ts +console.log("Hello world!"); +/// +``` + +**Good**: + +```ts +/// +console.log("Hello world!"); +``` diff --git a/packages/dtslint/docs/no-declare-current-package.md b/packages/dtslint/docs/no-declare-current-package.md new file mode 100644 index 00000000..0cbde592 --- /dev/null +++ b/packages/dtslint/docs/no-declare-current-package.md @@ -0,0 +1,35 @@ +# no-declare-current-package + +Avoid using `declare module`, and prefer to declare module contents in a file. + +**Bad**: + +```ts +// foo/index.d.ts +declare module "foo" { + export const x = 0; +} +``` + +**Good**: + +```ts +// foo/index.d.ts +export const x = 0; +``` + +**Bad**: + +```ts +// foo/index.d.ts +declare module "foo/bar" { + export const x = 0; +} +``` + +**Good**: + +```ts +// foo/bar.d.ts +export const x = 0; +``` diff --git a/packages/dtslint/docs/no-import-default-of-export-equals.md b/packages/dtslint/docs/no-import-default-of-export-equals.md new file mode 100644 index 00000000..b98c24c8 --- /dev/null +++ b/packages/dtslint/docs/no-import-default-of-export-equals.md @@ -0,0 +1,22 @@ +# no-import-default-of-export-equals + +Don't use a default import of a package that uses `export =`. +Users who do not have `--allowSyntheticDefaultExports` or `--esModuleInterop` will get different behavior. +This rule only applies to definition files -- for test files you can use a default import if you prefer. + +**Bad**: + +```ts +// foo/index.d.ts +declare interface I {} +export = I; + +// bar/index.d.ts +import I from "foo"; +``` + +**Good**: + +```ts +import I = require("foo"); +``` diff --git a/packages/dtslint/docs/no-outside-dependencies.md b/packages/dtslint/docs/no-outside-dependencies.md new file mode 100644 index 00000000..0d2780aa --- /dev/null +++ b/packages/dtslint/docs/no-outside-dependencies.md @@ -0,0 +1,23 @@ +# no-outside-dependencies + +Don't import from `DefinitelyTyped/node_modules`. + +**Bad**: + +```ts +import * as x from "x"; +// where 'x' is defined only in `DefinitelyTyped/node_modules` +``` + +**Good**: + +Add a `package.json`: + +```ts +{ + "private": true, + "dependencies": { + "x": "^1.2.3" + } +} +``` diff --git a/packages/dtslint/docs/no-padding.md b/packages/dtslint/docs/no-padding.md new file mode 100644 index 00000000..e4d6b448 --- /dev/null +++ b/packages/dtslint/docs/no-padding.md @@ -0,0 +1,33 @@ +# no-padding + +Avoid blank lines before opening tokens or after closing tokens. + +**Bad**: + +```ts +function f() { + + return [ + + g( + + 0 + + ) + + ]; + +} +``` + +**Good**: + +```ts +function f() { + return [ + g( + 0 + ) + ]; +} +``` diff --git a/packages/dtslint/docs/no-relative-import-in-test.md b/packages/dtslint/docs/no-relative-import-in-test.md new file mode 100644 index 00000000..4bfbdaa5 --- /dev/null +++ b/packages/dtslint/docs/no-relative-import-in-test.md @@ -0,0 +1,15 @@ +# no-relative-import-in-test + +A test file should not contain relative imports; it should use a global import of the library using [module resolution](http://www.typescriptlang.org/docs/handbook/module-resolution.html). + +**Bad**: + +```ts +import foo from "./index.d.ts"; +``` + +**Good**: + +```ts +import foo from "foo"; +``` diff --git a/packages/dtslint/docs/no-self-import.md b/packages/dtslint/docs/no-self-import.md new file mode 100644 index 00000000..327d152f --- /dev/null +++ b/packages/dtslint/docs/no-self-import.md @@ -0,0 +1,27 @@ +# no-self-import + +A package should not import components of itself using a globally-qualified name; it should use relative imports instead. + +**Bad**: + +```ts +import foo from "this-package/foo.d.ts"; +``` + +**Good**: + +```ts +import foo from "./foo.d.ts"; +``` + +**Bad**: + +```ts +import myself from "this-package"; +``` + +**Good**: + +```ts +import myself from "."; +``` diff --git a/packages/dtslint/docs/no-single-declare-module.md b/packages/dtslint/docs/no-single-declare-module.md new file mode 100644 index 00000000..61d60654 --- /dev/null +++ b/packages/dtslint/docs/no-single-declare-module.md @@ -0,0 +1,19 @@ +# no-single-declare-module + +`declare module` should typically be avoided. +Instead, the file itself should be used as the declaration for the module. +TypeScript uses [module resolution](http://www.typescriptlang.org/docs/handbook/module-resolution.html) to determine what files are associated with what modules. + +**Bad**: + +```ts +declare module "mylib" { + function foo(): number; +} +``` + +**Good**: + +```ts +export function foo(): number; +``` diff --git a/packages/dtslint/docs/no-single-element-tuple-type.md b/packages/dtslint/docs/no-single-element-tuple-type.md new file mode 100644 index 00000000..890219a7 --- /dev/null +++ b/packages/dtslint/docs/no-single-element-tuple-type.md @@ -0,0 +1,15 @@ +# no-single-element-tuple-type + +Some users mistakenly write `[T]` when then intend to write an array type `T[]`. + +**Bad**: + +```ts +export const x: [T]; +``` + +**Good**: + +```ts +export const x: T[]; +``` diff --git a/packages/dtslint/docs/no-unnecessary-generics.md b/packages/dtslint/docs/no-unnecessary-generics.md new file mode 100644 index 00000000..6507e8ed --- /dev/null +++ b/packages/dtslint/docs/no-unnecessary-generics.md @@ -0,0 +1,69 @@ +# no-unnecessary-generics + +Forbids a function to use a generic type parameter only once. +Generic type parameters allow you to relate the type of one thing to another; +if they are used only once, they can be replaced with their type constraint. + +**Bad**: + +```ts +function logAnything(x: T): void; +``` + +**Good**: + +```ts +function logAnything(x: any): void; +``` + +--- + +**Bad**: + +```ts +function useLogger(logger: T): void; +``` + +**Good**: + +```ts +function useLogger(logger: Logger): void; +``` + +--- + +**Bad**: + +```ts +function clear(array: T[]): void; +``` + +**Good**: + +```ts +function clear(array: any[]): void; +``` + +--- + +`getMeAT(): T`: +If a type parameter does not appear in the types of any parameters, you don't really have a generic function, you just have a disguised type assertion. +Prefer to use a real type assertion, e.g. `getMeAT() as number`. +Example where a type parameter is acceptable: `function id(value: T): T;`. +Example where it is not acceptable: `function parseJson(json: string): T;`. +Exception: `new Map()` is OK. + +**Bad**: + +```ts +function parse(): T; +const x = parse(); +``` + +**Good**: + + +```ts +function parse(): {}; +const x = parse() as number; +``` diff --git a/packages/dtslint/docs/no-useless-files.md b/packages/dtslint/docs/no-useless-files.md new file mode 100644 index 00000000..cdbea0b2 --- /dev/null +++ b/packages/dtslint/docs/no-useless-files.md @@ -0,0 +1,14 @@ +# no-useless-files + +Don't include empty files. + +**Bad**: + +```ts +``` + +**Good**: + +```ts +export function something(): void; +``` diff --git a/packages/dtslint/docs/npm-naming.md b/packages/dtslint/docs/npm-naming.md new file mode 100644 index 00000000..8660fbc5 --- /dev/null +++ b/packages/dtslint/docs/npm-naming.md @@ -0,0 +1,137 @@ +# npm-naming + +(This rule is specific to DefinitelyTyped.) + +## Name checks +In 'name-only' mode, checks that the name of the type package matches a source package on npm. + +--- + +**Bad**: + +```ts +// Type definitions for browser-only-package 1.2 +``` + +* If the package is really browser-only, you have to mark it with "non-npm package". +* If the package actually has a matching npm package, you must use that name. + +**Good**: + +```ts +// Type definitions for non-npm package browser-only-package 1.2 +``` + +--- + +**Bad**: + +```ts +// Type definitions for some-package 101.1 +``` + +* The version number in the header must actually exist on npm for the source package. + +**Good**: + +```ts +// Type definitions for some-package 10.1 +``` + +## Code checks + +In 'code' mode, in addition to the name checks, this rule also checks that the source JavaScript code matches the declaration file for npm packages. + +--- + +**Bad**: + +`foo/index.d.ts`: + +```ts +declare function f(): void; +export default f; +``` + +`foo/index.js`: + +```js +module.exports = function () { +}; +``` + +* A CommonJs module.exports assignment is not really an export default, and the d.ts should use the [`export =`](https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require) syntax. +* `export default` can only be used to export a CommonJs `module.exports =` when you have `esModuleInterop` turned on, which not everybody does. + +**Good**: + +`foo/index.d.ts`: + +```ts +declare function f(): void; +export = f; +``` + +--- + +**Bad**: + +`foo/index.d.ts`: + +```ts +export class C {} +``` + +`foo/index.js`: + +```js +module.exports = class C {} +``` + +* The CommonJs module is a class, which means it can be constructed, like this: +```js +var C = require('foo'); +var x = new C(); +``` +However, the way `class C` is exported in the d.ts file (using an export declaration) means it can only be used like this: +```ts +var foo = require('foo'); +var x = new foo.C(); +``` + +* The d.ts should use [`export =`](https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require) +syntax to match the CommonJs module behavior. + +**Good**: + +`foo/index.d.ts`: + +```ts +declare class C {} +export = C; +``` + +* If you need to use `export =` syntax as in the example above, and the source JavaScript also exports some properties, +you might need to use [*declaration merging*](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-namespaces-with-classes-functions-and-enums) in your d.ts. Example: + +**JavaScript**: + +`foo/index.js`: + +```js +function foo() {}; +foo.bar = "Exported property"; +module.exports = foo; // module.exports is a function, but it also has a property called `bar` +``` + +**Declaration**: + +`foo/index.d.ts`: + +```ts +declare function foo(): void; +declare namespace foo { + var bar: string; +} +export = foo; +``` diff --git a/packages/dtslint/docs/prefer-declare-function.md b/packages/dtslint/docs/prefer-declare-function.md new file mode 100644 index 00000000..7d16de88 --- /dev/null +++ b/packages/dtslint/docs/prefer-declare-function.md @@ -0,0 +1,15 @@ +# prefer-declare-function + +Prefer to declare a function using the `function` keyword instead of a variable of function type. + +**Bad**: + +```ts +export const f: () => number; +``` + +**Good**: + +```ts +export function f(): number; +``` diff --git a/packages/dtslint/docs/redundant-undefined.md b/packages/dtslint/docs/redundant-undefined.md new file mode 100644 index 00000000..f11b2096 --- /dev/null +++ b/packages/dtslint/docs/redundant-undefined.md @@ -0,0 +1,15 @@ +# redundant-undefined + +Avoid explicitly specifying `undefined` as a type for a parameter which is already optional. + +**Bad**: + +```ts +function f(s?: string | undefined): void {} +``` + +**Good**: + +```ts +function f(s?: string): void {} +``` diff --git a/packages/dtslint/docs/strict-export-declare-modifiers.md b/packages/dtslint/docs/strict-export-declare-modifiers.md new file mode 100644 index 00000000..efc6bd61 --- /dev/null +++ b/packages/dtslint/docs/strict-export-declare-modifiers.md @@ -0,0 +1,21 @@ +# strict-export-declare-modifiers + +Avoid adding the `declare` keyword unnecessarily. +Do add the `export` keyword unnecessarily, because sometimes it is necessary and we want to be consistent. + +**Bad**: + +```ts +export declare function f(): void; +declare function g(): void; +interface I {} +``` + + +**Good**: + +```ts +export function f(): void; +export function g(): void; +export interface I {} +``` diff --git a/packages/dtslint/docs/trim-file.md b/packages/dtslint/docs/trim-file.md new file mode 100644 index 00000000..51b0e516 --- /dev/null +++ b/packages/dtslint/docs/trim-file.md @@ -0,0 +1,17 @@ +# trim-file + +Don't include blank lines at the beginning or end of a file. + +**Bad**: + +```ts + +export function f(): number; + +``` + +**Good**: + +```ts +export function f(): number; +``` diff --git a/packages/dtslint/docs/void-return.md b/packages/dtslint/docs/void-return.md new file mode 100644 index 00000000..6b8dcfb7 --- /dev/null +++ b/packages/dtslint/docs/void-return.md @@ -0,0 +1,15 @@ +# void-return + +`void` should be used as a return type, but not as a parameter type. + +**Bad**: + +```ts +export function f(x: string | void): undefined; +``` + +**Good**: + +```ts +export function f(x: string | undefined): void; +``` diff --git a/packages/dtslint/dt.json b/packages/dtslint/dt.json new file mode 100644 index 00000000..a459af4b --- /dev/null +++ b/packages/dtslint/dt.json @@ -0,0 +1,15 @@ +{ + "extends": "./dtslint.json", + "rules": { + "dt-header": true, + "no-bad-reference": true, + "no-declare-current-package": true, + "no-self-import": true, + "no-outside-dependencies": true, + + "no-redundant-jsdoc": false, + "no-redundant-jsdoc-2": true, + + "npm-naming": [true, { "mode": "code" }] + } +} diff --git a/packages/dtslint/dtslint-expect-only.json b/packages/dtslint/dtslint-expect-only.json new file mode 100644 index 00000000..2a5f792c --- /dev/null +++ b/packages/dtslint/dtslint-expect-only.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": "./bin/rules", + "rules": { + "expect": true + } +} diff --git a/packages/dtslint/dtslint.json b/packages/dtslint/dtslint.json new file mode 100644 index 00000000..5bdb8202 --- /dev/null +++ b/packages/dtslint/dtslint.json @@ -0,0 +1,131 @@ +{ + "extends": "tslint:all", + "rulesDirectory": "./bin/rules", + "rules": { + // Custom rules + "expect": true, + "export-just-namespace": true, + "no-bad-reference": true, + "no-const-enum": true, + "no-dead-reference": true, + "no-import-default-of-export-equals": true, + "no-padding": true, + "redundant-undefined": true, + "no-relative-import-in-test": true, + "strict-export-declare-modifiers": true, + "no-any-union": true, + "no-single-declare-module": true, + "no-unnecessary-generics": true, + "no-useless-files": true, + "prefer-declare-function": true, + "trim-file": true, + "unified-signatures": true, + "void-return": true, + "npm-naming": true, + + "comment-format": [true, "check-space"], // But not check-uppercase or check-lowercase + "interface-name": [true, "never-prefix"], + "max-line-length": [true, 200], + "member-access": [true, "no-public"], + "no-consecutive-blank-lines": true, + "no-unnecessary-callback-wrapper": true, + "no-namespace": [true, "allow-declarations"], + "object-literal-key-quotes": [true, "as-needed"], + "one-line": [ + true, + "check-catch", + "check-finally", + "check-else", + "check-open-brace", + "check-whitespace" + ], + "one-variable-per-declaration": [true, "ignore-for-loop"], + "only-arrow-functions": [true, "allow-declarations", "allow-named-functions"], + "prefer-template": [true, "allow-single-concat"], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-module", + "check-separator", + "check-type", + "check-typecast" + ], + + // TODO? + "align": false, // TODO + "arrow-parens": false, + "arrow-return-shorthand": true, // TODO: "multiline" + "linebreak-style": false, // TODO + "no-void-expression": [true, "ignore-arrow-function-shorthand"], + "no-any": false, // TODO + "no-floating-promises": false, // TODO: https://github.com/palantir/tslint/issues/2879 + "no-import-side-effect": false, + "no-this-assignment": false, + "no-unbound-method": false, // TODO? + "no-unsafe-any": false, // TODO + "no-restricted-globals": false, + "number-literal-format": false, // TODO + "promise-function-async": false, + "restrict-plus-operands": false, // TODO + "return-undefined": false, // TODO + "switch-final-break": false, // TODO + "prefer-method-signature": false, // TODO? + + // Pretty sure we don't want these + "binary-expression-operand-order": false, + "class-name": false, + "completed-docs": false, + "curly": false, + "cyclomatic-complexity": false, + "deprecation": false, + "file-name-casing": false, + "forin": false, + "indent": false, + "match-default-export-name": false, + "max-classes-per-file": false, + "max-file-line-count": false, + "member-ordering": false, + "newline-before-return": false, + "newline-per-chained-call": false, + "no-bitwise": false, + "no-console": false, + "no-default-export": false, + "no-empty": false, + "no-implicit-dependencies": false, // See https://github.com/palantir/tslint/issues/3364 + "no-inferred-empty-object-type": false, + "no-magic-numbers": false, + "no-non-null-assertion": false, + "no-null-keyword": false, + "no-parameter-properties": false, + "no-parameter-reassignment": false, + "no-reference": false, // But see no-bad-reference + "no-require-imports": false, + "no-shadowed-variable": false, + "no-string-literal": false, + "no-submodule-imports": false, + "no-tautology-expression": false, + "no-unused-expression": false, + "no-unused-variable": false, + "no-use-before-declare": false, + "object-literal-sort-keys": false, + "ordered-imports": false, + "prefer-function-over-method": false, + "quotemark": false, + "strict-boolean-expressions": false, + "strict-type-predicates": false, + "switch-default": false, + "trailing-comma": false, + "triple-equals": [true, "allow-null-check"], + "typedef": false, + "type-literal-delimiter": false, + "variable-name": false, + "increment-decrement": false, + "unnecessary-constructor": false, + "unnecessary-else": false, + "no-angle-bracket-type-assertion": false, + "no-default-import": false, + "callable-types": false + } +} diff --git a/packages/dtslint/package.json b/packages/dtslint/package.json new file mode 100644 index 00000000..a51c292c --- /dev/null +++ b/packages/dtslint/package.json @@ -0,0 +1,57 @@ +{ + "name": "@definitelytyped/dtslint", + "version": "0.0.94", + "description": "Runs tests on TypeScript definition files", + "files": [ + "bin", + "dt.json", + "dtslint.json", + "dtslint-expect-only.json" + ], + "main": "bin", + "bin": "./bin/index.js", + "contributors": [ + "Nathan Shively-Sanders (https://github.com/sandersn)", + "Andy Hanson (https://github.com/andy-ms)", + "Dan Vanderkam (https://github.com/danvk)" + ], + "repository": { + "type": "git", + "url": "https://github.com/microsoft/DefinitelyTyped-tools.git" + }, + "scripts": { + "watch": "tsc --watch", + "build": "tsc", + "lint": "eslint --ext ts src", + "test": "npm run build && node test/test.js" + }, + "dependencies": { + "@definitelytyped/header-parser": "0.0.93", + "@definitelytyped/typescript-versions": "0.0.93", + "@definitelytyped/utils": "0.0.93", + "@definitelytyped/dts-critic": "0.0.94", + "fs-extra": "^6.0.1", + "json-stable-stringify": "^1.0.1", + "strip-json-comments": "^2.0.1", + "tslint": "5.14.0", + "yargs": "^15.1.0" + }, + "peerDependencies": { + "typescript": ">= 3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.7.0-dev || >= 3.8.0-dev || >= 3.9.0-dev || >= 4.0.0-dev" + }, + "devDependencies": { + "@types/fs-extra": "^5.0.2", + "@types/json-stable-stringify": "^1.0.32", + "@types/node": "14.0.x", + "@types/strip-json-comments": "^0.0.28", + "@types/yargs": "^15.0.3", + "@typescript-eslint/eslint-plugin": "^4.11.1", + "@typescript-eslint/parser": "^4.11.1", + "eslint": "^7.16.0", + "typescript": "next" + }, + "engines": { + "node": ">=10.0.0" + }, + "license": "MIT" +} diff --git a/packages/dtslint/src/.vscode/launch.json b/packages/dtslint/src/.vscode/launch.json new file mode 100644 index 00000000..62d7f5cf --- /dev/null +++ b/packages/dtslint/src/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Attach", + "port": 9229, + "sourceMaps": true + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${file}" + } + ] +} \ No newline at end of file diff --git a/packages/dtslint/src/checks.ts b/packages/dtslint/src/checks.ts new file mode 100644 index 00000000..886b1cb4 --- /dev/null +++ b/packages/dtslint/src/checks.ts @@ -0,0 +1,151 @@ +import { makeTypesVersionsForPackageJson } from "@definitelytyped/header-parser"; +import { TypeScriptVersion } from "@definitelytyped/typescript-versions"; +import assert = require("assert"); +import { pathExists } from "fs-extra"; +import { join as joinPaths } from "path"; + +import { getCompilerOptions, readJson } from "./util"; + +export async function checkPackageJson( + dirPath: string, + typesVersions: readonly TypeScriptVersion[], +): Promise { + const pkgJsonPath = joinPaths(dirPath, "package.json"); + const needsTypesVersions = typesVersions.length !== 0; + if (!await pathExists(pkgJsonPath)) { + if (needsTypesVersions) { + throw new Error(`${dirPath}: Must have 'package.json' for "typesVersions"`); + } + return; + } + + const pkgJson = await readJson(pkgJsonPath) as Record; + + if ((pkgJson as any).private !== true) { + throw new Error(`${pkgJsonPath} should set \`"private": true\``); + } + + if (needsTypesVersions) { + assert.strictEqual((pkgJson as any).types, "index", `"types" in '${pkgJsonPath}' should be "index".`); + const expected = makeTypesVersionsForPackageJson(typesVersions); + assert.deepEqual((pkgJson as any).typesVersions, expected, + `"typesVersions" in '${pkgJsonPath}' is not set right. Should be: ${JSON.stringify(expected, undefined, 4)}`); + } + + for (const key in pkgJson) { // tslint:disable-line forin + switch (key) { + case "private": + case "dependencies": + case "license": + case "imports": + case "exports": + case "type": + // "private"/"typesVersions"/"types" checked above, "dependencies" / "license" checked by types-publisher, + break; + case "typesVersions": + case "types": + if (!needsTypesVersions) { + throw new Error(`${pkgJsonPath} doesn't need to set "${key}" when no 'ts3.x' directories exist.`); + } + break; + default: + throw new Error(`${pkgJsonPath} should not include field ${key}`); + } + } +} + +export interface DefinitelyTypedInfo { + /** "../" or "../../" or "../../../". This should use '/' even on windows. */ + readonly relativeBaseUrl: string; +} +export async function checkTsconfig(dirPath: string, dt: DefinitelyTypedInfo | undefined): Promise { + const options = await getCompilerOptions(dirPath); + + if (dt) { + const { relativeBaseUrl } = dt; + + const mustHave = { + module: "commonjs", + noEmit: true, + forceConsistentCasingInFileNames: true, + baseUrl: relativeBaseUrl, + typeRoots: [relativeBaseUrl], + types: [], + }; + + for (const key of Object.getOwnPropertyNames(mustHave) as (keyof typeof mustHave)[]) { + const expected = mustHave[key]; + const actual = options[key]; + if (!deepEquals(expected, actual)) { + throw new Error(`Expected compilerOptions[${JSON.stringify(key)}] === ${JSON.stringify(expected)}`); + } + } + + for (const key in options) { // tslint:disable-line forin + switch (key) { + case "lib": + case "noImplicitAny": + case "noImplicitThis": + case "strict": + case "strictNullChecks": + case "noUncheckedIndexedAccess": + case "strictFunctionTypes": + case "esModuleInterop": + case "allowSyntheticDefaultImports": + // Allow any value + break; + case "target": + case "paths": + case "jsx": + case "jsxFactory": + case "experimentalDecorators": + case "noUnusedLocals": + case "noUnusedParameters": + // OK. "paths" checked further by types-publisher + break; + default: + if (!(key in mustHave)) { + throw new Error(`Unexpected compiler option ${key}`); + } + } + } + } + + if (!("lib" in options)) { + throw new Error('Must specify "lib", usually to `"lib": ["es6"]` or `"lib": ["es6", "dom"]`.'); + } + + if ("strict" in options) { + if (options.strict !== true) { + throw new Error('When "strict" is present, it must be set to `true`.'); + } + + for (const key of ["noImplicitAny", "noImplicitThis", "strictNullChecks", "strictFunctionTypes"]) { + if (key in options) { + throw new TypeError(`Expected "${key}" to not be set when "strict" is \`true\`.`); + } + } + } else { + for (const key of ["noImplicitAny", "noImplicitThis", "strictNullChecks", "strictFunctionTypes"]) { + if (!(key in options)) { + throw new Error(`Expected \`"${key}": true\` or \`"${key}": false\`.`); + } + } + } + + if (options.types && options.types.length) { + throw new Error( + 'Use `/// ` directives in source files and ensure ' + + 'that the "types" field in your tsconfig is an empty array.'); + } +} + +function deepEquals(expected: unknown, actual: unknown): boolean { + if (expected instanceof Array) { + return actual instanceof Array + && actual.length === expected.length + && expected.every((e, i) => deepEquals(e, actual[i])); + } else { + return expected === actual; + } +} diff --git a/packages/dtslint/src/index.ts b/packages/dtslint/src/index.ts new file mode 100644 index 00000000..2b57b51e --- /dev/null +++ b/packages/dtslint/src/index.ts @@ -0,0 +1,266 @@ +#!/usr/bin/env node + +import { parseTypeScriptVersionLine } from "@definitelytyped/header-parser"; +import { AllTypeScriptVersion, TypeScriptVersion } from "@definitelytyped/typescript-versions"; +import assert = require("assert"); +import { readdir, readFile, stat } from "fs-extra"; +import { basename, dirname, join as joinPaths, resolve } from "path"; + +import { cleanTypeScriptInstalls, installAllTypeScriptVersions, installTypeScriptNext } from "@definitelytyped/utils"; +import { checkPackageJson, checkTsconfig } from "./checks"; +import { checkTslintJson, lint, TsVersion } from "./lint"; +import { mapDefinedAsync, withoutPrefix } from "./util"; + +async function main(): Promise { + const args = process.argv.slice(2); + let dirPath = process.cwd(); + let onlyTestTsNext = false; + let expectOnly = false; + let shouldListen = false; + let lookingForTsLocal = false; + let tsLocal: string | undefined; + + for (const arg of args) { + if (lookingForTsLocal) { + if (arg.startsWith("--")) { + throw new Error("Looking for local path for TS, but got " + arg); + } + tsLocal = resolve(arg); + lookingForTsLocal = false; + continue; + } + switch (arg) { + case "--installAll": + console.log("Cleaning old installs and installing for all TypeScript versions..."); + console.log("Working..."); + await cleanTypeScriptInstalls(); + await installAllTypeScriptVersions(); + return; + case "--localTs": + lookingForTsLocal = true; + break; + case "--version": + console.log(require("../package.json").version); + return; + case "--expectOnly": + expectOnly = true; + break; + case "--onlyTestTsNext": + onlyTestTsNext = true; + break; + // Only for use by types-publisher. + // Listens for { path, onlyTestTsNext } messages and ouputs { path, status }. + case "--listen": + shouldListen = true; + break; + default: { + if (arg.startsWith("--")) { + console.error(`Unknown option '${arg}'`); + usage(); + process.exit(1); + } + + const path = arg.indexOf("@") === 0 && arg.indexOf("/") !== -1 + // we have a scoped module, e.g. @bla/foo + // which should be converted to bla__foo + ? arg.substr(1).replace("/", "__") + : arg; + dirPath = joinPaths(dirPath, path); + } + } + } + if (lookingForTsLocal) { + throw new Error("Path for --localTs was not provided."); + } + + if (shouldListen) { + listen(dirPath, tsLocal, onlyTestTsNext); + } else { + await installTypeScriptAsNeeded(tsLocal, onlyTestTsNext); + await runTests(dirPath, onlyTestTsNext, expectOnly, tsLocal); + } +} + +async function installTypeScriptAsNeeded(tsLocal: string | undefined, onlyTestTsNext: boolean): Promise { + if (tsLocal) return; + if (onlyTestTsNext) { + return installTypeScriptNext(); + } + return installAllTypeScriptVersions(); +} + +function usage(): void { + console.error("Usage: dtslint [--version] [--installAll] [--onlyTestTsNext] [--expectOnly] [--localTs path]"); + console.error("Args:"); + console.error(" --version Print version and exit."); + console.error(" --installAll Cleans and installs all TypeScript versions."); + console.error(" --expectOnly Run only the ExpectType lint rule."); + console.error(" --onlyTestTsNext Only run with `typescript@next`, not with the minimum version."); + console.error(" --localTs path Run with *path* as the latest version of TS."); + console.error(""); + console.error("onlyTestTsNext and localTs are (1) mutually exclusive and (2) test a single version of TS"); +} + +function listen(dirPath: string, tsLocal: string | undefined, alwaysOnlyTestTsNext: boolean): void { + // Don't await this here to ensure that messages sent during installation aren't dropped. + const installationPromise = installTypeScriptAsNeeded(tsLocal, alwaysOnlyTestTsNext); + process.on("message", async (message: unknown) => { + const { path, onlyTestTsNext, expectOnly } = message as { path: string, onlyTestTsNext: boolean, expectOnly?: boolean }; + + await installationPromise; + runTests(joinPaths(dirPath, path), onlyTestTsNext, !!expectOnly, tsLocal) + .catch(e => e.stack) + .then(maybeError => { + process.send!({ path, status: maybeError === undefined ? "OK" : maybeError }); + }) + .catch(e => console.error(e.stack)); + }); +} + +async function runTests( + dirPath: string, + onlyTestTsNext: boolean, + expectOnly: boolean, + tsLocal: string | undefined, +): Promise { + const isOlderVersion = /^v(0\.)?\d+$/.test(basename(dirPath)); + + const indexText = await readFile(joinPaths(dirPath, "index.d.ts"), "utf-8"); + // If this *is* on DefinitelyTyped, types-publisher will fail if it can't parse the header. + const dt = indexText.includes("// Type definitions for"); + if (dt) { + // Someone may have copied text from DefinitelyTyped to their type definition and included a header, + // so assert that we're really on DefinitelyTyped. + assertPathIsInDefinitelyTyped(dirPath); + assertPathIsNotBanned(dirPath); + } + + const typesVersions = await mapDefinedAsync(await readdir(dirPath), async name => { + if (name === "tsconfig.json" || name === "tslint.json" || name === "tsutils") { return undefined; } + const version = withoutPrefix(name, "ts"); + if (version === undefined || !(await stat(joinPaths(dirPath, name))).isDirectory()) { return undefined; } + + if (!TypeScriptVersion.isTypeScriptVersion(version)) { + throw new Error(`There is an entry named ${name}, but ${version} is not a valid TypeScript version.`); + } + if (!TypeScriptVersion.isRedirectable(version)) { + throw new Error(`At ${dirPath}/${name}: TypeScript version directories only available starting with ts3.1.`); + } + return version; + }); + + if (dt) { + await checkPackageJson(dirPath, typesVersions); + } + + const minVersion = maxVersion( + getMinimumTypeScriptVersionFromComment(indexText), + TypeScriptVersion.lowest) as TypeScriptVersion; + if (onlyTestTsNext || tsLocal) { + const tsVersion = tsLocal ? "local" : TypeScriptVersion.latest; + await testTypesVersion(dirPath, tsVersion, tsVersion, isOlderVersion, dt, expectOnly, tsLocal, /*isLatest*/ true); + } else { + // For example, typesVersions of [3.2, 3.5, 3.6] will have + // associated ts3.2, ts3.5, ts3.6 directories, for + // <=3.2, <=3.5, <=3.6 respectively; the root level is for 3.7 and above. + // so this code needs to generate ranges [lowest-3.2, 3.3-3.5, 3.6-3.6, 3.7-latest] + const lows = [TypeScriptVersion.lowest, ...typesVersions.map(next)]; + const his = [...typesVersions, TypeScriptVersion.latest]; + assert.strictEqual(lows.length, his.length); + for (let i = 0; i < lows.length; i++) { + const low = maxVersion(minVersion, lows[i]); + const hi = his[i]; + assert( + parseFloat(hi) >= parseFloat(low), + `'// Minimum TypeScript Version: ${minVersion}' in header skips ts${hi} folder.`); + const isLatest = hi === TypeScriptVersion.latest; + const versionPath = isLatest ? dirPath : joinPaths(dirPath, `ts${hi}`); + if (lows.length > 1) { + console.log("testing from", low, "to", hi, "in", versionPath); + } + await testTypesVersion(versionPath, low, hi, isOlderVersion, dt, expectOnly, undefined, isLatest); + } + } +} + +function maxVersion(v1: TypeScriptVersion | undefined, v2: TypeScriptVersion): TypeScriptVersion; +function maxVersion(v1: AllTypeScriptVersion | undefined, v2: AllTypeScriptVersion): AllTypeScriptVersion; +function maxVersion(v1: AllTypeScriptVersion | undefined, v2: AllTypeScriptVersion) { + if (!v1) return v2; + if (!v2) return v1; + if (parseFloat(v1) >= parseFloat(v2)) return v1; + return v2; +} + +function next(v: TypeScriptVersion): TypeScriptVersion { + const index = TypeScriptVersion.supported.indexOf(v); + assert.notStrictEqual(index, -1); + assert(index < TypeScriptVersion.supported.length); + return TypeScriptVersion.supported[index + 1]; +} + +async function testTypesVersion( + dirPath: string, + lowVersion: TsVersion, + hiVersion: TsVersion, + isOlderVersion: boolean, + dt: boolean, + expectOnly: boolean, + tsLocal: string | undefined, + isLatest: boolean, +): Promise { + await checkTslintJson(dirPath, dt); + await checkTsconfig(dirPath, dt + ? { relativeBaseUrl: ".." + (isOlderVersion ? "/.." : "") + (isLatest ? "" : "/..") + "/" } + : undefined); + const err = await lint(dirPath, lowVersion, hiVersion, isLatest, expectOnly, tsLocal); + if (err) { + throw new Error(err); + } +} + +function assertPathIsInDefinitelyTyped(dirPath: string): void { + const parent = dirname(dirPath); + const types = /^v\d+(\.\d+)?$/.test(basename(dirPath)) ? dirname(parent) : parent; + // TODO: It's not clear whether this assertion makes sense, and it's broken on Azure Pipelines + // Re-enable it later if it makes sense. + // const dt = dirname(types); + // if (basename(dt) !== "DefinitelyTyped" || basename(types) !== "types") { + if (basename(types) !== "types") { + throw new Error("Since this type definition includes a header (a comment starting with `// Type definitions for`), " + + "assumed this was a DefinitelyTyped package.\n" + + "But it is not in a `DefinitelyTyped/types/xxx` directory: " + + dirPath); + } +} + +function assertPathIsNotBanned(dirPath: string) { + const basedir = basename(dirPath); + if (/(^|\W)download($|\W)/.test(basedir) && + basedir !== "download" && + basedir !== "downloadjs" && + basedir !== "s3-download-stream") { + // Since npm won't release their banned-words list, we'll have to manually add to this list. + throw new Error(`${dirPath}: Contains the word 'download', which is banned by npm.`); + } +} + +function getMinimumTypeScriptVersionFromComment(text: string): AllTypeScriptVersion | undefined { + const match = text.match(/\/\/ (?:Minimum )?TypeScript Version: /); + if (!match) { + return undefined; + } + + let line = text.slice(match.index, text.indexOf("\n", match.index)); + if (line.endsWith("\r")) { + line = line.slice(0, line.length - 1); + } + return parseTypeScriptVersionLine(line); +} + +if (!module.parent) { + main().catch(err => { + console.error(err.stack); + process.exit(1); + }); +} diff --git a/packages/dtslint/src/lint.ts b/packages/dtslint/src/lint.ts new file mode 100644 index 00000000..2f7517b3 --- /dev/null +++ b/packages/dtslint/src/lint.ts @@ -0,0 +1,229 @@ +import { TypeScriptVersion } from "@definitelytyped/typescript-versions"; +import { typeScriptPath } from "@definitelytyped/utils"; +import assert = require("assert"); +import { pathExists } from "fs-extra"; +import { dirname, join as joinPaths, normalize } from "path"; +import { Configuration, ILinterOptions, Linter } from "tslint"; +import * as TsType from "typescript"; +type Configuration = typeof Configuration; +type IConfigurationFile = Configuration.IConfigurationFile; + +import { getProgram, Options as ExpectOptions } from "./rules/expectRule"; + +import { readJson, withoutPrefix } from "./util"; + +export async function lint( + dirPath: string, + minVersion: TsVersion, + maxVersion: TsVersion, + isLatest: boolean, + expectOnly: boolean, + tsLocal: string | undefined): Promise { + const tsconfigPath = joinPaths(dirPath, "tsconfig.json"); + const lintProgram = Linter.createProgram(tsconfigPath); + + for (const version of [maxVersion, minVersion]) { + const errors = testDependencies(version, dirPath, lintProgram, tsLocal); + if (errors) { return errors; } + } + + const lintOptions: ILinterOptions = { + fix: false, + formatter: "stylish", + }; + const linter = new Linter(lintOptions, lintProgram); + const configPath = expectOnly ? joinPaths(__dirname, "..", "dtslint-expect-only.json") : getConfigPath(dirPath); + const config = await getLintConfig(configPath, tsconfigPath, minVersion, maxVersion, tsLocal); + + for (const file of lintProgram.getSourceFiles()) { + if (lintProgram.isSourceFileDefaultLibrary(file)) { continue; } + + const { fileName, text } = file; + if (!fileName.includes("node_modules")) { + const err = testNoTsIgnore(text) || testNoTslintDisables(text); + if (err) { + const { pos, message } = err; + const place = file.getLineAndCharacterOfPosition(pos); + return `At ${fileName}:${JSON.stringify(place)}: ${message}`; + } + } + + // External dependencies should have been handled by `testDependencies`; + // typesVersions should be handled in a separate lint + if (!isExternalDependency(file, dirPath, lintProgram) && + (!isLatest || !isTypesVersionPath(fileName, dirPath))) { + linter.lint(fileName, text, config); + } + } + + const result = linter.getResult(); + return result.failures.length ? result.output : undefined; +} + +function testDependencies( + version: TsVersion, + dirPath: string, + lintProgram: TsType.Program, + tsLocal: string | undefined, +): string | undefined { + const tsconfigPath = joinPaths(dirPath, "tsconfig.json"); + assert(version !== "local" || tsLocal); + const ts: typeof TsType = require(typeScriptPath(version, tsLocal)); + const program = getProgram(tsconfigPath, ts, version, lintProgram); + const diagnostics = ts.getPreEmitDiagnostics(program).filter(d => !d.file || isExternalDependency(d.file, dirPath, program)); + if (!diagnostics.length) { return undefined; } + + const showDiags = ts.formatDiagnostics(diagnostics, { + getCanonicalFileName: f => f, + getCurrentDirectory: () => dirPath, + getNewLine: () => "\n", + }); + + const message = `Errors in typescript@${version} for external dependencies:\n${showDiags}`; + + // Add an edge-case for someone needing to `npm install` in react when they first edit a DT module which depends on it - #226 + const cannotFindDepsDiags = diagnostics.find(d => d.code === 2307 && d.messageText.toString().includes("Cannot find module")); + if (cannotFindDepsDiags && cannotFindDepsDiags.file) { + const path = cannotFindDepsDiags.file.fileName; + const typesFolder = dirname(path); + + return ` +A module look-up failed, this often occurs when you need to run \`npm install\` on a dependent module before you can lint. + +Before you debug, first try running: + + npm install --prefix ${typesFolder} + +Then re-run. Full error logs are below. + +${message}`; + } else { + return message; + } +} + +export function isExternalDependency(file: TsType.SourceFile, dirPath: string, program: TsType.Program): boolean { + return !startsWithDirectory(file.fileName, dirPath) || program.isSourceFileFromExternalLibrary(file); +} + +function normalizePath(file: string) { + // replaces '\' with '/' and forces all DOS drive letters to be upper-case + return normalize(file) + .replace(/\\/g, "/") + .replace(/^[a-z](?=:)/, c => c.toUpperCase()); +} + +function isTypesVersionPath(fileName: string, dirPath: string) { + const normalFileName = normalizePath(fileName); + const normalDirPath = normalizePath(dirPath); + const subdirPath = withoutPrefix(normalFileName, normalDirPath); + return subdirPath && /^\/ts\d+\.\d/.test(subdirPath); +} + +function startsWithDirectory(filePath: string, dirPath: string): boolean { + const normalFilePath = normalizePath(filePath); + const normalDirPath = normalizePath(dirPath).replace(/\/$/, ""); + return normalFilePath.startsWith(normalDirPath + "/") || normalFilePath.startsWith(normalDirPath + "\\"); +} + +interface Err { pos: number; message: string; } +function testNoTsIgnore(text: string): Err | undefined { + const tsIgnore = "ts-ignore"; + const pos = text.indexOf(tsIgnore); + return pos === -1 ? undefined : { pos, message: "'ts-ignore' is forbidden." }; +} +function testNoTslintDisables(text: string): Err | undefined { + const tslintDisable = "tslint:disable"; + let lastIndex = 0; + // eslint-disable-next-line no-constant-condition + while (true) { + const pos = text.indexOf(tslintDisable, lastIndex); + if (pos === -1) { + return undefined; + } + const end = pos + tslintDisable.length; + const nextChar = text.charAt(end); + if (nextChar !== "-" && nextChar !== ":") { + const message = "'tslint:disable' is forbidden. " + + "('tslint:disable:rulename', tslint:disable-line' and 'tslint:disable-next-line' are allowed.)"; + return { pos, message }; + } + lastIndex = end; + } +} + +export async function checkTslintJson(dirPath: string, dt: boolean): Promise { + const configPath = getConfigPath(dirPath); + const shouldExtend = `dtslint/${dt ? "dt" : "dtslint"}.json`; + const validateExtends = (extend: string | string[]) => + extend === shouldExtend || (!dt && Array.isArray(extend) && extend.some(val => val === shouldExtend)); + + if (!await pathExists(configPath)) { + if (dt) { + throw new Error( + `On DefinitelyTyped, must include \`tslint.json\` containing \`{ "extends": "${shouldExtend}" }\`.\n` + + "This was inferred as a DefinitelyTyped package because it contains a `// Type definitions for` header."); + } + return; + } + + const tslintJson = await readJson(configPath); + if (!validateExtends(tslintJson.extends)) { + throw new Error(`If 'tslint.json' is present, it should extend "${shouldExtend}"`); + } +} + +function getConfigPath(dirPath: string): string { + return joinPaths(dirPath, "tslint.json"); +} + +async function getLintConfig( + expectedConfigPath: string, + tsconfigPath: string, + minVersion: TsVersion, + maxVersion: TsVersion, + tsLocal: string | undefined, +): Promise { + const configExists = await pathExists(expectedConfigPath); + const configPath = configExists ? expectedConfigPath : joinPaths(__dirname, "..", "dtslint.json"); + // Second param to `findConfiguration` doesn't matter, since config path is provided. + const config = Configuration.findConfiguration(configPath, "").results; + if (!config) { + throw new Error(`Could not load config at ${configPath}`); + } + + const expectRule = config.rules.get("expect"); + if (!expectRule || expectRule.ruleSeverity !== "error") { + throw new Error("'expect' rule should be enabled, else compile errors are ignored"); + } + if (expectRule) { + const versionsToTest = + range(minVersion, maxVersion).map(versionName => ({ versionName, path: typeScriptPath(versionName, tsLocal) })); + const expectOptions: ExpectOptions = { tsconfigPath, versionsToTest }; + expectRule.ruleArguments = [expectOptions]; + } + return config; +} + +function range(minVersion: TsVersion, maxVersion: TsVersion): readonly TsVersion[] { + if (minVersion === "local") { + assert(maxVersion === "local"); + return ["local"]; + } + if (minVersion === TypeScriptVersion.latest) { + assert(maxVersion === TypeScriptVersion.latest); + return [TypeScriptVersion.latest]; + } + assert(maxVersion !== "local"); + + const minIdx = TypeScriptVersion.shipped.indexOf(minVersion); + assert(minIdx >= 0); + if (maxVersion === TypeScriptVersion.latest) { + return [...TypeScriptVersion.shipped.slice(minIdx), TypeScriptVersion.latest]; + } + const maxIdx = TypeScriptVersion.shipped.indexOf(maxVersion as TypeScriptVersion); + assert(maxIdx >= minIdx); + return TypeScriptVersion.shipped.slice(minIdx, maxIdx + 1); +} + +export type TsVersion = TypeScriptVersion | "local"; diff --git a/packages/dtslint/src/rules/dtHeaderRule.ts b/packages/dtslint/src/rules/dtHeaderRule.ts new file mode 100644 index 00000000..82d306ee --- /dev/null +++ b/packages/dtslint/src/rules/dtHeaderRule.ts @@ -0,0 +1,45 @@ +import { renderExpected, validate } from "@definitelytyped/header-parser"; +import * as Lint from "tslint"; +import * as ts from "typescript"; +import { failure, isMainFile } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "dt-header", + description: "Ensure consistency of DefinitelyTyped headers.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + const { text } = sourceFile; + const lookFor = (search: string, explanation: string) => { + const idx = text.indexOf(search); + if (idx !== -1) { + ctx.addFailureAt(idx, search.length, failure(Rule.metadata.ruleName, explanation)); + } + }; + if (!isMainFile(sourceFile.fileName, /*allowNested*/ true)) { + lookFor("// Type definitions for", "Header should only be in `index.d.ts` of the root."); + lookFor("// TypeScript Version", "TypeScript version should be specified under header in `index.d.ts`."); + lookFor("// Minimum TypeScript Version", "TypeScript version should be specified under header in `index.d.ts`."); + return; + } + + lookFor("// Definitions by: My Self", "Author name should be your name, not the default."); + const error = validate(text); + if (error) { + ctx.addFailureAt(error.index, 1, failure( + Rule.metadata.ruleName, + `Error parsing header. Expected: ${renderExpected(error.expected)}.`)); + } + // Don't recurse, we're done. +} diff --git a/packages/dtslint/src/rules/expectRule.ts b/packages/dtslint/src/rules/expectRule.ts new file mode 100644 index 00000000..39f8940d --- /dev/null +++ b/packages/dtslint/src/rules/expectRule.ts @@ -0,0 +1,402 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; +import os = require("os"); +import { basename, dirname, join, resolve as resolvePath } from "path"; +import * as Lint from "tslint"; +import * as TsType from "typescript"; +import { last } from "../util"; + +type Program = TsType.Program; +type SourceFile = TsType.SourceFile; + +// Based on https://github.com/danvk/typings-checker + +const cacheDir = join(os.homedir(), ".dts"); +const perfDir = join(os.homedir(), ".dts", "perf"); + +export class Rule extends Lint.Rules.TypedRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "expect", + description: "Asserts types with $ExpectType and presence of errors with $ExpectError.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + requiresTypeInfo: true, + }; + + static FAILURE_STRING_DUPLICATE_ASSERTION = "This line has 2 $ExpectType assertions."; + static FAILURE_STRING_ASSERTION_MISSING_NODE = "Can not match a node to this assertion."; + static FAILURE_STRING_EXPECTED_ERROR = "Expected an error on this line, but found none."; + + // TODO: If this naming convention is required by tslint, dump it when switching to eslint + // eslint-disable-next-line @typescript-eslint/naming-convention + static FAILURE_STRING(expectedVersion: string, expectedType: string, actualType: string): string { + return `TypeScript@${expectedVersion} expected type to be:\n ${expectedType}\ngot:\n ${actualType}`; + } + + applyWithProgram(sourceFile: SourceFile, lintProgram: Program): Lint.RuleFailure[] { + const options = this.ruleArguments[0] as Options | undefined; + if (!options) { + return this.applyWithFunction(sourceFile, ctx => + walk(ctx, lintProgram, TsType, "next", /*nextHigherVersion*/ undefined)); + } + + const { tsconfigPath, versionsToTest } = options; + + const getFailures = ( + { versionName, path }: VersionToTest, + nextHigherVersion: string | undefined, + writeOutput: boolean, + ) => { + const ts = require(path); + const program = getProgram(tsconfigPath, ts, versionName, lintProgram); + const failures = this.applyWithFunction(sourceFile, ctx => walk(ctx, program, ts, versionName, nextHigherVersion)); + if (writeOutput) { + const packageName = basename(dirname(tsconfigPath)); + if (!packageName.match(/v\d+/) && !packageName.match(/ts\d\.\d/)) { + const d = { + [packageName]: { + typeCount: (program as any).getTypeCount(), + memory: ts.sys.getMemoryUsage ? ts.sys.getMemoryUsage() : 0, + }, + }; + if (!existsSync(cacheDir)) { + mkdirSync(cacheDir); + } + if (!existsSync(perfDir)) { + mkdirSync(perfDir); + } + writeFileSync(join(perfDir, `${packageName}.json`), JSON.stringify(d)); + } + } + return failures; + }; + + const maxFailures = getFailures(last(versionsToTest), undefined, /*writeOutput*/ true); + if (maxFailures.length) { + return maxFailures; + } + + // As an optimization, check the earliest version for errors; + // assume that if it works on min and max, it works for everything in between. + const minFailures = getFailures(versionsToTest[0], undefined, /*writeOutput*/ false); + if (!minFailures.length) { + return []; + } + + // There are no failures in the max version, but there are failures in the min version. + // Work backward to find the newest version with failures. + for (let i = versionsToTest.length - 2; i >= 0; i--) { + const failures = getFailures(versionsToTest[i], options.versionsToTest[i + 1].versionName, /*writeOutput*/ false); + if (failures.length) { + return failures; + } + } + + throw new Error(); // unreachable -- at least the min version should have failures. + } +} + +export interface Options { + readonly tsconfigPath: string; + // These should be sorted with oldest first. + readonly versionsToTest: readonly VersionToTest[]; +} +export interface VersionToTest { + readonly versionName: string; + readonly path: string; +} + +const programCache = new WeakMap>(); +/** Maps a tslint Program to one created with the version specified in `options`. */ +export function getProgram(configFile: string, ts: typeof TsType, versionName: string, lintProgram: Program): Program { + let versionToProgram = programCache.get(lintProgram); + if (versionToProgram === undefined) { + versionToProgram = new Map(); + programCache.set(lintProgram, versionToProgram); + } + + let newProgram = versionToProgram.get(versionName); + if (newProgram === undefined) { + newProgram = createProgram(configFile, ts); + versionToProgram.set(versionName, newProgram); + } + return newProgram; +} + +function createProgram(configFile: string, ts: typeof TsType): Program { + const projectDirectory = dirname(configFile); + const { config } = ts.readConfigFile(configFile, ts.sys.readFile); + const parseConfigHost: TsType.ParseConfigHost = { + fileExists: existsSync, + readDirectory: ts.sys.readDirectory, + readFile: file => readFileSync(file, "utf8"), + useCaseSensitiveFileNames: true, + }; + const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, resolvePath(projectDirectory), {noEmit: true}); + const host = ts.createCompilerHost(parsed.options, true); + return ts.createProgram(parsed.fileNames, parsed.options, host); +} + +function walk( + ctx: Lint.WalkContext, + program: Program, + ts: typeof TsType, + versionName: string, + nextHigherVersion: string | undefined): void { + const { fileName } = ctx.sourceFile; + const sourceFile = program.getSourceFile(fileName)!; + if (!sourceFile) { + ctx.addFailure(0, 0, + `Program source files differ between TypeScript versions. This may be a dtslint bug.\n` + + `Expected to find a file '${fileName}' present in ${TsType.version}, but did not find it in ts@${versionName}.`); + return; + } + + const checker = program.getTypeChecker(); + // Don't care about emit errors. + const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile); + if (sourceFile.isDeclarationFile || !/\$Expect(Type|Error)/.test(sourceFile.text)) { + // Normal file. + for (const diagnostic of diagnostics) { + addDiagnosticFailure(diagnostic); + } + return; + } + + const { errorLines, typeAssertions, duplicates } = parseAssertions(sourceFile); + + for (const line of duplicates) { + addFailureAtLine(line, Rule.FAILURE_STRING_DUPLICATE_ASSERTION); + } + + const seenDiagnosticsOnLine = new Set(); + + for (const diagnostic of diagnostics) { + const line = lineOfPosition(diagnostic.start!, sourceFile); + seenDiagnosticsOnLine.add(line); + if (!errorLines.has(line)) { + addDiagnosticFailure(diagnostic); + } + } + + for (const line of errorLines) { + if (!seenDiagnosticsOnLine.has(line)) { + addFailureAtLine(line, Rule.FAILURE_STRING_EXPECTED_ERROR); + } + } + + const { unmetExpectations, unusedAssertions } = getExpectTypeFailures(sourceFile, typeAssertions, checker, ts); + for (const { node, expected, actual } of unmetExpectations) { + ctx.addFailureAtNode(node, Rule.FAILURE_STRING(versionName, expected, actual)); + } + for (const line of unusedAssertions) { + addFailureAtLine(line, Rule.FAILURE_STRING_ASSERTION_MISSING_NODE); + } + + function addDiagnosticFailure(diagnostic: TsType.Diagnostic): void { + const intro = getIntro(); + if (diagnostic.file === sourceFile) { + const msg = `${intro}\n${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`; + ctx.addFailureAt(diagnostic.start!, diagnostic.length!, msg); + } else { + ctx.addFailureAt(0, 0, `${intro}\n${fileName}${diagnostic.messageText}`); + } + } + + function getIntro(): string { + if (nextHigherVersion === undefined) { + return `TypeScript@${versionName} compile error: `; + } else { + const msg = `Compile error in typescript@${versionName} but not in typescript@${nextHigherVersion}.\n`; + const explain = nextHigherVersion === "next" + ? "TypeScript@next features not yet supported." + : `Fix with a comment '// Minimum TypeScript Version: ${nextHigherVersion}' just under the header.`; + return msg + explain; + } + } + + function addFailureAtLine(line: number, failure: string): void { + const start = sourceFile.getPositionOfLineAndCharacter(line, 0); + let end = start + sourceFile.text.split("\n")[line].length; + if (sourceFile.text[end - 1] === "\r") { + end--; + } + ctx.addFailure(start, end, `TypeScript@${versionName}: ${failure}`); + } +} + +interface Assertions { + /** Lines with an $ExpectError. */ + readonly errorLines: ReadonlySet; + /** Map from a line number to the expected type at that line. */ + readonly typeAssertions: Map; + /** Lines with more than one assertion (these are errors). */ + readonly duplicates: readonly number[]; +} + +function parseAssertions(sourceFile: SourceFile): Assertions { + const errorLines = new Set(); + const typeAssertions = new Map(); + const duplicates: number[] = []; + + const { text } = sourceFile; + const commentRegexp = /\/\/(.*)/g; + const lineStarts = sourceFile.getLineStarts(); + let curLine = 0; + + // eslint-disable-next-line no-constant-condition + while (true) { + const commentMatch = commentRegexp.exec(text); + if (commentMatch === null) { + break; + } + // Match on the contents of that comment so we do nothing in a commented-out assertion, + // i.e. `// foo; // $ExpectType number` + const match = /^ \$Expect((Type (.*))|Error)$/.exec(commentMatch[1]); + if (match === null) { + continue; + } + const line = getLine(commentMatch.index); + if (match[1] === "Error") { + if (errorLines.has(line)) { + duplicates.push(line); + } + errorLines.add(line); + } else { + const expectedType = match[3]; + // Don't bother with the assertion if there are 2 assertions on 1 line. Just fail for the duplicate. + if (typeAssertions.delete(line)) { + duplicates.push(line); + } else { + typeAssertions.set(line, expectedType); + } + } + } + + return { errorLines, typeAssertions, duplicates }; + + function getLine(pos: number): number { + // advance curLine to be the line preceding 'pos' + while (lineStarts[curLine + 1] <= pos) { + curLine++; + } + // If this is the first token on the line, it applies to the next line. + // Otherwise, it applies to the text to the left of it. + return isFirstOnLine(text, lineStarts[curLine], pos) ? curLine + 1 : curLine; + } +} + +function isFirstOnLine(text: string, lineStart: number, pos: number): boolean { + for (let i = lineStart; i < pos; i++) { + if (text[i] !== " ") { + return false; + } + } + return true; +} + +interface ExpectTypeFailures { + /** Lines with an $ExpectType, but a different type was there. */ + readonly unmetExpectations: readonly { node: TsType.Node, expected: string, actual: string }[]; + /** Lines with an $ExpectType, but no node could be found. */ + readonly unusedAssertions: Iterable; +} + +function matchReadonlyArray(actual: string, expected: string) { + if (!(/\breadonly\b/.test(actual) && /\bReadonlyArray\b/.test(expected))) return false; + const readonlyArrayRegExp = /\bReadonlyArray>>> + // A[]> + + let expectedPos = 0; + let actualPos = 0; + let depth = 0; + while (expectedPos < expected.length && actualPos < actual.length) { + const expectedChar = expected.charAt(expectedPos); + const actualChar = actual.charAt(actualPos); + if (expectedChar === actualChar) { + expectedPos++; + actualPos++; + continue; + } + + // check for end of readonly array + if (depth > 0 && expectedChar === ">" && actualChar === "[" && actualPos < actual.length - 1 && + actual.charAt(actualPos + 1) === "]") { + depth--; + expectedPos++; + actualPos += 2; + continue; + } + + // check for start of readonly array + readonlyArrayRegExp.lastIndex = expectedPos; + readonlyModifierRegExp.lastIndex = actualPos; + if (readonlyArrayRegExp.test(expected) && readonlyModifierRegExp.test(actual)) { + depth++; + expectedPos += 14; // "ReadonlyArray<".length; + actualPos += 9; // "readonly ".length; + continue; + } + + return false; + } + + return true; +} + +function getExpectTypeFailures( + sourceFile: SourceFile, + typeAssertions: Map, + checker: TsType.TypeChecker, + ts: typeof TsType, + ): ExpectTypeFailures { + const unmetExpectations: { node: TsType.Node, expected: string, actual: string }[] = []; + // Match assertions to the first node that appears on the line they apply to. + // `forEachChild` isn't available as a method in older TypeScript versions, so must use `ts.forEachChild` instead. + ts.forEachChild(sourceFile, function iterate(node) { + const line = lineOfPosition(node.getStart(sourceFile), sourceFile); + const expected = typeAssertions.get(line); + if (expected !== undefined) { + // https://github.com/Microsoft/TypeScript/issues/14077 + if (node.kind === ts.SyntaxKind.ExpressionStatement) { + node = (node as TsType.ExpressionStatement).expression; + } + + const type = checker.getTypeAtLocation(getNodeForExpectType(node, ts)); + + const actual = type + ? checker.typeToString(type, /*enclosingDeclaration*/ undefined, ts.TypeFormatFlags.NoTruncation) + : ""; + + if (!expected.split(/\s*\|\|\s*/).some(s => actual === s || matchReadonlyArray(actual, s))) { + unmetExpectations.push({ node, expected, actual }); + } + + typeAssertions.delete(line); + } + + ts.forEachChild(node, iterate); + }); + return { unmetExpectations, unusedAssertions: typeAssertions.keys() }; +} + +function getNodeForExpectType(node: TsType.Node, ts: typeof TsType): TsType.Node { + if (node.kind === ts.SyntaxKind.VariableStatement) { // ts2.0 doesn't have `isVariableStatement` + const { declarationList: { declarations } } = node as TsType.VariableStatement; + if (declarations.length === 1) { + const { initializer } = declarations[0]; + if (initializer) { + return initializer; + } + } + } + return node; +} + +function lineOfPosition(pos: number, sourceFile: SourceFile): number { + return sourceFile.getLineAndCharacterOfPosition(pos).line; +} diff --git a/packages/dtslint/src/rules/exportJustNamespaceRule.ts b/packages/dtslint/src/rules/exportJustNamespaceRule.ts new file mode 100644 index 00000000..0e49621b --- /dev/null +++ b/packages/dtslint/src/rules/exportJustNamespaceRule.ts @@ -0,0 +1,96 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "export-just-namespace", + description: + "Forbid to `export = foo` where `foo` is a namespace and isn't merged with a function/class/type/interface.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + static FAILURE_STRING = failure( + Rule.metadata.ruleName, + "Instead of `export =`-ing a namespace, use the body of the namespace as the module body."); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile: { statements } } = ctx; + const exportEqualsNode = statements.find(isExportEquals) as ts.ExportAssignment | undefined; + if (!exportEqualsNode) { + return; + } + const expr = exportEqualsNode.expression; + if (!ts.isIdentifier(expr)) { + return; + } + const exportEqualsName = expr.text; + + if (exportEqualsName && isJustNamespace(statements, exportEqualsName)) { + ctx.addFailureAtNode(exportEqualsNode, Rule.FAILURE_STRING); + } +} + +function isExportEquals(node: ts.Node): boolean { + return ts.isExportAssignment(node) && !!node.isExportEquals; +} + +/** Returns true if there is a namespace but there are no functions/classes with the name. */ +function isJustNamespace(statements: readonly ts.Statement[], exportEqualsName: string): boolean { + let anyNamespace = false; + + for (const statement of statements) { + switch (statement.kind) { + case ts.SyntaxKind.ModuleDeclaration: + anyNamespace = anyNamespace || nameMatches((statement as ts.ModuleDeclaration).name); + break; + case ts.SyntaxKind.VariableStatement: + if ((statement as ts.VariableStatement).declarationList.declarations.some(d => nameMatches(d.name))) { + // OK. It's merged with a variable. + return false; + } + break; + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.TypeAliasDeclaration: + case ts.SyntaxKind.InterfaceDeclaration: + if (nameMatches((statement as ts.DeclarationStatement).name)) { + // OK. It's merged with a function/class/type/interface. + return false; + } + break; + default: + } + } + + return anyNamespace; + + function nameMatches(nameNode: ts.Node | undefined): boolean { + return nameNode !== undefined && ts.isIdentifier(nameNode) && nameNode.text === exportEqualsName; + } +} + +/* +Tests: + +OK: + export = foo; + declare namespace foo {} + declare function foo(): void; // or interface, type, class + +Error: + export = foo; + declare namespace foo {} + +OK: (it's assumed to come from elsewhere) + export = foo; +*/ diff --git a/packages/dtslint/src/rules/noAnyUnionRule.ts b/packages/dtslint/src/rules/noAnyUnionRule.ts new file mode 100644 index 00000000..3807f377 --- /dev/null +++ b/packages/dtslint/src/rules/noAnyUnionRule.ts @@ -0,0 +1,32 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-any-union", + description: "Forbid a union to contain `any`", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + static FAILURE_STRING = failure( + Rule.metadata.ruleName, + "Including `any` in a union will override all other members of the union."); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + ctx.sourceFile.forEachChild(function recur(node) { + if (node.kind === ts.SyntaxKind.AnyKeyword && ts.isUnionTypeNode(node.parent!)) { + ctx.addFailureAtNode(node, Rule.FAILURE_STRING); + } + node.forEachChild(recur); + }); +} diff --git a/packages/dtslint/src/rules/noBadReferenceRule.ts b/packages/dtslint/src/rules/noBadReferenceRule.ts new file mode 100644 index 00000000..ad3da989 --- /dev/null +++ b/packages/dtslint/src/rules/noBadReferenceRule.ts @@ -0,0 +1,39 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-bad-reference", + description: 'Forbid in any file, and forbid in test files.', + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + static FAILURE_STRING = failure( + Rule.metadata.ruleName, + "Don't use to reference another package. Use an import or instead."); + static FAILURE_STRING_REFERENCE_IN_TEST = failure( + Rule.metadata.ruleName, + "Don't use in test files. Use or include the file in 'tsconfig.json'."); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + for (const ref of sourceFile.referencedFiles) { + if (sourceFile.isDeclarationFile) { + if (ref.fileName.startsWith("..")) { + ctx.addFailure(ref.pos, ref.end, Rule.FAILURE_STRING); + } + } else { + ctx.addFailure(ref.pos, ref.end, Rule.FAILURE_STRING_REFERENCE_IN_TEST); + } + } +} diff --git a/packages/dtslint/src/rules/noConstEnumRule.ts b/packages/dtslint/src/rules/noConstEnumRule.ts new file mode 100644 index 00000000..1d690ed7 --- /dev/null +++ b/packages/dtslint/src/rules/noConstEnumRule.ts @@ -0,0 +1,32 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-const-enum", + description: "Forbid `const enum`", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + static FAILURE_STRING = failure( + Rule.metadata.ruleName, + "Use of `const enum`s is forbidden."); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + ctx.sourceFile.forEachChild(function recur(node) { + if (ts.isEnumDeclaration(node) && node.modifiers && node.modifiers.some(m => m.kind === ts.SyntaxKind.ConstKeyword)) { + ctx.addFailureAtNode(node.name, Rule.FAILURE_STRING); + } + node.forEachChild(recur); + }); +} diff --git a/packages/dtslint/src/rules/noDeadReferenceRule.ts b/packages/dtslint/src/rules/noDeadReferenceRule.ts new file mode 100644 index 00000000..d3278930 --- /dev/null +++ b/packages/dtslint/src/rules/noDeadReferenceRule.ts @@ -0,0 +1,50 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-dead-reference", + description: "Ensures that all `/// ` comments go at the top of the file.", + rationale: "style", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + static FAILURE_STRING = failure( + Rule.metadata.ruleName, + "`/// ` directive must be at top of file to take effect."); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile: { statements, text } } = ctx; + if (!statements.length) { + return; + } + + // 'm' flag makes it multiline, so `^` matches the beginning of any line. + // 'g' flag lets us set rgx.lastIndex + const rgx = /^\s*(\/\/\/ ` before that is OK.) + rgx.lastIndex = statements[0].getStart(); + + // eslint-disable-next-line no-constant-condition + while (true) { + const match = rgx.exec(text); + if (match === null) { + break; + } + + const length = match[1].length; + const start = match.index + match[0].length - length; + ctx.addFailureAt(start, length, Rule.FAILURE_STRING); + } +} diff --git a/packages/dtslint/src/rules/noDeclareCurrentPackageRule.ts b/packages/dtslint/src/rules/noDeclareCurrentPackageRule.ts new file mode 100644 index 00000000..a2137ad7 --- /dev/null +++ b/packages/dtslint/src/rules/noDeclareCurrentPackageRule.ts @@ -0,0 +1,39 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure, getCommonDirectoryName } from "../util"; + +export class Rule extends Lint.Rules.TypedRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-declare-current-package", + description: "Don't use an ambient module declaration of the current package; use a normal module.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + if (!sourceFile.isDeclarationFile) { + return []; + } + + const packageName = getCommonDirectoryName(program.getRootFileNames()); + return this.applyWithFunction(sourceFile, ctx => walk(ctx, packageName)); + } +} + +function walk(ctx: Lint.WalkContext, packageName: string): void { + for (const statement of ctx.sourceFile.statements) { + if (ts.isModuleDeclaration(statement) && ts.isStringLiteral(statement.name)) { + const { text } = statement.name; + if (text === packageName || text.startsWith(`${packageName}/`)) { + const preferred = text === packageName ? '"index.d.ts"' : `"${text}.d.ts" or "${text}/index.d.ts`; + ctx.addFailureAtNode(statement.name, failure( + Rule.metadata.ruleName, + `Instead of declaring a module with \`declare module "${text}"\`, ` + + `write its contents in directly in ${preferred}.`)); + } + } + } +} diff --git a/packages/dtslint/src/rules/noImportDefaultOfExportEqualsRule.ts b/packages/dtslint/src/rules/noImportDefaultOfExportEqualsRule.ts new file mode 100644 index 00000000..6df1bb32 --- /dev/null +++ b/packages/dtslint/src/rules/noImportDefaultOfExportEqualsRule.ts @@ -0,0 +1,51 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { eachModuleStatement, failure, getModuleDeclarationStatements } from "../util"; + +export class Rule extends Lint.Rules.TypedRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-import-default-of-export-equals", + description: "Forbid a default import to reference an `export =` module.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + // eslint-disable-next-line @typescript-eslint/naming-convention + static FAILURE_STRING(importName: string, moduleName: string): string { + return failure( + Rule.metadata.ruleName, + `The module ${moduleName} uses \`export = \`. Import with \`import ${importName} = require(${moduleName})\`.`); + } + + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, ctx => walk(ctx, program.getTypeChecker())); + } +} + +function walk(ctx: Lint.WalkContext, checker: ts.TypeChecker): void { + eachModuleStatement(ctx.sourceFile, statement => { + if (!ts.isImportDeclaration(statement)) { + return; + } + const defaultName = statement.importClause && statement.importClause.name; + if (!defaultName) { + return; + } + const sym = checker.getSymbolAtLocation(statement.moduleSpecifier); + if (sym && sym.declarations && sym.declarations.some(d => { + const statements = getStatements(d); + return statements !== undefined && statements.some(s => ts.isExportAssignment(s) && !!s.isExportEquals); + })) { + ctx.addFailureAtNode(defaultName, Rule.FAILURE_STRING(defaultName.text, statement.moduleSpecifier.getText())); + } + }); +} + +function getStatements(decl: ts.Declaration): readonly ts.Statement[] | undefined { + return ts.isSourceFile(decl) ? decl.statements + : ts.isModuleDeclaration(decl) ? getModuleDeclarationStatements(decl) + : undefined; +} diff --git a/packages/dtslint/src/rules/noOutsideDependenciesRule.ts b/packages/dtslint/src/rules/noOutsideDependenciesRule.ts new file mode 100644 index 00000000..2694acad --- /dev/null +++ b/packages/dtslint/src/rules/noOutsideDependenciesRule.ts @@ -0,0 +1,36 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.TypedRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-outside-dependencies", + description: "Don't import things in `DefinitelyTyped/node_modules`.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + applyWithProgram(_sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + if (seenPrograms.has(program)) { + return []; + } + seenPrograms.add(program); + + const failures: Lint.RuleFailure[] = []; + for (const sourceFile of program.getSourceFiles()) { + const { fileName } = sourceFile; + if (fileName.includes("/DefinitelyTyped/node_modules/") && !program.isSourceFileDefaultLibrary(sourceFile)) { + const msg = failure( + Rule.metadata.ruleName, + `File ${fileName} comes from a \`node_modules\` but is not declared in this type's \`package.json\`. `); + failures.push(new Lint.RuleFailure(sourceFile, 0, 1, msg, Rule.metadata.ruleName)); + } + } + return failures; + } +} + +const seenPrograms = new WeakSet(); diff --git a/packages/dtslint/src/rules/noPaddingRule.ts b/packages/dtslint/src/rules/noPaddingRule.ts new file mode 100644 index 00000000..8e3aa8e3 --- /dev/null +++ b/packages/dtslint/src/rules/noPaddingRule.ts @@ -0,0 +1,86 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-padding", + description: "Forbids a blank line after `(` / `[` / `{`, or before `)` / `]` / `}`.", + optionsDescription: "Not configurable.", + options: null, + type: "style", + typescriptOnly: true, + }; + + // eslint-disable-next-line @typescript-eslint/naming-convention + static FAILURE_STRING(kind: "before" | "after", token: ts.SyntaxKind) { + return failure( + Rule.metadata.ruleName, + `Don't leave a blank line ${kind} '${ts.tokenToString(token)}'.`); + } + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + + function fail(kind: "before" | "after", child: ts.Node): void { + ctx.addFailureAtNode(child, Rule.FAILURE_STRING(kind, child.kind)); + } + + sourceFile.forEachChild(function cb(node) { + const children = node.getChildren(); + for (let i = 0; i < children.length; i++) { + const child = children[i]; + switch (child.kind) { + case ts.SyntaxKind.OpenParenToken: + case ts.SyntaxKind.OpenBracketToken: + case ts.SyntaxKind.OpenBraceToken: + if (i < children.length - 1 && blankLineInBetween(child.getEnd(), children[i + 1].getStart())) { + fail("after", child); + } + break; + + case ts.SyntaxKind.CloseParenToken: + case ts.SyntaxKind.CloseBracketToken: + case ts.SyntaxKind.CloseBraceToken: + if (i > 0 && blankLineInBetween(child.getStart() - 1, children[i - 1].getEnd() - 1)) { + fail("before", child); + } + break; + + default: + cb(child); + } + } + }); + + // Looks for two newlines (with nothing else in between besides whitespace) + function blankLineInBetween(start: number, end: number): boolean { + const step = start < end ? 1 : -1; + let seenLine = false; + for (let i = start; i !== end; i += step) { + switch (sourceFile.text[i]) { + case "\n": + if (seenLine) { + return true; + } else { + seenLine = true; + } + break; + + case " ": case "\t": case "\r": + break; + + default: + return false; + } + } + + return false; + } +} diff --git a/packages/dtslint/src/rules/noRedundantJsdoc2Rule.ts b/packages/dtslint/src/rules/noRedundantJsdoc2Rule.ts new file mode 100644 index 00000000..c2e9367e --- /dev/null +++ b/packages/dtslint/src/rules/noRedundantJsdoc2Rule.ts @@ -0,0 +1,253 @@ +// Fixes temporarily moved here until they are published by tslint. + +import assert = require("assert"); +import * as Lint from "tslint"; +import * as ts from "typescript"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-redundant-jsdoc", + description: "Forbids JSDoc which duplicates TypeScript functionality.", + optionsDescription: "Not configurable.", + options: null, + optionExamples: [true], + type: "style", + typescriptOnly: true, + }; + static readonly FAILURE_STRING_REDUNDANT_TYPE = + "Type annotation in JSDoc is redundant in TypeScript code."; + static readonly FAILURE_STRING_EMPTY = + "JSDoc comment is empty."; + // eslint-disable-next-line @typescript-eslint/naming-convention + static FAILURE_STRING_REDUNDANT_TAG(tagName: string): string { + return `JSDoc tag '@${tagName}' is redundant in TypeScript code.`; + } + // eslint-disable-next-line @typescript-eslint/naming-convention + static FAILURE_STRING_NO_COMMENT(tagName: string): string { + return `'@${tagName}' is redundant in TypeScript code if it has no description.`; + } + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + // Intentionally exclude EndOfFileToken: it can have JSDoc, but it is only relevant in JavaScript files + return sourceFile.statements.forEach(function cb(node: ts.Node): void { + if (node.kind !== ts.SyntaxKind.EndOfFileToken && (ts as any).hasJSDocNodes(node)) { + for (const jd of (node as any).jsDoc) { + const { tags } = jd as ts.JSDoc; + if (tags === undefined || tags.length === 0) { + if (jd.comment === undefined) { + ctx.addFailureAtNode( + jd, + Rule.FAILURE_STRING_EMPTY, + Lint.Replacement.deleteFromTo(jd.getStart(sourceFile), jd.getEnd())); + } + } else { + for (const tag of tags) { + checkTag(tag); + } + } + } + } + return ts.forEachChild(node, cb); + }); + + function checkTag(tag: ts.JSDocTag): void { + const jsdocSeeTag = (ts.SyntaxKind as any).JSDocSeeTag || 0; + const jsdocDeprecatedTag = (ts.SyntaxKind as any).JSDocDeprecatedTag || 0; + switch (tag.kind) { + case jsdocSeeTag: + case jsdocDeprecatedTag: + case ts.SyntaxKind.JSDocAuthorTag: + // @deprecated and @see always have meaning + break; + case ts.SyntaxKind.JSDocTag: { + const { tagName } = tag; + const { text } = tagName; + // Allow "default" in an ambient context (since you can't write an initializer in an ambient context) + if (redundantTags.has(text) && !(text === "default" && isInAmbientContext(tag))) { + ctx.addFailureAtNode(tagName, Rule.FAILURE_STRING_REDUNDANT_TAG(text), removeTag(tag, sourceFile)); + } + break; + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore (fallthrough) + case ts.SyntaxKind.JSDocTemplateTag: + if (tag.comment !== "") { + break; + } + // falls through + + case ts.SyntaxKind.JSDocPublicTag: + case ts.SyntaxKind.JSDocPrivateTag: + case ts.SyntaxKind.JSDocProtectedTag: + case ts.SyntaxKind.JSDocClassTag: + case ts.SyntaxKind.JSDocTypeTag: + case ts.SyntaxKind.JSDocTypedefTag: + case ts.SyntaxKind.JSDocReadonlyTag: + case ts.SyntaxKind.JSDocPropertyTag: + case ts.SyntaxKind.JSDocAugmentsTag: + case ts.SyntaxKind.JSDocImplementsTag: + case ts.SyntaxKind.JSDocCallbackTag: + case ts.SyntaxKind.JSDocThisTag: + case ts.SyntaxKind.JSDocEnumTag: + + // Always redundant + ctx.addFailureAtNode( + tag.tagName, + Rule.FAILURE_STRING_REDUNDANT_TAG(tag.tagName.text), + removeTag(tag, sourceFile)); + break; + + case ts.SyntaxKind.JSDocReturnTag: + case ts.SyntaxKind.JSDocParameterTag: { + const { typeExpression, comment } = tag as ts.JSDocReturnTag | ts.JSDocParameterTag; + const noComment = comment === ""; + if (typeExpression !== undefined) { + // If noComment, we will just completely remove it in the other fix + const fix = noComment ? undefined : removeTypeExpression(typeExpression, sourceFile); + ctx.addFailureAtNode(typeExpression, Rule.FAILURE_STRING_REDUNDANT_TYPE, fix); + } + if (noComment) { + // Redundant if no documentation + ctx.addFailureAtNode( + tag.tagName, + Rule.FAILURE_STRING_NO_COMMENT(tag.tagName.text), + removeTag(tag, sourceFile)); + } + break; + } + + default: + throw new Error(`Unexpected tag kind: ${ts.SyntaxKind[tag.kind]}`); + } + } +} + +function removeTag(tag: ts.JSDocTag, sourceFile: ts.SourceFile): Lint.Replacement | undefined { + const { text } = sourceFile; + const jsdoc = tag.parent; + if (jsdoc.kind === ts.SyntaxKind.JSDocTypeLiteral) { + return undefined; + } + + if (jsdoc.comment === undefined && jsdoc.tags!.length === 1) { + // This is the only tag -- remove the whole comment + return Lint.Replacement.deleteFromTo(jsdoc.getStart(sourceFile), jsdoc.getEnd()); + } + + let start = tag.getStart(sourceFile); + assert(text[start] === "@"); + start--; + while (ts.isWhiteSpaceSingleLine(text.charCodeAt(start))) { + start--; + } + if (text[start] !== "*") { + return undefined; + } + + let end = tag.getEnd(); + + // For some tags, like `@param`, `end` will be the start of the next tag. + // For some tags, like `@name`, `end` will be before the start of the comment. + // And `@typedef` doesn't end until the last `@property` tag attached to it ends. + switch (tag.tagName.text) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore (fallthrough) + case "param": { + const { isBracketed, isNameFirst, typeExpression } = tag as ts.JSDocParameterTag; + if (!isBracketed && !(isNameFirst && typeExpression !== undefined)) { + break; + } + // falls through + } + // eslint-disable-next-line no-fallthrough + case "name": + case "return": + case "returns": + case "interface": + case "default": + case "memberof": + case "memberOf": + case "method": + case "type": + case "class": + case "property": + case "function": + end--; // Might end with "\n" (test with just `@return` with no comment or type) + // For some reason, for "@name", "end" is before the start of the comment part of the tag. + // Also for "param" if the name is optional as in `@param {number} [x]` + while (!ts.isLineBreak(text.charCodeAt(end))) { + end++; + } + end++; + } + while (ts.isWhiteSpaceSingleLine(text.charCodeAt(end))) { + end++; + } + if (text[end] !== "*") { + return undefined; + } + + return Lint.Replacement.deleteFromTo(start, end); +} + +function removeTypeExpression( + typeExpression: ts.JSDocTypeExpression, + sourceFile: ts.SourceFile, +): Lint.Replacement | undefined { + const start = typeExpression.getStart(sourceFile); + let end = typeExpression.getEnd(); + const { text } = sourceFile; + if (text[start] !== "{" || text[end - 1] !== "}") { + // TypeScript parser messed up -- give up + return undefined; + } + if (ts.isWhiteSpaceSingleLine(text.charCodeAt(end))) { + end++; + } + return Lint.Replacement.deleteFromTo(start, end); +} + +// TODO: improve once https://github.com/Microsoft/TypeScript/pull/17831 is in +function isInAmbientContext(node: ts.Node): boolean { + return ts.isSourceFile(node) + ? node.isDeclarationFile + : Lint.hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword) || isInAmbientContext(node.parent!); +} + +const redundantTags = new Set([ + "abstract", + "access", + "class", + "constant", + "constructs", + "default", + "enum", + "export", + "exports", + "function", + "global", + "inherits", + "interface", + "instance", + "member", + "method", + "memberof", + "memberOf", + "mixes", + "mixin", + "module", + "name", + "namespace", + "override", + "property", + "requires", + "static", + "this", +]); diff --git a/packages/dtslint/src/rules/noRelativeImportInTestRule.ts b/packages/dtslint/src/rules/noRelativeImportInTestRule.ts new file mode 100644 index 00000000..0ded3103 --- /dev/null +++ b/packages/dtslint/src/rules/noRelativeImportInTestRule.ts @@ -0,0 +1,52 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.TypedRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-relative-import-in-test", + description: "Forbids test (non-declaration) files to use relative imports.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: false, + }; + + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + if (sourceFile.isDeclarationFile) { + return []; + } + + return this.applyWithFunction(sourceFile, ctx => walk(ctx, program.getTypeChecker())); + } +} + +const failureMessage = failure( + Rule.metadata.ruleName, + "Test file should not use a relative import. Use a global import as if this were a user of the package."); + +function walk(ctx: Lint.WalkContext, checker: ts.TypeChecker): void { + const { sourceFile } = ctx; + + for (const i of sourceFile.imports) { + if (i.text.startsWith(".")) { + const moduleSymbol = checker.getSymbolAtLocation(i); + if (!moduleSymbol || !moduleSymbol.declarations) { + continue; + } + + for (const decl of moduleSymbol.declarations) { + if (decl.kind === ts.SyntaxKind.SourceFile && (decl as ts.SourceFile).isDeclarationFile) { + ctx.addFailureAtNode(i, failureMessage); + } + } + } + } +} + +declare module "typescript" { + interface SourceFile { + imports: readonly ts.StringLiteral[]; + } +} diff --git a/packages/dtslint/src/rules/noSelfImportRule.ts b/packages/dtslint/src/rules/noSelfImportRule.ts new file mode 100644 index 00000000..af47a8cb --- /dev/null +++ b/packages/dtslint/src/rules/noSelfImportRule.ts @@ -0,0 +1,36 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure, getCommonDirectoryName } from "../util"; + +export class Rule extends Lint.Rules.TypedRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-self-import", + description: "Forbids declaration files to import the current package using a global import.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: false, + }; + + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + if (!sourceFile.isDeclarationFile) { + return []; + } + + const name = getCommonDirectoryName(program.getRootFileNames()); + return this.applyWithFunction(sourceFile, ctx => walk(ctx, name)); + } +} + +const failureMessage = failure( + Rule.metadata.ruleName, + "Declaration file should not use a global import of itself. Use a relative import."); + +function walk(ctx: Lint.WalkContext, packageName: string): void { + for (const i of ctx.sourceFile.imports) { + if (i.text === packageName || i.text.startsWith(packageName + "/")) { + ctx.addFailureAtNode(i, failureMessage); + } + } +} diff --git a/packages/dtslint/src/rules/noSingleDeclareModuleRule.ts b/packages/dtslint/src/rules/noSingleDeclareModuleRule.ts new file mode 100644 index 00000000..ce9d0317 --- /dev/null +++ b/packages/dtslint/src/rules/noSingleDeclareModuleRule.ts @@ -0,0 +1,54 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-single-declare-module", + description: "Don't use an ambient module declaration if there's just one -- write it as a normal module.", + rationale: "Cuts down on nesting", + optionsDescription: "Not configurable.", + options: null, + type: "style", + typescriptOnly: true, + }; + + static FAILURE_STRING = failure( + Rule.metadata.ruleName, + "File has only 1 ambient module declaration. Move the contents outside the ambient module block, rename the file to match the ambient module name, and remove the block."); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + + // If it's an external module, any module declarations inside are augmentations. + if (ts.isExternalModule(sourceFile)) { + return; + } + + let moduleDecl: ts.ModuleDeclaration | undefined; + for (const statement of sourceFile.statements) { + if (ts.isModuleDeclaration(statement) && ts.isStringLiteral(statement.name)) { + if (statement.name.text.indexOf('*') !== -1) { + // Ignore wildcard module declarations + return; + } + + if (moduleDecl === undefined) { + moduleDecl = statement; + } else { + // Has more than 1 declaration + return; + } + } + } + + if (moduleDecl) { + ctx.addFailureAtNode(moduleDecl, Rule.FAILURE_STRING); + } +} diff --git a/packages/dtslint/src/rules/noSingleElementTupleTypeRule.ts b/packages/dtslint/src/rules/noSingleElementTupleTypeRule.ts new file mode 100644 index 00000000..4c591e3a --- /dev/null +++ b/packages/dtslint/src/rules/noSingleElementTupleTypeRule.ts @@ -0,0 +1,31 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-single-element-tuple-type", + description: "Forbids `[T]`, which should be `T[]`.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: true, + }; + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + sourceFile.forEachChild(function cb(node) { + if (ts.isTupleTypeNode(node) && (node.elements ?? (node as any).elementTypes).length === 1) { + ctx.addFailureAtNode(node, failure( + Rule.metadata.ruleName, + "Type [T] is a single-element tuple type. You probably meant T[].")); + } + node.forEachChild(cb); + }); +} diff --git a/packages/dtslint/src/rules/noUnnecessaryGenericsRule.ts b/packages/dtslint/src/rules/noUnnecessaryGenericsRule.ts new file mode 100644 index 00000000..e193a201 --- /dev/null +++ b/packages/dtslint/src/rules/noUnnecessaryGenericsRule.ts @@ -0,0 +1,121 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.TypedRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-unnecessary-generics", + description: "Forbids signatures using a generic parameter only once.", + optionsDescription: "Not configurable.", + options: null, + type: "style", + typescriptOnly: true, + }; + // eslint-disable-next-line @typescript-eslint/naming-convention + static FAILURE_STRING(typeParameter: string) { + return failure( + Rule.metadata.ruleName, + `Type parameter ${typeParameter} is used only once.`); + } + // eslint-disable-next-line @typescript-eslint/naming-convention + static FAILURE_STRING_NEVER(typeParameter: string) { + return failure( + Rule.metadata.ruleName, + `Type parameter ${typeParameter} is never used.`); + } + + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, ctx => walk(ctx, program.getTypeChecker())); + } +} + +function walk(ctx: Lint.WalkContext, checker: ts.TypeChecker): void { + const { sourceFile } = ctx; + sourceFile.forEachChild(function cb(node) { + if (ts.isFunctionLike(node)) { + checkSignature(node); + } + node.forEachChild(cb); + }); + + function checkSignature(sig: ts.SignatureDeclaration) { + if (!sig.typeParameters) { + return; + } + + for (const tp of sig.typeParameters) { + const typeParameter = tp.name.text; + const res = getSoleUse(sig, assertDefined(checker.getSymbolAtLocation(tp.name)), checker); + switch (res.type) { + case "ok": + break; + case "sole": + ctx.addFailureAtNode(res.soleUse, Rule.FAILURE_STRING(typeParameter)); + break; + case "never": + ctx.addFailureAtNode(tp, Rule.FAILURE_STRING_NEVER(typeParameter)); + break; + default: + assertNever(res); + } + } + } +} + +type Result = + | { type: "ok" | "never" } + | { type: "sole", soleUse: ts.Identifier }; +function getSoleUse(sig: ts.SignatureDeclaration, typeParameterSymbol: ts.Symbol, checker: ts.TypeChecker): Result { + const exit = {}; + let soleUse: ts.Identifier | undefined; + + try { + if (sig.typeParameters) { + for (const tp of sig.typeParameters) { + if (tp.constraint) { + recur(tp.constraint); + } + } + } + for (const param of sig.parameters) { + if (param.type) { + recur(param.type); + } + } + if (sig.type) { + recur(sig.type); + } + } catch (err) { + if (err === exit) { + return { type: "ok" }; + } + throw err; + } + + return soleUse ? { type: "sole", soleUse } : { type: "never" }; + + function recur(node: ts.TypeNode): void { + if (ts.isIdentifier(node)) { + if (checker.getSymbolAtLocation(node) === typeParameterSymbol) { + if (soleUse === undefined) { + soleUse = node; + } else { + throw exit; + } + } + } else { + node.forEachChild(recur); + } + } +} + +function assertDefined(value: T | undefined): T { + if (value === undefined) { + throw new Error("unreachable"); + } + return value; +} +function assertNever(_: never) { + throw new Error("unreachable"); +} diff --git a/packages/dtslint/src/rules/noUselessFilesRule.ts b/packages/dtslint/src/rules/noUselessFilesRule.ts new file mode 100644 index 00000000..54a45fe9 --- /dev/null +++ b/packages/dtslint/src/rules/noUselessFilesRule.ts @@ -0,0 +1,31 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +// Same functionality as https://github.com/palantir/tslint/pull/1654, but simpler implementation. +// Remove when that PR is in. + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "no-useless-files", + description: "Forbids files with no content.", + optionsDescription: "Not configurable.", + options: null, + type: "functionality", + typescriptOnly: false, + }; + + static FAILURE_STRING = failure( + Rule.metadata.ruleName, + "File has no content."); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + const { statements, referencedFiles, typeReferenceDirectives } = sourceFile; + if (statements.length + referencedFiles.length + typeReferenceDirectives.length !== 0) { + return []; + } + + return [new Lint.RuleFailure(sourceFile, 0, 1, Rule.FAILURE_STRING, this.ruleName)]; + } +} diff --git a/packages/dtslint/src/rules/npmNamingRule.ts b/packages/dtslint/src/rules/npmNamingRule.ts new file mode 100644 index 00000000..1a412690 --- /dev/null +++ b/packages/dtslint/src/rules/npmNamingRule.ts @@ -0,0 +1,309 @@ +import { + CheckOptions as CriticOptions, + CriticError, + defaultErrors, + dtsCritic as critic, + ErrorKind, + ExportErrorKind, + Mode, + parseExportErrorKind, + parseMode } from "@definitelytyped/dts-critic"; +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { addSuggestion } from "../suggestions"; +import { failure, isMainFile } from "../util"; + +/** Options as parsed from the rule configuration. */ +type ConfigOptions = { + mode: Mode.NameOnly, + singleLine?: boolean, +} | { + mode: Mode.Code, + errors: [ExportErrorKind, boolean][], + singleLine?: boolean, +}; + +type Options = CriticOptions & { singleLine?: boolean }; + +const defaultOptions: ConfigOptions = { + mode: Mode.NameOnly, +}; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "npm-naming", + description: "Ensure that package name and DefinitelyTyped header match npm package info.", + optionsDescription: `An object with a \`mode\` property should be provided. +If \`mode\` is '${Mode.Code}', then option \`errors\` can be provided. +\`errors\` should be an array specifying which code checks should be enabled or disabled.`, + options: { + oneOf: [ + { + type: "object", + properties: { + "mode": { + type: "string", + enum: [Mode.NameOnly], + }, + "single-line": { + description: "Whether to print error messages in a single line. Used for testing.", + type: "boolean", + }, + "required": ["mode"], + }, + }, + { + type: "object", + properties: { + "mode": { + type: "string", + enum: [Mode.Code], + }, + "errors": { + type: "array", + items: { + type: "array", + items: [ + { description: "Name of the check.", + type: "string", + enum: [ErrorKind.NeedsExportEquals, ErrorKind.NoDefaultExport] as ExportErrorKind[], + }, + { + description: "Whether the check is enabled or disabled.", + type: "boolean", + }, + ], + minItems: 2, + maxItems: 2, + }, + default: [], + }, + "single-line": { + description: "Whether to print error messages in a single line. Used for testing.", + type: "boolean", + }, + "required": ["mode"], + }, + }, + ], + }, + optionExamples: [ + true, + [true, { mode: Mode.NameOnly }], + [ + true, + { + mode: Mode.Code, + errors: [[ErrorKind.NeedsExportEquals, true], [ErrorKind.NoDefaultExport, false]], + }, + ], + ], + type: "functionality", + typescriptOnly: true, + }; + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk, toCriticOptions(parseOptions(this.ruleArguments))); + } +} + +function parseOptions(args: unknown[]): ConfigOptions { + if (args.length === 0) { + return defaultOptions; + } + + const arg = args[0] as { [prop: string]: unknown } | null | undefined; + // eslint-disable-next-line eqeqeq + if (arg == null) { + return defaultOptions; + } + + if (!arg.mode || typeof arg.mode !== "string") { + return defaultOptions; + } + + const mode = parseMode(arg.mode); + if (!mode) { + return defaultOptions; + } + + const singleLine = !!arg["single-line"]; + + switch (mode) { + case Mode.NameOnly: + return { mode, singleLine }; + case Mode.Code: + if (!arg.errors || !Array.isArray(arg.errors)) { + return { mode, errors: [], singleLine }; + } + return { mode, errors: parseEnabledErrors(arg.errors), singleLine }; + } +} + +function parseEnabledErrors(errors: unknown[]): [ExportErrorKind, boolean][] { + const enabledChecks: [ExportErrorKind, boolean][] = []; + for (const tuple of errors) { + if (Array.isArray(tuple) + && tuple.length === 2 + && typeof tuple[0] === "string" + && typeof tuple[1] === "boolean") { + const error = parseExportErrorKind(tuple[0]); + if (error) { + enabledChecks.push([error, tuple[1]]); + } + } + } + return enabledChecks; +} + +function toCriticOptions(options: ConfigOptions): Options { + switch (options.mode) { + case Mode.NameOnly: + return options; + case Mode.Code: + return { ...options, errors: new Map(options.errors) }; + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + const { text } = sourceFile; + const lookFor = (search: string, explanation: string) => { + const idx = text.indexOf(search); + if (idx !== -1) { + ctx.addFailureAt(idx, search.length, failure(Rule.metadata.ruleName, explanation)); + } + }; + if (isMainFile(sourceFile.fileName, /*allowNested*/ false)) { + try { + const optionsWithSuggestions = toOptionsWithSuggestions(ctx.options); + const diagnostics = critic(sourceFile.fileName, /* sourcePath */ undefined, optionsWithSuggestions); + const errors = filterErrors(diagnostics, ctx); + for (const error of errors) { + switch (error.kind) { + case ErrorKind.NoMatchingNpmPackage: + case ErrorKind.NoMatchingNpmVersion: + case ErrorKind.NonNpmHasMatchingPackage: + lookFor("// Type definitions for", errorMessage(error, ctx.options)); + break; + case ErrorKind.DtsPropertyNotInJs: + case ErrorKind.DtsSignatureNotInJs: + case ErrorKind.JsPropertyNotInDts: + case ErrorKind.JsSignatureNotInDts: + case ErrorKind.NeedsExportEquals: + case ErrorKind.NoDefaultExport: + if (error.position) { + ctx.addFailureAt( + error.position.start, + error.position.length, + failure(Rule.metadata.ruleName, errorMessage(error, ctx.options))); + } else { + ctx.addFailure(0, 1, failure(Rule.metadata.ruleName, errorMessage(error, ctx.options))); + } + break; + } + } + } catch (e) { + // We're ignoring exceptions. + } + } + // Don't recur, we're done. +} + +const enabledSuggestions: ExportErrorKind[] = [ + ErrorKind.JsPropertyNotInDts, + ErrorKind.JsSignatureNotInDts, +]; + +function toOptionsWithSuggestions(options: CriticOptions): CriticOptions { + if (options.mode === Mode.NameOnly) { + return options; + } + const optionsWithSuggestions = { mode: options.mode, errors: new Map(options.errors) }; + enabledSuggestions.forEach(err => optionsWithSuggestions.errors.set(err, true)); + return optionsWithSuggestions; +} + +function filterErrors(diagnostics: CriticError[], ctx: Lint.WalkContext): CriticError[] { + const errors: CriticError[] = []; + diagnostics.forEach(diagnostic => { + if (isSuggestion(diagnostic, ctx.options)) { + addSuggestion(ctx, diagnostic.message, diagnostic.position?.start, diagnostic.position?.length); + } else { + errors.push(diagnostic); + } + }); + return errors; +} + +function isSuggestion(diagnostic: CriticError, options: Options): boolean { + return options.mode === Mode.Code + && (enabledSuggestions as ErrorKind[]).includes(diagnostic.kind) + && !(options.errors as Map).get(diagnostic.kind); +} + +function tslintDisableOption(error: ErrorKind): string { + switch (error) { + case ErrorKind.NoMatchingNpmPackage: + case ErrorKind.NoMatchingNpmVersion: + case ErrorKind.NonNpmHasMatchingPackage: + return `false`; + case ErrorKind.NoDefaultExport: + case ErrorKind.NeedsExportEquals: + case ErrorKind.JsSignatureNotInDts: + case ErrorKind.JsPropertyNotInDts: + case ErrorKind.DtsSignatureNotInJs: + case ErrorKind.DtsPropertyNotInJs: + return JSON.stringify([true, { mode: Mode.Code, errors: [[error, false]]}]); + } +} + +function errorMessage(error: CriticError, opts: Options): string { + const message = error.message + +`\nIf you won't fix this error now or you think this error is wrong, +you can disable this check by adding the following options to your project's tslint.json file under "rules": + + "npm-naming": ${tslintDisableOption(error.kind)} +`; + if (opts.singleLine) { + return message.replace(/(\r\n|\n|\r|\t)/gm, " "); + } + + return message; +} + +/** + * Given npm-naming lint failures, returns a rule configuration that prevents such failures. + */ +export function disabler(failures: Lint.IRuleFailureJson[]): false | [true, ConfigOptions] { + const disabledErrors = new Set(); + for (const ruleFailure of failures) { + if (ruleFailure.ruleName !== "npm-naming") { + throw new Error(`Expected failures of rule "npm-naming", found failures of rule ${ruleFailure.ruleName}.`); + } + const message = ruleFailure.failure; + // Name errors. + if (message.includes("must have a matching npm package") + || message.includes("must match a version that exists on npm") + || message.includes("conflicts with the existing npm package")) { + return false; + } + // Code errors. + if (message.includes("declaration should use 'export =' syntax")) { + disabledErrors.add(ErrorKind.NeedsExportEquals); + } else if (message.includes("declaration specifies 'export default' but the JavaScript source \ + does not mention 'default' anywhere")) { + disabledErrors.add(ErrorKind.NoDefaultExport); + } else { + return [true, { mode: Mode.NameOnly }]; + } + } + + if ((defaultErrors as ExportErrorKind[]).every(error => disabledErrors.has(error))) { + return [true, { mode: Mode.NameOnly }]; + } + const errors: [ExportErrorKind, boolean][] = []; + disabledErrors.forEach(error => errors.push([error, false])); + return [true, { mode: Mode.Code, errors }]; +} diff --git a/packages/dtslint/src/rules/preferDeclareFunctionRule.ts b/packages/dtslint/src/rules/preferDeclareFunctionRule.ts new file mode 100644 index 00000000..eee1dcda --- /dev/null +++ b/packages/dtslint/src/rules/preferDeclareFunctionRule.ts @@ -0,0 +1,35 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { eachModuleStatement, failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "prefer-declare-function", + description: "Forbids `export const x = () => void`.", + optionsDescription: "Not configurable.", + options: null, + type: "style", + typescriptOnly: true, + }; + + static FAILURE_STRING = failure( + Rule.metadata.ruleName, + "Use a function declaration instead of a variable of function type."); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + eachModuleStatement(ctx.sourceFile, statement => { + if (ts.isVariableStatement(statement)) { + for (const varDecl of statement.declarationList.declarations) { + if (varDecl.type !== undefined && varDecl.type.kind === ts.SyntaxKind.FunctionType) { + ctx.addFailureAtNode(varDecl, Rule.FAILURE_STRING); + } + } + } + }); +} diff --git a/packages/dtslint/src/rules/redundantUndefinedRule.ts b/packages/dtslint/src/rules/redundantUndefinedRule.ts new file mode 100644 index 00000000..22752a25 --- /dev/null +++ b/packages/dtslint/src/rules/redundantUndefinedRule.ts @@ -0,0 +1,40 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "redundant-undefined", + description: "Forbids optional parameters to include an explicit `undefined` in their type; requires it in optional properties.", + optionsDescription: "Not configurable.", + options: null, + type: "style", + typescriptOnly: true, + }; + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + if (ctx.sourceFile.fileName.includes('node_modules')) return; + ctx.sourceFile.forEachChild(function recur(node) { + if (node.kind === ts.SyntaxKind.UndefinedKeyword + && ts.isUnionTypeNode(node.parent!) + && isOptionalParameter(node.parent!.parent!)) { + ctx.addFailureAtNode( + node, + failure( + Rule.metadata.ruleName, + `Parameter is optional, so no need to include \`undefined\` in the type.`)); + } + node.forEachChild(recur); + }); +} + +function isOptionalParameter(node: ts.Node): boolean { + return node.kind === ts.SyntaxKind.Parameter + && (node as ts.ParameterDeclaration).questionToken !== undefined; +} diff --git a/packages/dtslint/src/rules/strictExportDeclareModifiersRule.ts b/packages/dtslint/src/rules/strictExportDeclareModifiersRule.ts new file mode 100644 index 00000000..52bc0c66 --- /dev/null +++ b/packages/dtslint/src/rules/strictExportDeclareModifiersRule.ts @@ -0,0 +1,163 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "strict-export-declare-modifiers", + description: "Enforces strict rules about where the 'export' and 'declare' modifiers may appear.", + optionsDescription: "Not configurable.", + options: null, + type: "style", + typescriptOnly: true, + }; + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + const isExternal = sourceFile.isDeclarationFile + && !sourceFile.statements.some( + s => s.kind === ts.SyntaxKind.ExportAssignment || + s.kind === ts.SyntaxKind.ExportDeclaration && !!(s as ts.ExportDeclaration).exportClause) + && ts.isExternalModule(sourceFile); + + for (const node of sourceFile.statements) { + if (isExternal) { + checkInExternalModule(node, isAutomaticExport(sourceFile)); + } else { + checkInOther(node, sourceFile.isDeclarationFile); + } + + if (isModuleDeclaration(node) && (sourceFile.isDeclarationFile || isDeclare(node))) { + checkModule(node); + } + } + + function checkInExternalModule(node: ts.Statement, autoExportEnabled: boolean) { + // Ignore certain node kinds (these can't have 'export' or 'default' modifiers) + switch (node.kind) { + case ts.SyntaxKind.ImportDeclaration: + case ts.SyntaxKind.ImportEqualsDeclaration: + case ts.SyntaxKind.ExportDeclaration: + case ts.SyntaxKind.NamespaceExportDeclaration: + return; + } + + // `declare global` and `declare module "foo"` OK. `declare namespace N` not OK, should be `export namespace`. + if (!isDeclareGlobalOrExternalModuleDeclaration(node)) { + if (isDeclare(node)) { + fail(mod(node, ts.SyntaxKind.DeclareKeyword), "'declare' keyword is redundant here."); + } + if (autoExportEnabled && !isExport(node)) { + fail( + (node as ts.DeclarationStatement).name || node, + "All declarations in this module are exported automatically. " + + "Prefer to explicitly write 'export' for clarity. " + + "If you have a good reason not to export this declaration, " + + "add 'export {}' to the module to shut off automatic exporting."); + } + } + } + + function checkInOther(node: ts.Statement, inDeclarationFile: boolean): void { + // Compiler will enforce presence of 'declare' where necessary. But types do not need 'declare'. + if (isDeclare(node)) { + if (isExport(node) && inDeclarationFile || + node.kind === ts.SyntaxKind.InterfaceDeclaration || + node.kind === ts.SyntaxKind.TypeAliasDeclaration) { + fail(mod(node, ts.SyntaxKind.DeclareKeyword), "'declare' keyword is redundant here."); + } + } + } + + function fail(node: ts.Node, reason: string): void { + ctx.addFailureAtNode(node, failure(Rule.metadata.ruleName, reason)); + } + + function mod(node: ts.Statement, kind: ts.SyntaxKind): ts.Node { + return node.modifiers!.find(m => m.kind === kind)!; + } + + function checkModule(moduleDeclaration: ts.ModuleDeclaration): void { + const body = moduleDeclaration.body; + if (!body) { + return; + } + + switch (body.kind) { + case ts.SyntaxKind.ModuleDeclaration: + checkModule(body); + break; + case ts.SyntaxKind.ModuleBlock: + checkBlock(body, isAutomaticExport(moduleDeclaration)); + break; + } + } + + function checkBlock(block: ts.ModuleBlock, autoExportEnabled: boolean): void { + for (const s of block.statements) { + // Compiler will error for 'declare' here anyway, so just check for 'export'. + if (isExport(s) && autoExportEnabled && !isDefault(s)) { + fail(mod(s, ts.SyntaxKind.ExportKeyword), + "'export' keyword is redundant here because " + + "all declarations in this module are exported automatically. " + + "If you have a good reason to export some declarations and not others, " + + "add 'export {}' to the module to shut off automatic exporting."); + } + + if (isModuleDeclaration(s)) { + checkModule(s); + } + } + } +} + +function isDeclareGlobalOrExternalModuleDeclaration(node: ts.Node): boolean { + return isModuleDeclaration(node) && ( + node.name.kind === ts.SyntaxKind.StringLiteral || + node.name.kind === ts.SyntaxKind.Identifier && node.name.text === "global"); +} + +function isModuleDeclaration(node: ts.Node): node is ts.ModuleDeclaration { + return node.kind === ts.SyntaxKind.ModuleDeclaration; +} + +function isDeclare(node: ts.Node): boolean { + return Lint.hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword); +} + +function isExport(node: ts.Node): boolean { + return Lint.hasModifier(node.modifiers, ts.SyntaxKind.ExportKeyword); +} + +function isDefault(node: ts.Node): boolean { + return Lint.hasModifier(node.modifiers, ts.SyntaxKind.DefaultKeyword); +} + +// tslint:disable-next-line:max-line-length +// Copied from https://github.com/Microsoft/TypeScript/blob/dd9b8cab34a3e389e924d768eb656cf50656f582/src/compiler/binder.ts#L1571-L1581 +function hasExportDeclarations(node: ts.SourceFile | ts.ModuleDeclaration): boolean { + const body = node.kind === ts.SyntaxKind.SourceFile ? node : node.body; + if (body && (body.kind === ts.SyntaxKind.SourceFile || body.kind === ts.SyntaxKind.ModuleBlock)) { + for (const stat of (body as ts.BlockLike).statements) { + if (stat.kind === ts.SyntaxKind.ExportDeclaration || stat.kind === ts.SyntaxKind.ExportAssignment) { + return true; + } + } + } + return false; +} + +function isAutomaticExport(node: ts.SourceFile | ts.ModuleDeclaration): boolean { + // We'd like to just test ts.NodeFlags.ExportContext, but we don't run the + // binder, so that flag won't be set, so duplicate the logic instead. :( + // + // ts.NodeFlags.Ambient is @internal, but all modules that get here should + // be ambient. + return !hasExportDeclarations(node); +} diff --git a/packages/dtslint/src/rules/trimFileRule.ts b/packages/dtslint/src/rules/trimFileRule.ts new file mode 100644 index 00000000..af2f49fd --- /dev/null +++ b/packages/dtslint/src/rules/trimFileRule.ts @@ -0,0 +1,36 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "trim-file", + description: "Forbids leading/trailing blank lines in a file. Allows file to end in '\n'.", + optionsDescription: "Not configurable.", + options: null, + type: "style", + typescriptOnly: false, + }; + + static FAILURE_STRING_LEADING = failure(Rule.metadata.ruleName, "File should not begin with a blank line."); + static FAILURE_STRING_TRAILING = failure( + Rule.metadata.ruleName, + "File should not end with a blank line. (Ending in one newline OK, ending in two newlines not OK.)"); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile: { text } } = ctx; + if (text.startsWith("\r") || text.startsWith("\n")) { + ctx.addFailureAt(0, 0, Rule.FAILURE_STRING_LEADING); + } + + if (text.endsWith("\n\n") || text.endsWith("\r\n\r\n")) { + const start = text.endsWith("\r\n") ? text.length - 2 : text.length - 1; + ctx.addFailureAt(start, 0, Rule.FAILURE_STRING_TRAILING); + } +} diff --git a/packages/dtslint/src/rules/voidReturnRule.ts b/packages/dtslint/src/rules/voidReturnRule.ts new file mode 100644 index 00000000..9f9dee5a --- /dev/null +++ b/packages/dtslint/src/rules/voidReturnRule.ts @@ -0,0 +1,70 @@ +import * as Lint from "tslint"; +import * as ts from "typescript"; + +import { failure } from "../util"; + +export class Rule extends Lint.Rules.AbstractRule { + static metadata: Lint.IRuleMetadata = { + ruleName: "void-return", + description: "`void` may only be used as a return type.", + rationale: "style", + optionsDescription: "Not configurable.", + options: null, + type: "style", + typescriptOnly: true, + }; + + static FAILURE_STRING = failure( + Rule.metadata.ruleName, + "Use the `void` type for return types only. Otherwise, use `undefined`."); + + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + ctx.sourceFile.forEachChild(function cb(node) { + if (node.kind === ts.SyntaxKind.VoidKeyword && !mayContainVoid(node.parent!) && !isReturnType(node)) { + ctx.addFailureAtNode(node, Rule.FAILURE_STRING); + } else { + node.forEachChild(cb); + } + }); +} + +function mayContainVoid({ kind }: ts.Node): boolean { + switch (kind) { + case ts.SyntaxKind.TypeReference: + case ts.SyntaxKind.ExpressionWithTypeArguments: + case ts.SyntaxKind.NewExpression: + case ts.SyntaxKind.CallExpression: + case ts.SyntaxKind.TypeParameter: // Allow f + return true; + default: + return false; + } +} + +function isReturnType(node: ts.Node): boolean { + let parent = node.parent!; + if (parent.kind === ts.SyntaxKind.UnionType) { + [node, parent] = [parent, parent.parent!]; + } + return isSignatureDeclaration(parent) && parent.type === node; +} + +function isSignatureDeclaration(node: ts.Node): node is ts.SignatureDeclaration { + switch (node.kind) { + case ts.SyntaxKind.ArrowFunction: + case ts.SyntaxKind.CallSignature: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.FunctionType: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + return true; + default: + return false; + } +} diff --git a/packages/dtslint/src/suggestions.ts b/packages/dtslint/src/suggestions.ts new file mode 100644 index 00000000..32c470f7 --- /dev/null +++ b/packages/dtslint/src/suggestions.ts @@ -0,0 +1,75 @@ +import fs = require("fs"); +import os = require("os"); +import path = require("path"); +import { WalkContext } from "tslint"; + +const suggestionsDir = path.join(os.homedir(), ".dts", "suggestions"); + +export interface Suggestion { + fileName: string; + ruleName: string; + message: string; + start?: number; + width?: number; +} + +// Packages for which suggestions were already added in this run of dtslint. +const existingPackages = new Set(); + +/** + * A rule should call this function to provide a suggestion instead of a lint failure. + */ +export function addSuggestion(ctx: WalkContext, message: string, start?: number, width?: number) { + const suggestion: Suggestion = { + fileName: ctx.sourceFile.fileName, + ruleName: ctx.ruleName, + message, + start, + width, + }; + + const packageName = dtPackageName(ctx.sourceFile.fileName); + if (!packageName) { + return; + } + let flag = "a"; + if (!existingPackages.has(packageName)) { + flag = "w"; + existingPackages.add(packageName); + } + try { + if (!fs.existsSync(suggestionsDir)) { + fs.mkdirSync(suggestionsDir, { recursive: true }); + } + fs.writeFileSync( + path.join(suggestionsDir, packageName + ".txt"), + flag === "a" ? "\n" + formatSuggestion(suggestion) : formatSuggestion(suggestion), + { flag, encoding: "utf8" }); + } catch (e) { + console.log(`Could not write suggestions for package ${packageName}. ${e.message || ""}`); + } +} + +const dtPath = path.join("DefinitelyTyped", "types"); + +function dtPackageName(filePath: string): string | undefined { + const dtIndex = filePath.indexOf(dtPath); + if (dtIndex === -1) { + return undefined; + } + const basePath = filePath.substr(dtIndex + dtPath.length); + const dirs = basePath.split(path.sep).filter(dir => dir !== ""); + if (dirs.length === 0) { + return undefined; + } + const packageName = dirs[0]; + // Check if this is an old version of a package. + if (dirs.length > 1 && /^v\d+(\.\d+)?$/.test(dirs[1])) { + return packageName + dirs[1]; + } + return packageName; +} + +function formatSuggestion(suggestion: Suggestion): string { + return JSON.stringify(suggestion, /*replacer*/ undefined, 0); +} diff --git a/packages/dtslint/src/updateConfig.ts b/packages/dtslint/src/updateConfig.ts new file mode 100644 index 00000000..2db746ba --- /dev/null +++ b/packages/dtslint/src/updateConfig.ts @@ -0,0 +1,249 @@ +// This is a stand-alone script that updates TSLint configurations for DefinitelyTyped packages. +// It runs all rules specified in `dt.json`, and updates the existing configuration for a package +// by adding rule exemptions only for the rules that caused a lint failure. +// For example, if a configuration specifies `"no-trailing-whitespace": false` and this rule +// no longer produces an error, then it will not be disabled in the new configuration. +// If you update or create a rule and now it causes new failures in DT, you can update the `dt.json` +// configuration with your rule, then register a disabler function for your rule +// (check `disableRules` function below), then run this script with your rule as argument. + +import cp = require("child_process"); +import fs = require("fs"); +import stringify = require("json-stable-stringify"); +import path = require("path"); +import { Configuration as Config, ILinterOptions, IRuleFailureJson, Linter, LintResult, RuleFailure } from "tslint"; +import * as ts from "typescript"; +import yargs = require("yargs"); +import { isExternalDependency } from "./lint"; +import { disabler as npmNamingDisabler } from "./rules/npmNamingRule"; + +// Rule "expect" needs TypeScript version information, which this script doesn't collect. +const ignoredRules: string[] = ["expect"]; + +function main() { + const args = yargs + .usage(`\`$0 --dt=path-to-dt\` or \`$0 --package=path-to-dt-package\` +'dt.json' is used as the base tslint config for running the linter.`) + .option("package", { + describe: "Path of DT package.", + type: "string", + conflicts: "dt", + }) + .option("dt", { + describe: "Path of local DefinitelyTyped repository.", + type: "string", + conflicts: "package", + }) + .option("rules", { + describe: "Names of the rules to be updated. Leave this empty to update all rules.", + type: "array", + string: true, + default: [] as string[], + }) + .check(arg => { + if (!arg.package && !arg.dt) { + throw new Error("You must provide either argument 'package' or 'dt'."); + } + const unsupportedRules = arg.rules.filter(rule => ignoredRules.includes(rule)); + if (unsupportedRules.length > 0) { + throw new Error(`Rules ${unsupportedRules.join(", ")} are not supported at the moment.`); + } + return true; + }).argv; + + if (args.package) { + updatePackage(args.package, dtConfig(args.rules)); + } else if (args.dt) { + updateAll(args.dt, dtConfig(args.rules)); + } +} + +const dtConfigPath = "dt.json"; + +function dtConfig(updatedRules: string[]): Config.IConfigurationFile { + const config = Config.findConfiguration(dtConfigPath).results; + if (!config) { + throw new Error(`Could not load config at ${dtConfigPath}.`); + } + // Disable ignored or non-updated rules. + for (const entry of config.rules.entries()) { + const [rule, ruleOpts] = entry; + if (ignoredRules.includes(rule) || (updatedRules.length > 0 && !updatedRules.includes(rule))) { + ruleOpts.ruleSeverity = "off"; + } + } + return config; +} + +function updateAll(dtPath: string, config: Config.IConfigurationFile): void { + const packages = fs.readdirSync(path.join(dtPath, "types")); + for (const pkg of packages) { + updatePackage(path.join(dtPath, "types", pkg), config); + } +} + +function updatePackage(pkgPath: string, baseConfig: Config.IConfigurationFile): void { + installDependencies(pkgPath); + const packages = walkPackageDir(pkgPath); + + const linterOpts: ILinterOptions = { + fix: false, + }; + + for (const pkg of packages) { + const results = pkg.lint(linterOpts, baseConfig); + if (results.failures.length > 0) { + const disabledRules = disableRules(results.failures); + const newConfig = mergeConfigRules(pkg.config(), disabledRules, baseConfig); + pkg.updateConfig(newConfig); + } + } +} + +function installDependencies(pkgPath: string): void { + if (fs.existsSync(path.join(pkgPath, "package.json"))) { + cp.execSync( + "npm install --ignore-scripts --no-shrinkwrap --no-package-lock --no-bin-links", + { + encoding: "utf8", + cwd: pkgPath, + }); + } +} + +function mergeConfigRules( + config: Config.RawConfigFile, + newRules: Config.RawRulesConfig, + baseConfig: Config.IConfigurationFile): Config.RawConfigFile { + const activeRules: string[] = []; + baseConfig.rules.forEach((ruleOpts, ruleName) => { + if (ruleOpts.ruleSeverity !== "off") { + activeRules.push(ruleName); + } + }); + const oldRules: Config.RawRulesConfig = config.rules || {}; + let newRulesConfig: Config.RawRulesConfig = {}; + for (const rule of Object.keys(oldRules)) { + if (activeRules.includes(rule)) { + continue; + } + newRulesConfig[rule] = oldRules[rule]; + } + newRulesConfig = { ...newRulesConfig, ...newRules }; + return { ...config, rules: newRulesConfig }; +} + +/** + * Represents a package from the linter's perspective. + * For example, `DefinitelyTyped/types/react` and `DefinitelyTyped/types/react/v15` are different + * packages. + */ +class LintPackage { + private files: ts.SourceFile[] = []; + private program: ts.Program; + + constructor(private rootDir: string) { + this.program = Linter.createProgram(path.join(this.rootDir, "tsconfig.json")); + } + + config(): Config.RawConfigFile { + return Config.readConfigurationFile(path.join(this.rootDir, "tslint.json")); + } + + addFile(filePath: string): void { + const file = this.program.getSourceFile(filePath); + if (file) { + this.files.push(file); + } + } + + lint(opts: ILinterOptions, config: Config.IConfigurationFile): LintResult { + const linter = new Linter(opts, this.program); + for (const file of this.files) { + if (ignoreFile(file, this.rootDir, this.program)) { + continue; + } + linter.lint(file.fileName, file.text, config); + } + return linter.getResult(); + } + + updateConfig(config: Config.RawConfigFile): void { + fs.writeFileSync( + path.join(this.rootDir, "tslint.json"), + stringify(config, { space: 4 }), + { encoding: "utf8", flag: "w" }); + } +} + +function ignoreFile(file: ts.SourceFile, dirPath: string, program: ts.Program): boolean { + return program.isSourceFileDefaultLibrary(file) || isExternalDependency(file, path.resolve(dirPath), program); +} + +function walkPackageDir(rootDir: string): LintPackage[] { + const packages: LintPackage[] = []; + + function walk(curPackage: LintPackage, dir: string): void { + for (const ent of fs.readdirSync(dir, { encoding: "utf8", withFileTypes: true })) { + const entPath = path.join(dir, ent.name); + if (ent.isFile()) { + curPackage.addFile(entPath); + } else if (ent.isDirectory() && ent.name !== "node_modules") { + if (isVersionDir(ent.name)) { + const newPackage = new LintPackage(entPath); + packages.push(newPackage); + walk(newPackage, entPath); + } else { + walk(curPackage, entPath); + } + } + } + } + + const lintPackage = new LintPackage(rootDir); + packages.push(lintPackage); + walk(lintPackage, rootDir); + return packages; +} + +/** + * Returns true if directory name matches a TypeScript or package version directory. + * Examples: `ts3.5`, `v11`, `v0.6` are all version names. + */ +function isVersionDir(dirName: string): boolean { + return /^ts\d+\.\d$/.test(dirName) || /^v\d+(\.\d+)?$/.test(dirName); +} + +type RuleOptions = boolean | unknown[]; +type RuleDisabler = (failures: IRuleFailureJson[]) => RuleOptions; +const defaultDisabler: RuleDisabler = () => { + return false; +}; + +function disableRules(allFailures: RuleFailure[]): Config.RawRulesConfig { + const ruleToFailures: Map = new Map(); + for (const failure of allFailures) { + const failureJson = failure.toJson(); + if (ruleToFailures.has(failureJson.ruleName)) { + ruleToFailures.get(failureJson.ruleName)!.push(failureJson); + } else { + ruleToFailures.set(failureJson.ruleName, [failureJson]); + } + } + + const newRulesConfig: Config.RawRulesConfig = {}; + ruleToFailures.forEach((failures, rule) => { + if (ignoredRules.includes(rule)) { + return; + } + const disabler = rule === "npm-naming" ? npmNamingDisabler : defaultDisabler; + const opts: RuleOptions = disabler(failures); + newRulesConfig[rule] = opts; + }); + + return newRulesConfig; +} + +if (!module.parent) { + main(); +} diff --git a/packages/dtslint/src/util.ts b/packages/dtslint/src/util.ts new file mode 100644 index 00000000..6036f55b --- /dev/null +++ b/packages/dtslint/src/util.ts @@ -0,0 +1,109 @@ +import assert = require("assert"); +import { pathExists, readFile } from "fs-extra"; +import { basename, dirname, join } from "path"; +import stripJsonComments = require("strip-json-comments"); +import * as ts from "typescript"; + +export async function readJson(path: string) { + const text = await readFile(path, "utf-8"); + return JSON.parse(stripJsonComments(text)); +} + +export function failure(ruleName: string, s: string): string { + return `${s} See: https://github.com/Microsoft/dtslint/blob/master/docs/${ruleName}.md`; +} + +export function getCommonDirectoryName(files: readonly string[]): string { + let minLen = 999; + let minDir = ""; + for (const file of files) { + const dir = dirname(file); + if (dir.length < minLen) { + minDir = dir; + minLen = dir.length; + } + } + return basename(minDir); +} + +export function eachModuleStatement(sourceFile: ts.SourceFile, action: (statement: ts.Statement) => void): void { + if (!sourceFile.isDeclarationFile) { + return; + } + + for (const node of sourceFile.statements) { + if (ts.isModuleDeclaration(node)) { + const statements = getModuleDeclarationStatements(node); + if (statements) { + for (const statement of statements) { + action(statement); + } + } + } else { + action(node); + } + } +} + +export function getModuleDeclarationStatements(node: ts.ModuleDeclaration): readonly ts.Statement[] | undefined { + let { body } = node; + while (body && body.kind === ts.SyntaxKind.ModuleDeclaration) { + body = body.body; + } + return body && ts.isModuleBlock(body) ? body.statements : undefined; +} + +export async function getCompilerOptions(dirPath: string): Promise { + const tsconfigPath = join(dirPath, "tsconfig.json"); + if (!await pathExists(tsconfigPath)) { + throw new Error(`Need a 'tsconfig.json' file in ${dirPath}`); + } + return (await readJson(tsconfigPath)).compilerOptions; +} + +export function withoutPrefix(s: string, prefix: string): string | undefined { + return s.startsWith(prefix) ? s.slice(prefix.length) : undefined; +} + +export function last(a: readonly T[]): T { + assert(a.length !== 0); + return a[a.length - 1]; +} + +export function assertDefined(a: T | undefined): T { + if (a === undefined) { throw new Error(); } + return a; +} + +export async function mapDefinedAsync( + arr: Iterable, mapper: (t: T) => Promise): Promise { + const out = []; + for (const a of arr) { + const res = await mapper(a); + if (res !== undefined) { + out.push(res); + } + } + return out; +} + +export function isMainFile(fileName: string, allowNested: boolean) { + // Linter may be run with cwd of the package. We want `index.d.ts` but not `submodule/index.d.ts` to match. + if (fileName === "index.d.ts") { + return true; + } + + if (basename(fileName) !== "index.d.ts") { + return false; + } + + let parent = dirname(fileName); + // May be a directory for an older version, e.g. `v0`. + // Note a types redirect `foo/ts3.1` should not have its own header. + if (allowNested && /^v(0\.)?\d+$/.test(basename(parent))) { + parent = dirname(parent); + } + + // Allow "types/foo/index.d.ts", not "types/foo/utils/index.d.ts" + return basename(dirname(parent)) === "types"; +} diff --git a/packages/dtslint/test/dt-header/correct/tslint.json b/packages/dtslint/test/dt-header/correct/tslint.json new file mode 100644 index 00000000..e43c1125 --- /dev/null +++ b/packages/dtslint/test/dt-header/correct/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../../bin/rules"], + "rules": { + "dt-header": true + } +} diff --git a/packages/dtslint/test/dt-header/correct/types/foo/bar/index.d.ts.lint b/packages/dtslint/test/dt-header/correct/types/foo/bar/index.d.ts.lint new file mode 100644 index 00000000..5b29058a --- /dev/null +++ b/packages/dtslint/test/dt-header/correct/types/foo/bar/index.d.ts.lint @@ -0,0 +1 @@ +// This isn't the main index diff --git a/packages/dtslint/test/dt-header/correct/types/foo/index.d.ts.lint b/packages/dtslint/test/dt-header/correct/types/foo/index.d.ts.lint new file mode 100644 index 00000000..300eb907 --- /dev/null +++ b/packages/dtslint/test/dt-header/correct/types/foo/index.d.ts.lint @@ -0,0 +1,5 @@ +// Type definitions for dt-header 2.0 +// Project: https://github.com/bobby-headers/dt-header +// Definitions by: Jane Doe +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// Minimum TypeScript Version: 3.1 diff --git a/packages/dtslint/test/dt-header/correct/types/foo/notIndex.d.ts.lint b/packages/dtslint/test/dt-header/correct/types/foo/notIndex.d.ts.lint new file mode 100644 index 00000000..e69de29b diff --git a/packages/dtslint/test/dt-header/correct/types/foo/v0.75/index.d.ts.lint b/packages/dtslint/test/dt-header/correct/types/foo/v0.75/index.d.ts.lint new file mode 100644 index 00000000..71915f71 --- /dev/null +++ b/packages/dtslint/test/dt-header/correct/types/foo/v0.75/index.d.ts.lint @@ -0,0 +1,5 @@ +// Type definitions for dt-header 0.75 +// Project: https://github.com/bobby-headers/dt-header +// Definitions by: Jane Doe +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// Minimum TypeScript Version: 3.1 diff --git a/packages/dtslint/test/dt-header/correct/types/foo/v1/index.d.ts.lint b/packages/dtslint/test/dt-header/correct/types/foo/v1/index.d.ts.lint new file mode 100644 index 00000000..96bd3849 --- /dev/null +++ b/packages/dtslint/test/dt-header/correct/types/foo/v1/index.d.ts.lint @@ -0,0 +1,5 @@ +// Type definitions for dt-header 1.0 +// Project: https://github.com/bobby-headers/dt-header +// Definitions by: Jane Doe +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// Minimum TypeScript Version: 3.1 diff --git a/packages/dtslint/test/dt-header/wrong/tslint.json b/packages/dtslint/test/dt-header/wrong/tslint.json new file mode 100644 index 00000000..e43c1125 --- /dev/null +++ b/packages/dtslint/test/dt-header/wrong/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../../bin/rules"], + "rules": { + "dt-header": true + } +} diff --git a/packages/dtslint/test/dt-header/wrong/types/bad-url-username/index.d.ts.lint b/packages/dtslint/test/dt-header/wrong/types/bad-url-username/index.d.ts.lint new file mode 100644 index 00000000..ee1c493a --- /dev/null +++ b/packages/dtslint/test/dt-header/wrong/types/bad-url-username/index.d.ts.lint @@ -0,0 +1,5 @@ +// Type definitions for dt-header 1.0 +// Project: https://github.com/bobby-headers/dt-header +// Definitions by: Jane Doe + ~ [Error parsing header. Expected: /\/. See: https://github.com/Microsoft/dtslint/blob/master/docs/dt-header.md] +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped diff --git a/packages/dtslint/test/dt-header/wrong/types/bad-url/index.d.ts.lint b/packages/dtslint/test/dt-header/wrong/types/bad-url/index.d.ts.lint new file mode 100644 index 00000000..58d577f7 --- /dev/null +++ b/packages/dtslint/test/dt-header/wrong/types/bad-url/index.d.ts.lint @@ -0,0 +1,5 @@ +// Type definitions for dt-header 1.0 +// Project: https://github.com/bobby-headers/dt-header +// Definitions by: Jane Doe + ~ [Error parsing header. Expected: /\/. See: https://github.com/Microsoft/dtslint/blob/master/docs/dt-header.md] +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped diff --git a/packages/dtslint/test/dt-header/wrong/types/default-author-name/index.d.ts.lint b/packages/dtslint/test/dt-header/wrong/types/default-author-name/index.d.ts.lint new file mode 100644 index 00000000..d7eaab7d --- /dev/null +++ b/packages/dtslint/test/dt-header/wrong/types/default-author-name/index.d.ts.lint @@ -0,0 +1,5 @@ +// Type definitions for dt-header 1.0 +// Project: https://github.com/not/important +// Definitions by: My Self +~~~~~~~~~~~~~~~~~~~~~~~~~~ [Author name should be your name, not the default. See: https://github.com/Microsoft/dtslint/blob/master/docs/dt-header.md] +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped diff --git a/packages/dtslint/test/dt-header/wrong/types/foo/index.d.ts.lint b/packages/dtslint/test/dt-header/wrong/types/foo/index.d.ts.lint new file mode 100644 index 00000000..16728faa --- /dev/null +++ b/packages/dtslint/test/dt-header/wrong/types/foo/index.d.ts.lint @@ -0,0 +1,5 @@ +// Type definitions for dt-header v1.0.3 + ~ [Error parsing header. Expected: foo MAJOR.MINOR (patch version not allowed). See: https://github.com/Microsoft/dtslint/blob/master/docs/dt-header.md] +// Project: https://github.com/bobby-headers/dt-header +// Definitions by: Jane Doe +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped diff --git a/packages/dtslint/test/dt-header/wrong/types/foo/notIndex.d.ts.lint b/packages/dtslint/test/dt-header/wrong/types/foo/notIndex.d.ts.lint new file mode 100644 index 00000000..5b2c9803 --- /dev/null +++ b/packages/dtslint/test/dt-header/wrong/types/foo/notIndex.d.ts.lint @@ -0,0 +1,2 @@ +// Type definitions for +~~~~~~~~~~~~~~~~~~~~~~~ [Header should only be in `index.d.ts` of the root. See: https://github.com/Microsoft/dtslint/blob/master/docs/dt-header.md] diff --git a/packages/dtslint/test/expect/expectError.ts.lint b/packages/dtslint/test/expect/expectError.ts.lint new file mode 100644 index 00000000..63f9770f --- /dev/null +++ b/packages/dtslint/test/expect/expectError.ts.lint @@ -0,0 +1,12 @@ +const x: string = 0; // $ExpectError + +// Handles 2 errors on 1 line +const y: string = 0 / "1"; // $ExpectError + +// $ExpectError +const z: string = ""; +~~~~~~~~~~~~~~~~~~~~~ [TypeScript@next: Expected an error on this line, but found none.] + +// $ExpectError +"0" / 1; // $ExpectError +~~~~~~~~~~~~~~~~~~~~~~~~ [TypeScript@next: This line has 2 $ExpectType assertions.] diff --git a/packages/dtslint/test/expect/expectType.ts.lint b/packages/dtslint/test/expect/expectType.ts.lint new file mode 100644 index 00000000..1d38c4c2 --- /dev/null +++ b/packages/dtslint/test/expect/expectType.ts.lint @@ -0,0 +1,30 @@ + +// $ExpectType xxx + +~nil [TypeScript@next: Can not match a node to this assertion.] + +// $ExpectType number[] +[1, 2]; + +// $ExpectType 1 +1; // $ExpectType 2 +~~~~~~~~~~~~~~~~~~~ [TypeScript@next: This line has 2 $ExpectType assertions.] + +// Do nothing for commented-out comments +// f; // $ExpectType foo + +declare function f( + one: number, + two: [number, number], + three: [number, number, number], + four: [number, number, number, number]): number; + +// Test that we never truncate types. +f; // $ExpectType (one: number, two: [number, number], three: [number, number, number], four: [number, number, number, number]) => number + +// Test that we get the type of the initializer on a variable declaration. +// $ExpectType 1 +const x = 1; + +declare const ar: readonly number[]; +ar; // $ExpectType ReadonlyArray \ No newline at end of file diff --git a/packages/dtslint/test/expect/tsconfig.json b/packages/dtslint/test/expect/tsconfig.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/packages/dtslint/test/expect/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/dtslint/test/expect/tslint.json b/packages/dtslint/test/expect/tslint.json new file mode 100644 index 00000000..e8646206 --- /dev/null +++ b/packages/dtslint/test/expect/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "expect": true + } +} diff --git a/packages/dtslint/test/export-just-namespace/ok.d.ts.lint b/packages/dtslint/test/export-just-namespace/ok.d.ts.lint new file mode 100644 index 00000000..0fd4a823 --- /dev/null +++ b/packages/dtslint/test/export-just-namespace/ok.d.ts.lint @@ -0,0 +1,5 @@ +namespace N { + export const x: number; +} +function N(): void; +export = N; diff --git a/packages/dtslint/test/export-just-namespace/test.d.ts.lint b/packages/dtslint/test/export-just-namespace/test.d.ts.lint new file mode 100644 index 00000000..7d156d9a --- /dev/null +++ b/packages/dtslint/test/export-just-namespace/test.d.ts.lint @@ -0,0 +1,5 @@ +namespace N { + export const x: number; +} +export = N; +~~~~~~~~~~~ [Instead of `export =`-ing a namespace, use the body of the namespace as the module body. See: https://github.com/Microsoft/dtslint/blob/master/docs/export-just-namespace.md] diff --git a/packages/dtslint/test/export-just-namespace/tslint.json b/packages/dtslint/test/export-just-namespace/tslint.json new file mode 100644 index 00000000..5c3ab252 --- /dev/null +++ b/packages/dtslint/test/export-just-namespace/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "export-just-namespace": true + } +} diff --git a/packages/dtslint/test/no-any-union/test.d.ts.lint b/packages/dtslint/test/no-any-union/test.d.ts.lint new file mode 100644 index 00000000..42fe4a66 --- /dev/null +++ b/packages/dtslint/test/no-any-union/test.d.ts.lint @@ -0,0 +1,4 @@ +export const x: any; + +export const y: string | any; + ~~~ [Including `any` in a union will override all other members of the union. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-any-union.md] diff --git a/packages/dtslint/test/no-any-union/tslint.json b/packages/dtslint/test/no-any-union/tslint.json new file mode 100644 index 00000000..a6f28f6d --- /dev/null +++ b/packages/dtslint/test/no-any-union/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-any-union": true + } + } diff --git a/packages/dtslint/test/no-bad-reference/decl.d.ts.lint b/packages/dtslint/test/no-bad-reference/decl.d.ts.lint new file mode 100644 index 00000000..57eb51f2 --- /dev/null +++ b/packages/dtslint/test/no-bad-reference/decl.d.ts.lint @@ -0,0 +1,4 @@ +/// + ~~~~~~ [Don't use to reference another package. Use an import or instead. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-bad-reference.md] + +/// diff --git a/packages/dtslint/test/no-bad-reference/test.ts.lint b/packages/dtslint/test/no-bad-reference/test.ts.lint new file mode 100644 index 00000000..9284936e --- /dev/null +++ b/packages/dtslint/test/no-bad-reference/test.ts.lint @@ -0,0 +1,7 @@ +/// + ~~~~~~ [0] + +/// + ~~~ [0] + +[0]: Don't use in test files. Use or include the file in 'tsconfig.json'. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-bad-reference.md diff --git a/packages/dtslint/test/no-bad-reference/tslint.json b/packages/dtslint/test/no-bad-reference/tslint.json new file mode 100644 index 00000000..6477de50 --- /dev/null +++ b/packages/dtslint/test/no-bad-reference/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-bad-reference": true + } +} diff --git a/packages/dtslint/test/no-const-enum/test.ts.lint b/packages/dtslint/test/no-const-enum/test.ts.lint new file mode 100644 index 00000000..0502fc78 --- /dev/null +++ b/packages/dtslint/test/no-const-enum/test.ts.lint @@ -0,0 +1,7 @@ +const enum E { + ~ [0] +} + +enum F {} + +[0]: Use of `const enum`s is forbidden. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-const-enum.md diff --git a/packages/dtslint/test/no-const-enum/tslint.json b/packages/dtslint/test/no-const-enum/tslint.json new file mode 100644 index 00000000..f301cb1e --- /dev/null +++ b/packages/dtslint/test/no-const-enum/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-const-enum": true + } +} diff --git a/packages/dtslint/test/no-dead-reference/test.ts.lint b/packages/dtslint/test/no-dead-reference/test.ts.lint new file mode 100644 index 00000000..8f0c3143 --- /dev/null +++ b/packages/dtslint/test/no-dead-reference/test.ts.lint @@ -0,0 +1,8 @@ +/// +import * as bar from "bar"; +/// +~~~~~~~~~~~~~~ [0] + /// + ~~~~~~~~~~~~~~ [0] + +[0]: `/// ` directive must be at top of file to take effect. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-dead-reference.md diff --git a/packages/dtslint/test/no-dead-reference/tslint.json b/packages/dtslint/test/no-dead-reference/tslint.json new file mode 100644 index 00000000..3d9bb37b --- /dev/null +++ b/packages/dtslint/test/no-dead-reference/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-dead-reference": true + } +} diff --git a/packages/dtslint/test/no-import-default-of-export-equals/bad-ambient-modules/test.d.ts.lint b/packages/dtslint/test/no-import-default-of-export-equals/bad-ambient-modules/test.d.ts.lint new file mode 100644 index 00000000..5ffb9e2f --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/bad-ambient-modules/test.d.ts.lint @@ -0,0 +1,11 @@ +declare module "a" { + interface I {} + export = I; +} + +declare module "b" { + import a from "a"; + ~ [0] +} + +[0]: The module "a" uses `export = `. Import with `import a = require("a")`. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-import-default-of-export-equals.md diff --git a/packages/dtslint/test/no-import-default-of-export-equals/bad-ambient-modules/tsconfig.json b/packages/dtslint/test/no-import-default-of-export-equals/bad-ambient-modules/tsconfig.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/bad-ambient-modules/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/dtslint/test/no-import-default-of-export-equals/bad-ambient-modules/tslint.json b/packages/dtslint/test/no-import-default-of-export-equals/bad-ambient-modules/tslint.json new file mode 100644 index 00000000..f4f43559 --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/bad-ambient-modules/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../../bin/rules"], + "rules": { + "no-import-default-of-export-equals": true + } +} diff --git a/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/a.d.ts b/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/a.d.ts new file mode 100644 index 00000000..c579e034 --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/a.d.ts @@ -0,0 +1,2 @@ +declare function foo(): void; +export = foo; \ No newline at end of file diff --git a/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/b.d.ts.lint b/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/b.d.ts.lint new file mode 100644 index 00000000..4574b26f --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/b.d.ts.lint @@ -0,0 +1,4 @@ +import D from "./a"; + ~ [0] + +[0]: The module "./a" uses `export = `. Import with `import D = require("./a")`. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-import-default-of-export-equals.md \ No newline at end of file diff --git a/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/tsconfig.json b/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/tsconfig.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/tslint.json b/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/tslint.json new file mode 100644 index 00000000..42c7451f --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/bad-external-modules/tslint.json @@ -0,0 +1,7 @@ +{ + "rulesDirectory": ["../../../bin/rules"], + "rules": { + "no-import-default-of-export-equals": true + } + } + \ No newline at end of file diff --git a/packages/dtslint/test/no-import-default-of-export-equals/good-ambient-modules/test.d.ts.lint b/packages/dtslint/test/no-import-default-of-export-equals/good-ambient-modules/test.d.ts.lint new file mode 100644 index 00000000..8048c8f5 --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/good-ambient-modules/test.d.ts.lint @@ -0,0 +1,9 @@ +declare module "a" { + interface I {} + export default I; +} + +declare module "b" { + import a from "a"; +} + diff --git a/packages/dtslint/test/no-import-default-of-export-equals/good-ambient-modules/tsconfig.json b/packages/dtslint/test/no-import-default-of-export-equals/good-ambient-modules/tsconfig.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/good-ambient-modules/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/dtslint/test/no-import-default-of-export-equals/good-ambient-modules/tslint.json b/packages/dtslint/test/no-import-default-of-export-equals/good-ambient-modules/tslint.json new file mode 100644 index 00000000..f4f43559 --- /dev/null +++ b/packages/dtslint/test/no-import-default-of-export-equals/good-ambient-modules/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../../bin/rules"], + "rules": { + "no-import-default-of-export-equals": true + } +} diff --git a/packages/dtslint/test/no-padding/test.ts.lint b/packages/dtslint/test/no-padding/test.ts.lint new file mode 100644 index 00000000..9bc51b48 --- /dev/null +++ b/packages/dtslint/test/no-padding/test.ts.lint @@ -0,0 +1,29 @@ +function f() { + ~ [0 % ("after '{'")] + + return [ + ~ [0 % ("after '['")] + + f( + ~ [0 % ("after '('")] + + 0 + + ) + ~ [0 % ("before ')'")] + + ]; + ~ [0 % ("before ']'")] + +} +~ [0 % ("before '}'")] + +function f() { + return [ + f( + 0 + ) + ]; +} + +[0]: Don't leave a blank line %s. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-padding.md \ No newline at end of file diff --git a/packages/dtslint/test/no-padding/tslint.json b/packages/dtslint/test/no-padding/tslint.json new file mode 100644 index 00000000..e8b7989d --- /dev/null +++ b/packages/dtslint/test/no-padding/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-padding": true + } +} diff --git a/packages/dtslint/test/no-redundant-jsdoc2/test.ts.lint b/packages/dtslint/test/no-redundant-jsdoc2/test.ts.lint new file mode 100644 index 00000000..d048a649 --- /dev/null +++ b/packages/dtslint/test/no-redundant-jsdoc2/test.ts.lint @@ -0,0 +1,24 @@ +/** @deprecated */ +export const x: number; +/** @see x */ +export const y: number; +/** @author look, nobody can remember the format for this thing */ +export const z: number; +/** @private */ + ~~~~~~~ [JSDoc tag '@private' is redundant in TypeScript code.] +export const soHidden: number; +/** @protected */ + ~~~~~~~~~ [JSDoc tag '@protected' is redundant in TypeScript code.] +export const muchSecurity: number; +/** @readonly */ + ~~~~~~~~ [JSDoc tag '@readonly' is redundant in TypeScript code.] +export const wow: number; +/** @implements {Glorfindel} + ~~~~~~~~~~ [JSDoc tag '@implements' is redundant in TypeScript code.] + * narrator: it doesn't. + */ +class Yubatyu { +} + + + diff --git a/packages/dtslint/test/no-redundant-jsdoc2/tslint.json b/packages/dtslint/test/no-redundant-jsdoc2/tslint.json new file mode 100644 index 00000000..222912b6 --- /dev/null +++ b/packages/dtslint/test/no-redundant-jsdoc2/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-redundant-jsdoc2": true + } +} diff --git a/packages/dtslint/test/no-relative-import-in-test/decl.d.ts.lint b/packages/dtslint/test/no-relative-import-in-test/decl.d.ts.lint new file mode 100644 index 00000000..ff7e1890 --- /dev/null +++ b/packages/dtslint/test/no-relative-import-in-test/decl.d.ts.lint @@ -0,0 +1 @@ +import { x } from "./declarationFile.d"; diff --git a/packages/dtslint/test/no-relative-import-in-test/declarationFile.d.ts b/packages/dtslint/test/no-relative-import-in-test/declarationFile.d.ts new file mode 100644 index 00000000..226fa6a5 --- /dev/null +++ b/packages/dtslint/test/no-relative-import-in-test/declarationFile.d.ts @@ -0,0 +1 @@ +export const x: number; diff --git a/packages/dtslint/test/no-relative-import-in-test/test.ts.lint b/packages/dtslint/test/no-relative-import-in-test/test.ts.lint new file mode 100644 index 00000000..d7f55fc4 --- /dev/null +++ b/packages/dtslint/test/no-relative-import-in-test/test.ts.lint @@ -0,0 +1,5 @@ +import { x } from "./declarationFile.d"; + ~~~~~~~~~~~~~~~~~~~~~ [0] +import { y } from "./testFile"; + +[0]: Test file should not use a relative import. Use a global import as if this were a user of the package. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-relative-import-in-test.md diff --git a/packages/dtslint/test/no-relative-import-in-test/testFile.ts b/packages/dtslint/test/no-relative-import-in-test/testFile.ts new file mode 100644 index 00000000..3fa9c2d2 --- /dev/null +++ b/packages/dtslint/test/no-relative-import-in-test/testFile.ts @@ -0,0 +1 @@ +export const y = 0; diff --git a/packages/dtslint/test/no-relative-import-in-test/tsconfig.json b/packages/dtslint/test/no-relative-import-in-test/tsconfig.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/packages/dtslint/test/no-relative-import-in-test/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/dtslint/test/no-relative-import-in-test/tslint.json b/packages/dtslint/test/no-relative-import-in-test/tslint.json new file mode 100644 index 00000000..66391a9a --- /dev/null +++ b/packages/dtslint/test/no-relative-import-in-test/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-relative-import-in-test": true + } +} diff --git a/packages/dtslint/test/no-single-declare-module/augmentation.d.ts.lint b/packages/dtslint/test/no-single-declare-module/augmentation.d.ts.lint new file mode 100644 index 00000000..751d5e6d --- /dev/null +++ b/packages/dtslint/test/no-single-declare-module/augmentation.d.ts.lint @@ -0,0 +1,3 @@ +import x from "x"; + +declare module "foo" {} diff --git a/packages/dtslint/test/no-single-declare-module/multiple.d.ts.lint b/packages/dtslint/test/no-single-declare-module/multiple.d.ts.lint new file mode 100644 index 00000000..6ddb31fe --- /dev/null +++ b/packages/dtslint/test/no-single-declare-module/multiple.d.ts.lint @@ -0,0 +1,2 @@ +declare module "foo" {} +declare module "bar" {} diff --git a/packages/dtslint/test/no-single-declare-module/tslint.json b/packages/dtslint/test/no-single-declare-module/tslint.json new file mode 100644 index 00000000..9da0ac73 --- /dev/null +++ b/packages/dtslint/test/no-single-declare-module/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-single-declare-module": true + } +} diff --git a/packages/dtslint/test/no-single-declare-module/wildcard.d.ts.lint b/packages/dtslint/test/no-single-declare-module/wildcard.d.ts.lint new file mode 100644 index 00000000..794d08e5 --- /dev/null +++ b/packages/dtslint/test/no-single-declare-module/wildcard.d.ts.lint @@ -0,0 +1 @@ +declare module "*.svg" {} diff --git a/packages/dtslint/test/no-single-declare-module/wrong.d.ts.lint b/packages/dtslint/test/no-single-declare-module/wrong.d.ts.lint new file mode 100644 index 00000000..f4c50f30 --- /dev/null +++ b/packages/dtslint/test/no-single-declare-module/wrong.d.ts.lint @@ -0,0 +1,5 @@ +declare module "foo" {} +~~~~~~~~~~~~~~~~~~~~~~~ [File has only 1 ambient module declaration. Move the contents outside the ambient module block, rename the file to match the ambient module name, and remove the block. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-single-declare-module.md] + +// Other global declarations don't affect this. They should go in "declare global". +interface I {} diff --git a/packages/dtslint/test/no-single-element-tuple-type/test.ts.lint b/packages/dtslint/test/no-single-element-tuple-type/test.ts.lint new file mode 100644 index 00000000..913f1ee6 --- /dev/null +++ b/packages/dtslint/test/no-single-element-tuple-type/test.ts.lint @@ -0,0 +1,5 @@ +const x: [string]; + ~~~~~~~~ [0] +const y: [string, number]; + +[0]: Type [T] is a single-element tuple type. You probably meant T[]. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-single-element-tuple-type.md diff --git a/packages/dtslint/test/no-single-element-tuple-type/tslint.json b/packages/dtslint/test/no-single-element-tuple-type/tslint.json new file mode 100644 index 00000000..07abf62e --- /dev/null +++ b/packages/dtslint/test/no-single-element-tuple-type/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-single-element-tuple-type": true + } + } diff --git a/packages/dtslint/test/no-unnecessary-generics/test.ts.lint b/packages/dtslint/test/no-unnecessary-generics/test.ts.lint new file mode 100644 index 00000000..b507b37b --- /dev/null +++ b/packages/dtslint/test/no-unnecessary-generics/test.ts.lint @@ -0,0 +1,38 @@ +interface I { + (value: T): void; + ~ [0] + m(x: T): void; + ~ [0] +} + +class C { + constructor(x: T) {} + ~ [0] +} + +type Fn = () => T; + ~ [0] +type Ctr = new() => T; + ~ [0] + +function f(): T { } + ~ [0] + +const f = function(): T {}; + ~ [0] +const f2 = (): T => {}; + ~ [0] + +function f(x: { T: number }): void; + ~ [Type parameter T is never used. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-unnecessary-generics.md] + +function f(u: U): U; + ~ [0] + +// OK: +// Uses type parameter twice +function foo(m: Map): void {} +// `T` appears in a constraint, so it appears twice. +function f(t: T, u: U): U; + +[0]: Type parameter T is used only once. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-unnecessary-generics.md diff --git a/packages/dtslint/test/no-unnecessary-generics/tsconfig.json b/packages/dtslint/test/no-unnecessary-generics/tsconfig.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/packages/dtslint/test/no-unnecessary-generics/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/dtslint/test/no-unnecessary-generics/tslint.json b/packages/dtslint/test/no-unnecessary-generics/tslint.json new file mode 100644 index 00000000..dd1b74a5 --- /dev/null +++ b/packages/dtslint/test/no-unnecessary-generics/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-unnecessary-generics": true + } +} diff --git a/packages/dtslint/test/no-useless-files/ok.ts.tlint b/packages/dtslint/test/no-useless-files/ok.ts.tlint new file mode 100644 index 00000000..28db87dd --- /dev/null +++ b/packages/dtslint/test/no-useless-files/ok.ts.tlint @@ -0,0 +1 @@ +export default "I am useful"; diff --git a/packages/dtslint/test/no-useless-files/test.ts.lint b/packages/dtslint/test/no-useless-files/test.ts.lint new file mode 100644 index 00000000..2b042bac --- /dev/null +++ b/packages/dtslint/test/no-useless-files/test.ts.lint @@ -0,0 +1,2 @@ +// I am useless +~ [File has no content. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-useless-files.md] diff --git a/packages/dtslint/test/no-useless-files/tslint.json b/packages/dtslint/test/no-useless-files/tslint.json new file mode 100644 index 00000000..91da4fd1 --- /dev/null +++ b/packages/dtslint/test/no-useless-files/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "no-useless-files": true + } +} diff --git a/packages/dtslint/test/npm-naming/code/tslint.json b/packages/dtslint/test/npm-naming/code/tslint.json new file mode 100644 index 00000000..b10460bf --- /dev/null +++ b/packages/dtslint/test/npm-naming/code/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../../bin/rules"], + "rules": { + "npm-naming": [true, {"mode": "code", "errors": [["NeedsExportEquals", true]], "single-line": true }] + } + } \ No newline at end of file diff --git a/packages/dtslint/test/npm-naming/code/types/dts-critic/index.d.ts b/packages/dtslint/test/npm-naming/code/types/dts-critic/index.d.ts new file mode 100644 index 00000000..ea974773 --- /dev/null +++ b/packages/dtslint/test/npm-naming/code/types/dts-critic/index.d.ts @@ -0,0 +1,6 @@ +// Type definitions for package dts-critic 1.0 +// Project: https://https://github.com/DefinitelyTyped/dts-critic +// Definitions by: Jane Doe +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +export default dtsCritic(); \ No newline at end of file diff --git a/packages/dtslint/test/npm-naming/code/types/dts-critic/index.d.ts.lint b/packages/dtslint/test/npm-naming/code/types/dts-critic/index.d.ts.lint new file mode 100644 index 00000000..10c79ecb --- /dev/null +++ b/packages/dtslint/test/npm-naming/code/types/dts-critic/index.d.ts.lint @@ -0,0 +1,8 @@ +// Type definitions for package dts-critic 1.0 +~ [0] +// Project: https://https://github.com/DefinitelyTyped/dts-critic +// Definitions by: Jane Doe +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +export default dtsCritic(); +[0]: The declaration doesn't match the JavaScript module 'dts-critic'. Reason: The declaration should use 'export =' syntax because the JavaScript source uses 'module.exports =' syntax and 'module.exports' can be called or constructed. To learn more about 'export =' syntax, see https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require. If you won't fix this error now or you think this error is wrong, you can disable this check by adding the following options to your project's tslint.json file under "rules": "npm-naming": [true,{"mode":"code","errors":[["NeedsExportEquals",false]]}] See: https://github.com/Microsoft/dtslint/blob/master/docs/npm-naming.md \ No newline at end of file diff --git a/packages/dtslint/test/npm-naming/name/tslint.json b/packages/dtslint/test/npm-naming/name/tslint.json new file mode 100644 index 00000000..7af8562f --- /dev/null +++ b/packages/dtslint/test/npm-naming/name/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../../bin/rules"], + "rules": { + "npm-naming": [true, {"mode": "name-only", "single-line": true}] + } +} \ No newline at end of file diff --git a/packages/dtslint/test/npm-naming/name/types/parseltongue/index.d.ts b/packages/dtslint/test/npm-naming/name/types/parseltongue/index.d.ts new file mode 100644 index 00000000..4df6c8e3 --- /dev/null +++ b/packages/dtslint/test/npm-naming/name/types/parseltongue/index.d.ts @@ -0,0 +1,4 @@ +// Type definitions for package parseltongue 1.0 +// Project: https://github.com/bobby-headers/dt-header +// Definitions by: Jane Doe +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped \ No newline at end of file diff --git a/packages/dtslint/test/npm-naming/name/types/parseltongue/index.d.ts.lint b/packages/dtslint/test/npm-naming/name/types/parseltongue/index.d.ts.lint new file mode 100644 index 00000000..75c67716 --- /dev/null +++ b/packages/dtslint/test/npm-naming/name/types/parseltongue/index.d.ts.lint @@ -0,0 +1,6 @@ +// Type definitions for package parseltongue 1.0 +~~~~~~~~~~~~~~~~~~~~~~~ [0] +// Project: https://github.com/bobby-headers/dt-header +// Definitions by: Jane Doe +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +[0]: Declaration file must have a matching npm package. To resolve this error, either: 1. Change the name to match an npm package. 2. Add a Definitely Typed header with the first line // Type definitions for non-npm package parseltongue-browser Add -browser to the end of your name to make sure it doesn't conflict with existing npm packages. If you won't fix this error now or you think this error is wrong, you can disable this check by adding the following options to your project's tslint.json file under "rules": "npm-naming": false See: https://github.com/Microsoft/dtslint/blob/master/docs/npm-naming.md \ No newline at end of file diff --git a/packages/dtslint/test/prefer-declare-function/test.d.ts.lint b/packages/dtslint/test/prefer-declare-function/test.d.ts.lint new file mode 100644 index 00000000..4c78d174 --- /dev/null +++ b/packages/dtslint/test/prefer-declare-function/test.d.ts.lint @@ -0,0 +1,10 @@ +export const x: () => void; + ~~~~~~~~~~~~~ [0] + +namespace N { + const x: () => void; + ~~~~~~~~~~~~~ [0] +} + + +[0]: Use a function declaration instead of a variable of function type. See: https://github.com/Microsoft/dtslint/blob/master/docs/prefer-declare-function.md diff --git a/packages/dtslint/test/prefer-declare-function/tslint.json b/packages/dtslint/test/prefer-declare-function/tslint.json new file mode 100644 index 00000000..0e67bed5 --- /dev/null +++ b/packages/dtslint/test/prefer-declare-function/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "prefer-declare-function": true + } +} diff --git a/packages/dtslint/test/redundant-undefined/test.ts.lint b/packages/dtslint/test/redundant-undefined/test.ts.lint new file mode 100644 index 00000000..955e59e5 --- /dev/null +++ b/packages/dtslint/test/redundant-undefined/test.ts.lint @@ -0,0 +1,10 @@ +function f(s?: string | undefined): void {} + ~~~~~~~~~ [0] + +interface I { + ok?: string | undefined; + s?: string; + almost?: number | string; +} + +[0]: Parameter is optional, so no need to include `undefined` in the type. See: https://github.com/Microsoft/dtslint/blob/master/docs/redundant-undefined.md diff --git a/packages/dtslint/test/redundant-undefined/tslint.json b/packages/dtslint/test/redundant-undefined/tslint.json new file mode 100644 index 00000000..af6c494d --- /dev/null +++ b/packages/dtslint/test/redundant-undefined/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "redundant-undefined": true + } +} diff --git a/packages/dtslint/test/strict-export-declare-modifiers/testAmbient.d.ts.lint b/packages/dtslint/test/strict-export-declare-modifiers/testAmbient.d.ts.lint new file mode 100644 index 00000000..14a97e4a --- /dev/null +++ b/packages/dtslint/test/strict-export-declare-modifiers/testAmbient.d.ts.lint @@ -0,0 +1,24 @@ +declare function f(): void; + +declare type T = number; +~~~~~~~ [declare-redundant] + +declare interface I {} +~~~~~~~ [declare-redundant] + +declare module "m" { + export function f(): void; + ~~~~~~ [export-redundant] + function g(): void; + export default function h(); +} + +declare module "m2" { + export {}; + export function f(): void; + function g(): void; +} + + +[declare-redundant]: 'declare' keyword is redundant here. See: https://github.com/Microsoft/dtslint/blob/master/docs/strict-export-declare-modifiers.md +[export-redundant]: 'export' keyword is redundant here because all declarations in this module are exported automatically. If you have a good reason to export some declarations and not others, add 'export {}' to the module to shut off automatic exporting. See: https://github.com/Microsoft/dtslint/blob/master/docs/strict-export-declare-modifiers.md diff --git a/packages/dtslint/test/strict-export-declare-modifiers/testModule.d.ts.lint b/packages/dtslint/test/strict-export-declare-modifiers/testModule.d.ts.lint new file mode 100644 index 00000000..4c5ef312 --- /dev/null +++ b/packages/dtslint/test/strict-export-declare-modifiers/testModule.d.ts.lint @@ -0,0 +1,23 @@ +import * as foo from "foo"; +import foo = require("foo"); +export { foo }; +export { foo } from "foo"; +export as namespace Foo; + +interface I {} + +export declare function f(): void; + ~~~~~~~ [declare-redundant] + +declare function g(): void; + +declare namespace N {} + +export namespace M { + export function f(): void; + ~~~~~~ [export-redundant] + // TS compiler warns for 'declare' here. +} + +[declare-redundant]: 'declare' keyword is redundant here. See: https://github.com/Microsoft/dtslint/blob/master/docs/strict-export-declare-modifiers.md +[export-redundant]: 'export' keyword is redundant here because all declarations in this module are exported automatically. If you have a good reason to export some declarations and not others, add 'export {}' to the module to shut off automatic exporting. See: https://github.com/Microsoft/dtslint/blob/master/docs/strict-export-declare-modifiers.md \ No newline at end of file diff --git a/packages/dtslint/test/strict-export-declare-modifiers/testModuleAutoExport.d.ts.lint b/packages/dtslint/test/strict-export-declare-modifiers/testModuleAutoExport.d.ts.lint new file mode 100644 index 00000000..8edf9bbc --- /dev/null +++ b/packages/dtslint/test/strict-export-declare-modifiers/testModuleAutoExport.d.ts.lint @@ -0,0 +1,27 @@ +import * as foo from "foo"; +import foo = require("foo"); +export as namespace Foo; + +interface I {} + ~ [export-preferred] + +export declare function f(): void; + ~~~~~~~ [declare] + +declare function g(): void; +~~~~~~~ [declare] + ~ [export-preferred] + +declare namespace N {} +~~~~~~~ [declare] + ~ [export-preferred] + +export namespace M { + export function f(): void; + ~~~~~~ [export-redundant] + // TS compiler warns for 'declare' here. +} + +[declare]: 'declare' keyword is redundant here. See: https://github.com/Microsoft/dtslint/blob/master/docs/strict-export-declare-modifiers.md +[export-preferred]: All declarations in this module are exported automatically. Prefer to explicitly write 'export' for clarity. If you have a good reason not to export this declaration, add 'export {}' to the module to shut off automatic exporting. See: https://github.com/Microsoft/dtslint/blob/master/docs/strict-export-declare-modifiers.md +[export-redundant]: 'export' keyword is redundant here because all declarations in this module are exported automatically. If you have a good reason to export some declarations and not others, add 'export {}' to the module to shut off automatic exporting. See: https://github.com/Microsoft/dtslint/blob/master/docs/strict-export-declare-modifiers.md \ No newline at end of file diff --git a/packages/dtslint/test/strict-export-declare-modifiers/testModuleTs.ts.lint b/packages/dtslint/test/strict-export-declare-modifiers/testModuleTs.ts.lint new file mode 100644 index 00000000..9f621021 --- /dev/null +++ b/packages/dtslint/test/strict-export-declare-modifiers/testModuleTs.ts.lint @@ -0,0 +1,20 @@ +export declare class C {} + +export function f() {} + +declare interface I {} +~~~~~~~ [declare-redundant] + +interface J {} + +namespace N { + export const x: number; +} + +declare namespace M { + export const x: number; + ~~~~~~ [export-redundant] +} + +[declare-redundant]: 'declare' keyword is redundant here. See: https://github.com/Microsoft/dtslint/blob/master/docs/strict-export-declare-modifiers.md +[export-redundant]: 'export' keyword is redundant here because all declarations in this module are exported automatically. If you have a good reason to export some declarations and not others, add 'export {}' to the module to shut off automatic exporting. See: https://github.com/Microsoft/dtslint/blob/master/docs/strict-export-declare-modifiers.md diff --git a/packages/dtslint/test/strict-export-declare-modifiers/testValues.d.ts.lint b/packages/dtslint/test/strict-export-declare-modifiers/testValues.d.ts.lint new file mode 100644 index 00000000..1015779d --- /dev/null +++ b/packages/dtslint/test/strict-export-declare-modifiers/testValues.d.ts.lint @@ -0,0 +1,2 @@ +declare class Foo {} +export { Foo as Bar } \ No newline at end of file diff --git a/packages/dtslint/test/strict-export-declare-modifiers/tslint.json b/packages/dtslint/test/strict-export-declare-modifiers/tslint.json new file mode 100644 index 00000000..466c95a0 --- /dev/null +++ b/packages/dtslint/test/strict-export-declare-modifiers/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "strict-export-declare-modifiers": true + } +} diff --git a/packages/dtslint/test/test.js b/packages/dtslint/test/test.js new file mode 100755 index 00000000..42167065 --- /dev/null +++ b/packages/dtslint/test/test.js @@ -0,0 +1,26 @@ +#! /usr/bin/env node +const { join: joinPaths } = require("path"); +const { consoleTestResultHandler, runTest } = require("tslint/lib/test"); +const { existsSync, readdirSync } = require("fs"); + +const testDir = __dirname; + +const tests = readdirSync(testDir).filter(x => x !== "test.js"); + +for (const testName of tests) { + const testDirectory = joinPaths(testDir, testName); + if (existsSync(joinPaths(testDirectory, "tslint.json"))) { + testSingle(testDirectory); + } else { + for (const subTestName of readdirSync(testDirectory)) { + testSingle(joinPaths(testDirectory, subTestName)); + } + } +} + +function testSingle(testDirectory) { + const result = runTest(testDirectory); + if (!consoleTestResultHandler(result, /*logger*/ console)) { + process.exit(1); + } +} diff --git a/packages/dtslint/test/trim-file/test.ts.lint b/packages/dtslint/test/trim-file/test.ts.lint new file mode 100644 index 00000000..53fa74f6 --- /dev/null +++ b/packages/dtslint/test/trim-file/test.ts.lint @@ -0,0 +1,5 @@ + +~nil [File should not begin with a blank line. See: https://github.com/Microsoft/dtslint/blob/master/docs/trim-file.md] +0; + +~nil [File should not end with a blank line. (Ending in one newline OK, ending in two newlines not OK.) See: https://github.com/Microsoft/dtslint/blob/master/docs/trim-file.md] diff --git a/packages/dtslint/test/trim-file/tslint.json b/packages/dtslint/test/trim-file/tslint.json new file mode 100644 index 00000000..92f3f78e --- /dev/null +++ b/packages/dtslint/test/trim-file/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "trim-file": true + } +} diff --git a/packages/dtslint/test/void-return/test.ts.lint b/packages/dtslint/test/void-return/test.ts.lint new file mode 100644 index 00000000..f0897517 --- /dev/null +++ b/packages/dtslint/test/void-return/test.ts.lint @@ -0,0 +1,14 @@ +function f(): void; +function f(): number | void; +function f(): Promise; +function f(): T; +function f(action: () => void): void; + +function f(x: void): number; + ~~~~ [0] +function f(x: number | void): number; + ~~~~ [0] + +f(); + +[0]: Use the `void` type for return types only. Otherwise, use `undefined`. See: https://github.com/Microsoft/dtslint/blob/master/docs/void-return.md diff --git a/packages/dtslint/test/void-return/tslint.json b/packages/dtslint/test/void-return/tslint.json new file mode 100644 index 00000000..6f223be6 --- /dev/null +++ b/packages/dtslint/test/void-return/tslint.json @@ -0,0 +1,6 @@ +{ + "rulesDirectory": ["../../bin/rules"], + "rules": { + "void-return": true + } +} diff --git a/packages/dtslint/tsconfig.json b/packages/dtslint/tsconfig.json new file mode 100644 index 00000000..218f2881 --- /dev/null +++ b/packages/dtslint/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "lib": ["es2017"], + "outDir": "bin", + "sourceMap": true, + "newLine": "lf", + + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictNullChecks": true, + "esModuleInterop": true + }, + "include": ["src"] +} diff --git a/packages/dtslint/tslint.json b/packages/dtslint/tslint.json new file mode 100644 index 00000000..8259bc81 --- /dev/null +++ b/packages/dtslint/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "tslint:latest", + "rules": { + "arrow-parens": [true, "ban-single-arg-parens"], + "indent": [true, "spaces"], + "interface-name": [true, "never-prefix"], + "max-line-length": [true, 130], + "member-access": [true, "no-public"], + "variable-name": [true, "check-format", "allow-leading-underscore"], + "curly": false, + + "no-console": false, + "no-namespace": false, + "object-literal-sort-keys": false, + "switch-default": false + } +} diff --git a/yarn.lock b/yarn.lock index 0eb853bd..f43f33ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -200,6 +200,13 @@ dependencies: "@babel/highlight" "^7.14.5" +"@babel/code-frame@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" + integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== + dependencies: + "@babel/highlight" "^7.16.0" + "@babel/code-frame@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" @@ -229,6 +236,15 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/generator@^7.16.0", "@babel/generator@^7.4.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2" + integrity sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew== + dependencies: + "@babel/types" "^7.16.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" @@ -239,6 +255,15 @@ lodash "^4.17.13" source-map "^0.5.0" +"@babel/helper-function-name@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481" + integrity sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog== + dependencies: + "@babel/helper-get-function-arity" "^7.16.0" + "@babel/template" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/helper-function-name@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" @@ -248,6 +273,13 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.9.5" +"@babel/helper-get-function-arity@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa" + integrity sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" @@ -255,6 +287,13 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-hoist-variables@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" + integrity sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-member-expression-to-functions@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" @@ -312,6 +351,13 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/helper-split-export-declaration@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438" + integrity sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" @@ -338,7 +384,7 @@ "@babel/traverse" "^7.9.6" "@babel/types" "^7.9.6" -"@babel/highlight@^7.10.4": +"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g== @@ -361,6 +407,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== +"@babel/parser@^7.16.0", "@babel/parser@^7.16.3", "@babel/parser@^7.4.3": + version "7.16.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" + integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -410,7 +461,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-object-rest-spread@^7.8.3": +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== @@ -431,6 +482,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/template@^7.16.0", "@babel/template@^7.4.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" + integrity sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A== + dependencies: + "@babel/code-frame" "^7.16.0" + "@babel/parser" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" @@ -455,6 +515,21 @@ globals "^11.1.0" lodash "^4.17.13" +"@babel/traverse@^7.4.3": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787" + integrity sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag== + dependencies: + "@babel/code-frame" "^7.16.0" + "@babel/generator" "^7.16.0" + "@babel/helper-function-name" "^7.16.0" + "@babel/helper-hoist-variables" "^7.16.0" + "@babel/helper-split-export-declaration" "^7.16.0" + "@babel/parser" "^7.16.3" + "@babel/types" "^7.16.0" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" @@ -464,6 +539,14 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.16.0", "@babel/types@^7.4.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" + integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== + dependencies: + "@babel/helper-validator-identifier" "^7.15.7" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -477,15 +560,6 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@definitelytyped/header-parser@0.0.34": - version "0.0.34" - resolved "https://registry.yarnpkg.com/@definitelytyped/header-parser/-/header-parser-0.0.34.tgz#892c83ae0cdbc52ee439dfc3ac34244903b8a5ee" - integrity sha512-/yTifMAhYKB8SFH3pSlAQmcBzrk7UyqpEz9/vJKaMKdzRpJrxmc1zWMP+hwJtJTVCjAK+Ul4m3i1GZQrTZfymw== - dependencies: - "@definitelytyped/typescript-versions" "^0.0.34" - "@types/parsimmon" "^1.10.1" - parsimmon "^1.13.0" - "@definitelytyped/header-parser@latest": version "0.0.88" resolved "https://registry.yarnpkg.com/@definitelytyped/header-parser/-/header-parser-0.0.88.tgz#0c24c162ef5eb9439580ada987bb7399140f81aa" @@ -495,30 +569,11 @@ "@types/parsimmon" "^1.10.1" parsimmon "^1.13.0" -"@definitelytyped/typescript-versions@^0.0.34": - version "0.0.34" - resolved "https://registry.yarnpkg.com/@definitelytyped/typescript-versions/-/typescript-versions-0.0.34.tgz#6167363d378670ad7ef9485b7cff7d198106dcdf" - integrity sha512-7IqWcbHKYbfY8Lt7AigXDa29cbz3gynzBHMjwMUCeLnex8D682M6OW8uBLouvVHCr+YENL58tQB3dn0Zos8mFQ== - -"@definitelytyped/typescript-versions@^0.0.88", "@definitelytyped/typescript-versions@latest": +"@definitelytyped/typescript-versions@^0.0.88": version "0.0.88" resolved "https://registry.yarnpkg.com/@definitelytyped/typescript-versions/-/typescript-versions-0.0.88.tgz#2b8e5efe6e1b72f5e99f40695c42142516225b89" integrity sha512-UjZagFi/oi+8XaT1DSZBYgmT8MR4FlnRG3O9vV83KgX3jI6OlvNZTsqG/r+E5zbydLgxzaUKw+wFaq13sDheeA== -"@definitelytyped/utils@latest": - version "0.0.88" - resolved "https://registry.yarnpkg.com/@definitelytyped/utils/-/utils-0.0.88.tgz#7f5efd84a390d649e56e358ef912443ea84101ed" - integrity sha512-c6Q++56EMq7/oaruSu/mI+12Yczglz0hSdeQT/oSUBI/0He8t/b7frR5RM+64FabailHYP9HvWS6hm9ReTwE1Q== - dependencies: - "@definitelytyped/typescript-versions" "^0.0.88" - "@qiwi/npm-registry-client" "^8.9.1" - "@types/node" "^14.14.35" - charm "^1.0.2" - fs-extra "^8.1.0" - fstream "^1.0.12" - tar "^2.2.2" - tar-stream "^2.1.4" - "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -573,6 +628,15 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== +"@jest/console@^24.7.1", "@jest/console@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" + integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== + dependencies: + "@jest/source-map" "^24.9.0" + chalk "^2.0.1" + slash "^2.0.0" + "@jest/console@^25.5.0": version "25.5.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.5.0.tgz#770800799d510f37329c508a9edd0b7b447d9abb" @@ -584,6 +648,40 @@ jest-util "^25.5.0" slash "^3.0.0" +"@jest/core@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" + integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== + dependencies: + "@jest/console" "^24.7.1" + "@jest/reporters" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-changed-files "^24.9.0" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-resolve-dependencies "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + jest-watcher "^24.9.0" + micromatch "^3.1.10" + p-each-series "^1.0.0" + realpath-native "^1.1.0" + rimraf "^2.5.4" + slash "^2.0.0" + strip-ansi "^5.0.0" + "@jest/core@^25.5.4": version "25.5.4" resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.5.4.tgz#3ef7412f7339210f003cdf36646bbca786efe7b4" @@ -618,6 +716,16 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/environment@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" + integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== + dependencies: + "@jest/fake-timers" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + "@jest/environment@^25.5.0": version "25.5.0" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.5.0.tgz#aa33b0c21a716c65686638e7ef816c0e3a0c7b37" @@ -627,6 +735,15 @@ "@jest/types" "^25.5.0" jest-mock "^25.5.0" +"@jest/fake-timers@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" + integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== + dependencies: + "@jest/types" "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + "@jest/fake-timers@^25.5.0": version "25.5.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.5.0.tgz#46352e00533c024c90c2bc2ad9f2959f7f114185" @@ -647,6 +764,33 @@ "@jest/types" "^25.5.0" expect "^25.5.0" +"@jest/reporters@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" + integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.2" + istanbul-lib-coverage "^2.0.2" + istanbul-lib-instrument "^3.0.1" + istanbul-lib-report "^2.0.4" + istanbul-lib-source-maps "^3.0.1" + istanbul-reports "^2.2.6" + jest-haste-map "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + node-notifier "^5.4.2" + slash "^2.0.0" + source-map "^0.6.0" + string-length "^2.0.0" + "@jest/reporters@^25.5.1": version "25.5.1" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.5.1.tgz#cb686bcc680f664c2dbaf7ed873e93aa6811538b" @@ -679,6 +823,15 @@ optionalDependencies: node-notifier "^6.0.0" +"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" + integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + "@jest/source-map@^25.5.0": version "25.5.0" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.5.0.tgz#df5c20d6050aa292c2c6d3f0d2c7606af315bd1b" @@ -688,6 +841,15 @@ graceful-fs "^4.2.4" source-map "^0.6.0" +"@jest/test-result@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" + integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== + dependencies: + "@jest/console" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@jest/test-result@^25.5.0": version "25.5.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.5.0.tgz#139a043230cdeffe9ba2d8341b27f2efc77ce87c" @@ -698,6 +860,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-sequencer@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" + integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== + dependencies: + "@jest/test-result" "^24.9.0" + jest-haste-map "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + "@jest/test-sequencer@^25.5.4": version "25.5.4" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.5.4.tgz#9b4e685b36954c38d0f052e596d28161bdc8b737" @@ -709,6 +881,28 @@ jest-runner "^25.5.4" jest-runtime "^25.5.4" +"@jest/transform@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" + integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^24.9.0" + babel-plugin-istanbul "^5.1.0" + chalk "^2.0.1" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.15" + jest-haste-map "^24.9.0" + jest-regex-util "^24.9.0" + jest-util "^24.9.0" + micromatch "^3.1.10" + pirates "^4.0.1" + realpath-native "^1.1.0" + slash "^2.0.0" + source-map "^0.6.1" + write-file-atomic "2.4.1" + "@jest/transform@^25.5.1": version "25.5.1" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.5.1.tgz#0469ddc17699dd2bf985db55fa0fb9309f5c2db3" @@ -731,6 +925,15 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/types@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" + integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^13.0.0" + "@jest/types@^25.5.0": version "25.5.0" resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" @@ -1728,6 +1931,17 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@types/babel__core@^7.1.0": + version "7.1.16" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.16.tgz#bc12c74b7d65e82d29876b5d0baf5c625ac58702" + integrity sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + "@types/babel__core@^7.1.7": version "7.1.7" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" @@ -1768,11 +1982,28 @@ dependencies: "@types/node" "*" +"@types/command-exists@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/command-exists/-/command-exists-1.2.0.tgz#d97e0ed10097090e4ab0367ed425b0312fad86f3" + integrity sha512-ugsxEJfsCuqMLSuCD4PIJkp5Uk2z6TCMRCgYVuhRo5cYQY3+1xXTQkSlPtkpGHuvWMjS2KTeVQXxkXRACMbM6A== + "@types/debug@^4.1.4": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== + +"@types/fs-extra@^5.0.2": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.1.0.tgz#2a325ef97901504a3828718c390d34b8426a10a1" + integrity sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ== + dependencies: + "@types/node" "*" + "@types/fs-extra@^8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.0.tgz#1114834b53c3914806cd03b3304b37b3bd221a4d" @@ -1787,6 +2018,14 @@ dependencies: "@types/node" "*" +"@types/glob@*": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + "@types/graceful-fs@^4.1.2": version "4.1.3" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" @@ -1824,6 +2063,13 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/jest@^24.0.0": + version "24.9.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.9.1.tgz#02baf9573c78f1b9974a5f36778b366aa77bd534" + integrity sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q== + dependencies: + jest-diff "^24.3.0" + "@types/jest@^25.1.3": version "25.2.3" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.3.tgz#33d27e4c4716caae4eced355097a47ad363fdcaf" @@ -1832,12 +2078,27 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" +"@types/json-schema@^7.0.3": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + "@types/json-schema@^7.0.7": version "7.0.8" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== -"@types/minimatch@^3.0.3": +"@types/json-stable-stringify@^1.0.32": + version "1.0.33" + resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.33.tgz#099b0712d824d15e2660c20e1c16e6a8381f308c" + integrity sha512-qEWiQff6q2tA5gcJGWwzplQcXdJtm+0oy6IHGHzlOf3eFAkGE/FIPXZK9ofWgNSHVp8AFFI33PJJshS0ei3Gvw== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/minimatch@*", "@types/minimatch@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== @@ -1874,6 +2135,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.4.tgz#1581d6c16e3d4803eb079c87d4ac893ee7501c2c" integrity sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA== +"@types/node@14.0.x": + version "14.0.27" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.27.tgz#a151873af5a5e851b51b3b065c9e63390a9e0eb1" + integrity sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g== + "@types/node@^10.17.21": version "10.17.21" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.21.tgz#c00e9603399126925806bed2d9a1e37da506965e" @@ -1894,6 +2160,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== +"@types/node@~10.17.0": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -1921,6 +2192,19 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== +"@types/rimraf@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.2.tgz#a63d175b331748e5220ad48c901d7bbf1f44eef8" + integrity sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ== + dependencies: + "@types/glob" "*" + "@types/node" "*" + +"@types/semver@^6.0.1": + version "6.2.3" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.3.tgz#5798ecf1bec94eaa64db39ee52808ec0693315aa" + integrity sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A== + "@types/source-map-support@^0.4.0": version "0.4.2" resolved "https://registry.yarnpkg.com/@types/source-map-support/-/source-map-support-0.4.2.tgz#5b9f4502183430511bb90e84a6e4bf4214e55a67" @@ -1945,6 +2229,16 @@ dependencies: "@types/node" "*" +"@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + +"@types/strip-json-comments@^0.0.28": + version "0.0.28" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.28.tgz#a445d85d63616970294d079327597074f9ca7700" + integrity sha1-pEXYXWNhaXApTQeTJ1lwdPnKdwA= + "@types/tar-stream@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-2.1.0.tgz#884b1cbe6c35ff459c05a5eba86b406805943ef6" @@ -1960,6 +2254,11 @@ "@types/minipass" "*" "@types/node" "*" +"@types/tmp@^0.2.0": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.2.tgz#424537a3b91828cb26aaf697f21ae3cd1b69f7e7" + integrity sha512-MhSa0yylXtVMsyT8qFpHA1DLHj4DvQGH5ntxrhHSh8PxUVNi35Wk+P5hVgqbO2qZqOotqr9jaoPRL+iRjWYm/A== + "@types/tunnel@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.3.tgz#f109e730b072b3136347561fc558c9358bb8c6e9" @@ -1972,7 +2271,19 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== -"@types/yargs@^15.0.0": +"@types/yargs@^12.0.8": + version "12.0.20" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.20.tgz#50c390e95b8dd32105560e08ea0571dde99d2d8a" + integrity sha512-MjOKUoDmNattFOBJvAZng7X9KXIKSGy6XHoXY9mASkKwCn35X4Ckh+Ugv1DewXZXrWYXMNtLiXhlCfWlpcAV+Q== + +"@types/yargs@^13.0.0": + version "13.0.12" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.12.tgz#d895a88c703b78af0465a9de88aa92c61430b092" + integrity sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^15.0.0", "@types/yargs@^15.0.3": version "15.0.14" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== @@ -1993,6 +2304,30 @@ dependencies: "@types/yargs-parser" "*" +"@typescript-eslint/eslint-plugin@^2.3.2": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" + integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== + dependencies: + "@typescript-eslint/experimental-utils" "2.34.0" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + tsutils "^3.17.1" + +"@typescript-eslint/eslint-plugin@^4.11.1": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" + integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== + dependencies: + "@typescript-eslint/experimental-utils" "4.33.0" + "@typescript-eslint/scope-manager" "4.33.0" + debug "^4.3.1" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" + "@typescript-eslint/eslint-plugin@^4.8.1": version "4.28.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.2.tgz#7a8320f00141666813d0ae43b49ee8244f7cf92a" @@ -2006,6 +2341,16 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/experimental-utils@2.34.0", "@typescript-eslint/experimental-utils@^2.3.2": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" + integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.34.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + "@typescript-eslint/experimental-utils@4.28.2": version "4.28.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.2.tgz#4ebdec06a10888e9326e1d51d81ad52a361bd0b0" @@ -2018,6 +2363,38 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" +"@typescript-eslint/experimental-utils@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" + integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== + dependencies: + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/parser@^2.3.2": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" + integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.34.0" + "@typescript-eslint/typescript-estree" "2.34.0" + eslint-visitor-keys "^1.1.0" + +"@typescript-eslint/parser@^4.11.1": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" + integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== + dependencies: + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" + debug "^4.3.1" + "@typescript-eslint/parser@^4.8.1": version "4.28.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.2.tgz#6aff11bf4b91eb67ca7517962eede951e9e2a15d" @@ -2036,11 +2413,37 @@ "@typescript-eslint/types" "4.28.2" "@typescript-eslint/visitor-keys" "4.28.2" +"@typescript-eslint/scope-manager@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" + integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + "@typescript-eslint/types@4.28.2": version "4.28.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.2.tgz#e6b9e234e0e9a66c4d25bab881661e91478223b5" integrity sha512-Gr15fuQVd93uD9zzxbApz3wf7ua3yk4ZujABZlZhaxxKY8ojo448u7XTm/+ETpy0V0dlMtj6t4VdDvdc0JmUhA== +"@typescript-eslint/types@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== + +"@typescript-eslint/typescript-estree@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" + integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + "@typescript-eslint/typescript-estree@4.28.2": version "4.28.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.2.tgz#680129b2a285289a15e7c6108c84739adf3a798c" @@ -2054,6 +2457,19 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + "@typescript-eslint/visitor-keys@4.28.2": version "4.28.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.2.tgz#bf56a400857bb68b59b311e6d0a5fbef5c3b5130" @@ -2062,6 +2478,14 @@ "@typescript-eslint/types" "4.28.2" eslint-visitor-keys "^2.0.0" +"@typescript-eslint/visitor-keys@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== + dependencies: + "@typescript-eslint/types" "4.33.0" + eslint-visitor-keys "^2.0.0" + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2080,7 +2504,7 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -acorn-globals@^4.3.2: +acorn-globals@^4.1.0, acorn-globals@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== @@ -2088,7 +2512,7 @@ acorn-globals@^4.3.2: acorn "^6.0.1" acorn-walk "^6.0.1" -acorn-jsx@^5.3.1: +acorn-jsx@^5.2.0, acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== @@ -2098,6 +2522,11 @@ acorn-walk@^6.0.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== +acorn@^5.5.3: + version "5.7.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" + integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== + acorn@^6.0.1: version "6.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" @@ -2108,7 +2537,7 @@ acorn@^7.1.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== -acorn@^7.4.0: +acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -2142,7 +2571,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2177,6 +2606,11 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-escapes@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + ansi-escapes@^4.2.1: version "4.3.1" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" @@ -2194,7 +2628,7 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.1.0: +ansi-regex@^4.0.0, ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== @@ -2209,7 +2643,7 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -2315,6 +2749,17 @@ array-includes@^3.1.3: get-intrinsic "^1.1.1" is-string "^1.0.5" +array-includes@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" + integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + is-string "^1.0.7" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -2334,6 +2779,15 @@ array.prototype.flat@^1.2.4: define-properties "^1.1.3" es-abstract "^1.18.0-next.1" +array.prototype.flat@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" + integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -2383,6 +2837,11 @@ async-hook-jl@^1.7.6: dependencies: stack-chain "^1.3.7" +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + async-listener@^0.6.0: version "0.6.10" resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc" @@ -2437,6 +2896,19 @@ babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.2" +babel-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" + integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== + dependencies: + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/babel__core" "^7.1.0" + babel-plugin-istanbul "^5.1.0" + babel-preset-jest "^24.9.0" + chalk "^2.4.2" + slash "^2.0.0" + babel-jest@^25.5.1: version "25.5.1" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.5.1.tgz#bc2e6101f849d6f6aec09720ffc7bc5332e62853" @@ -2451,6 +2923,16 @@ babel-jest@^25.5.1: graceful-fs "^4.2.4" slash "^3.0.0" +babel-plugin-istanbul@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" + integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + find-up "^3.0.0" + istanbul-lib-instrument "^3.3.0" + test-exclude "^5.2.3" + babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" @@ -2462,6 +2944,13 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" +babel-plugin-jest-hoist@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" + integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== + dependencies: + "@types/babel__traverse" "^7.0.6" + babel-plugin-jest-hoist@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.5.0.tgz#129c80ba5c7fc75baf3a45b93e2e372d57ca2677" @@ -2487,6 +2976,14 @@ babel-preset-current-node-syntax@^0.1.2: "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" +babel-preset-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" + integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== + dependencies: + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + babel-plugin-jest-hoist "^24.9.0" + babel-preset-jest@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.5.0.tgz#c1d7f191829487a907764c65307faa0e66590b49" @@ -2535,6 +3032,13 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bl@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" @@ -2740,7 +3244,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.3.0: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2757,7 +3261,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -2819,6 +3323,15 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -2939,6 +3452,11 @@ commander@^2.12.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +comment-parser@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.6.2.tgz#b71e8fcacad954bea616779391838150d0096dcb" + integrity sha512-Wdms0Q8d4vvb2Yk72OwZjwNWtMklbC5Re7lD9cjCP/AG1fhocmc0TrxGBBAXPLy8fZQPrfHGgyygwI0lA7pbzA== + comment-parser@^0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.6.tgz#0e743a53c8e646c899a1323db31f6cd337b10f12" @@ -3103,7 +3621,7 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cross-spawn@^6.0.0: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -3132,15 +3650,22 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + cssom@^0.4.1: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== +cssstyle@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" + integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== + dependencies: + cssom "0.3.x" cssstyle@^2.0.0: version "2.3.0" @@ -3161,7 +3686,7 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-urls@^1.1.0: +data-urls@^1.0.0, data-urls@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== @@ -3329,6 +3854,11 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -3354,6 +3884,11 @@ diagnostic-channel@0.2.0: dependencies: semver "^5.3.0" +diff-sequences@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" + integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== + diff-sequences@^25.2.6: version "25.2.6" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" @@ -3411,33 +3946,6 @@ dot-prop@^6.0.1: dependencies: is-obj "^2.0.0" -dts-critic@latest: - version "3.2.6" - resolved "https://registry.yarnpkg.com/dts-critic/-/dts-critic-3.2.6.tgz#48ac170ff3e787230888a9677942ecf3af1c70af" - integrity sha512-8O07RoGrTzlNxE5Y6x+YPC5iDDhOj00ypxPmLST2EVYWKybcrGq6ubXM3X6poB1LE0cNtrW15MLP7SrU+m4WtQ== - dependencies: - "@definitelytyped/header-parser" "0.0.34" - command-exists "^1.2.8" - rimraf "^3.0.2" - semver "^6.2.0" - tmp "^0.2.1" - yargs "^15.3.1" - -dtslint@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/dtslint/-/dtslint-4.0.6.tgz#47a939365ff1fbbff2345b3858fb81c42e10fddd" - integrity sha512-bj5EaTeAqVXDHo/ut8oTRN/hZNTVKTAsNkavyH/T7XeDU/bqrctlYJmQo2HWJzPDoNCjq6NdVgvOnmQBGmSHsg== - dependencies: - "@definitelytyped/header-parser" latest - "@definitelytyped/typescript-versions" latest - "@definitelytyped/utils" latest - dts-critic latest - fs-extra "^6.0.1" - json-stable-stringify "^1.0.1" - strip-json-comments "^2.0.1" - tslint "5.14.0" - yargs "^15.1.0" - duplexer@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -3465,6 +3973,11 @@ emitter-listener@^1.0.1, emitter-listener@^1.1.1: dependencies: shimmer "^1.2.0" +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -3535,6 +4048,32 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-abstract@^1.19.0, es-abstract@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -3571,6 +4110,26 @@ escodegen@^1.11.1: optionalDependencies: source-map "~0.6.1" +escodegen@^1.9.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-formatter-autolinkable-stylish@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/eslint-formatter-autolinkable-stylish/-/eslint-formatter-autolinkable-stylish-1.2.0.tgz#ad63f2fb1723d01269c59f0ed5d93fbbc254c456" + integrity sha512-fID9VwZBc2iLcefBvhwU08KRBa0SQkWoVVtm70o9F8w/nYQaJVtBBzNeTJHRFyt3gOm1NGqyDRxAcddfZ6JQzA== + dependencies: + chalk "^4.1.2" + plur "^4.0.0" + eslint-import-resolver-node@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" @@ -3579,6 +4138,14 @@ eslint-import-resolver-node@^0.3.4: debug "^2.6.9" resolve "^1.13.1" +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== + dependencies: + debug "^3.2.7" + resolve "^1.20.0" + eslint-module-utils@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" @@ -3587,6 +4154,34 @@ eslint-module-utils@^2.6.1: debug "^3.2.7" pkg-dir "^2.0.0" +eslint-module-utils@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz#b435001c9f8dd4ab7f6d0efcae4b9696d4c24b7c" + integrity sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ== + dependencies: + debug "^3.2.7" + find-up "^2.1.0" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.18.2: + version "2.25.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.3.tgz#a554b5f66e08fb4f6dc99221866e57cfff824766" + integrity sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg== + dependencies: + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" + debug "^2.6.9" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.1" + has "^1.0.3" + is-core-module "^2.8.0" + is-glob "^4.0.3" + minimatch "^3.0.4" + object.values "^1.1.5" + resolve "^1.20.0" + tsconfig-paths "^3.11.0" + eslint-plugin-import@^2.22.1: version "2.23.4" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz#8dceb1ed6b73e46e50ec9a5bb2411b645e7d3d97" @@ -3608,6 +4203,18 @@ eslint-plugin-import@^2.22.1: resolve "^1.20.0" tsconfig-paths "^3.9.0" +eslint-plugin-jsdoc@^15.9.9: + version "15.12.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-15.12.2.tgz#d8afb762921dbf841bad42d4b2b554de1ece9305" + integrity sha512-QHzPc3VKTEbTn369/HpqDjl/czv3fCei/bZg5NA5tu9Od10MfpTH4kc1xnRDobhQoDs3AMz9wuaI4coHWRzMQw== + dependencies: + comment-parser "^0.6.2" + debug "^4.1.1" + jsdoctypeparser "^5.1.1" + lodash "^4.17.15" + object.entries-ponyfill "^1.0.1" + regextras "^0.6.1" + eslint-plugin-jsdoc@^30.7.8: version "30.7.13" resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-30.7.13.tgz#52e5c74fb806d3bbeb51d04a0c829508c3c6b563" @@ -3621,7 +4228,12 @@ eslint-plugin-jsdoc@^30.7.8: semver "^7.3.4" spdx-expression-parse "^3.0.1" -eslint-scope@^5.1.1: +eslint-plugin-no-null@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz#1236a812391390a1877ad4007c26e745341c951f" + integrity sha1-EjaoEjkTkKGHetQAfCbnRTQclR8= + +eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -3629,7 +4241,14 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.1.0: +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== @@ -3653,7 +4272,50 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^7.31.0: +eslint@^6.5.1: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +eslint@^7.16.0, eslint@^7.31.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== @@ -3699,6 +4361,15 @@ eslint@^7.31.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + espree@^7.3.0, espree@^7.3.1: version "7.3.1" resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" @@ -3713,7 +4384,7 @@ esprima@^4.0.0, esprima@^4.0.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.0: +esquery@^1.0.1, esquery@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== @@ -3824,6 +4495,18 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== +expect@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" + integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== + dependencies: + "@jest/types" "^24.9.0" + ansi-styles "^3.2.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.9.0" + expect@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/expect/-/expect-25.5.0.tgz#f07f848712a2813bb59167da3fb828ca21f58bba" @@ -3936,6 +4619,13 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -3943,6 +4633,11 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -3967,6 +4662,13 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -3975,6 +4677,15 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -3983,6 +4694,11 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + flatted@^3.1.0: version "3.2.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" @@ -3993,6 +4709,13 @@ follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -4099,6 +4822,14 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + fsevents@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" @@ -4148,7 +4879,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== @@ -4191,6 +4922,14 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -4257,7 +4996,7 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -glob-parent@^5.1.1, glob-parent@^5.1.2: +glob-parent@^5.0.0, glob-parent@^5.1.1, glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -4293,6 +5032,13 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + globals@^13.6.0, globals@^13.9.0: version "13.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e" @@ -4389,6 +5135,13 @@ has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -4552,6 +5305,11 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +ignore@^5.1.8: + version "5.1.9" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb" + integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -4560,6 +5318,14 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + import-local@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" @@ -4614,7 +5380,7 @@ init-package-json@^2.0.2: validate-npm-package-license "^3.0.4" validate-npm-package-name "^3.0.0" -inquirer@^7.3.3: +inquirer@^7.0.0, inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== @@ -4633,6 +5399,22 @@ inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -4643,6 +5425,11 @@ ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +irregular-plurals@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.3.0.tgz#67d0715d4361a60d9fd9ee80af3881c631a31ee2" + integrity sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g== + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -4679,6 +5466,11 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-callable@^1.1.3, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" @@ -4705,6 +5497,13 @@ is-core-module@^2.5.0: dependencies: has "^1.0.3" +is-core-module@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" + integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -4786,7 +5585,7 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -4862,6 +5661,19 @@ is-regex@^1.1.3: call-bind "^1.0.2" has-symbols "^1.0.2" +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== + is-ssh@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.1.tgz#f349a8cadd24e65298037a522cf7520f2e81a0f3" @@ -4884,6 +5696,13 @@ is-string@^1.0.5, is-string@^1.0.6: resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== +is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" @@ -4903,11 +5722,23 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-weakref@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" + integrity sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ== + dependencies: + call-bind "^1.0.0" + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + is-wsl@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -4952,11 +5783,29 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== + istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== +istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + istanbul-lib-instrument@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" @@ -4970,6 +5819,15 @@ istanbul-lib-instrument@^4.0.0: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" +istanbul-lib-report@^2.0.4: + version "2.0.8" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" + integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== + dependencies: + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + supports-color "^6.1.0" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -4979,6 +5837,17 @@ istanbul-lib-report@^3.0.0: make-dir "^3.0.0" supports-color "^7.1.0" +istanbul-lib-source-maps@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" + integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + rimraf "^2.6.3" + source-map "^0.6.1" + istanbul-lib-source-maps@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" @@ -4988,6 +5857,13 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" +istanbul-reports@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.7.tgz#5d939f6237d7b48393cc0959eab40cd4fd056931" + integrity sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg== + dependencies: + html-escaper "^2.0.0" + istanbul-reports@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" @@ -4996,6 +5872,15 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jest-changed-files@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" + integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== + dependencies: + "@jest/types" "^24.9.0" + execa "^1.0.0" + throat "^4.0.0" + jest-changed-files@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.5.0.tgz#141cc23567ceb3f534526f8614ba39421383634c" @@ -5005,6 +5890,25 @@ jest-changed-files@^25.5.0: execa "^3.2.0" throat "^5.0.0" +jest-cli@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" + integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== + dependencies: + "@jest/core" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + import-local "^2.0.0" + is-ci "^2.0.0" + jest-config "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + prompts "^2.0.1" + realpath-native "^1.1.0" + yargs "^13.3.0" + jest-cli@^25.5.4: version "25.5.4" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.5.4.tgz#b9f1a84d1301a92c5c217684cb79840831db9f0d" @@ -5025,6 +5929,29 @@ jest-cli@^25.5.4: realpath-native "^2.0.0" yargs "^15.3.1" +jest-config@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" + integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^24.9.0" + "@jest/types" "^24.9.0" + babel-jest "^24.9.0" + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^24.9.0" + jest-environment-node "^24.9.0" + jest-get-type "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + micromatch "^3.1.10" + pretty-format "^24.9.0" + realpath-native "^1.1.0" + jest-config@^25.5.4: version "25.5.4" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.5.4.tgz#38e2057b3f976ef7309b2b2c8dcd2a708a67f02c" @@ -5050,6 +5977,16 @@ jest-config@^25.5.4: pretty-format "^25.5.0" realpath-native "^2.0.0" +jest-diff@^24.3.0, jest-diff@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" + integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== + dependencies: + chalk "^2.0.1" + diff-sequences "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-diff@^25.2.1, jest-diff@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9" @@ -5060,6 +5997,13 @@ jest-diff@^25.2.1, jest-diff@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" +jest-docblock@^24.3.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" + integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== + dependencies: + detect-newline "^2.1.0" + jest-docblock@^25.3.0: version "25.3.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.3.0.tgz#8b777a27e3477cd77a168c05290c471a575623ef" @@ -5067,6 +6011,17 @@ jest-docblock@^25.3.0: dependencies: detect-newline "^3.0.0" +jest-each@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" + integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== + dependencies: + "@jest/types" "^24.9.0" + chalk "^2.0.1" + jest-get-type "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + jest-each@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.5.0.tgz#0c3c2797e8225cb7bec7e4d249dcd96b934be516" @@ -5078,6 +6033,18 @@ jest-each@^25.5.0: jest-util "^25.5.0" pretty-format "^25.5.0" +jest-environment-jsdom@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" + integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + jsdom "^11.5.1" + jest-environment-jsdom@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.5.0.tgz#dcbe4da2ea997707997040ecf6e2560aec4e9834" @@ -5090,6 +6057,17 @@ jest-environment-jsdom@^25.5.0: jest-util "^25.5.0" jsdom "^15.2.1" +jest-environment-node@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" + integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + jest-environment-node@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.5.0.tgz#0f55270d94804902988e64adca37c6ce0f7d07a1" @@ -5102,11 +6080,35 @@ jest-environment-node@^25.5.0: jest-util "^25.5.0" semver "^6.3.0" +jest-get-type@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" + integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== + jest-get-type@^25.2.6: version "25.2.6" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== +jest-haste-map@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" + integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== + dependencies: + "@jest/types" "^24.9.0" + anymatch "^2.0.0" + fb-watchman "^2.0.0" + graceful-fs "^4.1.15" + invariant "^2.2.4" + jest-serializer "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.9.0" + micromatch "^3.1.10" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^1.2.7" + jest-haste-map@^25.5.1: version "25.5.1" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.5.1.tgz#1df10f716c1d94e60a1ebf7798c9fb3da2620943" @@ -5127,6 +6129,28 @@ jest-haste-map@^25.5.1: optionalDependencies: fsevents "^2.1.2" +jest-jasmine2@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" + integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^24.9.0" + is-generator-fn "^2.0.0" + jest-each "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + throat "^4.0.0" + jest-jasmine2@^25.5.4: version "25.5.4" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.5.4.tgz#66ca8b328fb1a3c5364816f8958f6970a8526968" @@ -5150,6 +6174,14 @@ jest-jasmine2@^25.5.4: pretty-format "^25.5.0" throat "^5.0.0" +jest-leak-detector@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" + integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== + dependencies: + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-leak-detector@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.5.0.tgz#2291c6294b0ce404241bb56fe60e2d0c3e34f0bb" @@ -5158,6 +6190,16 @@ jest-leak-detector@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" +jest-matcher-utils@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" + integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== + dependencies: + chalk "^2.0.1" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-matcher-utils@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz#fbc98a12d730e5d2453d7f1ed4a4d948e34b7867" @@ -5168,6 +6210,20 @@ jest-matcher-utils@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" +jest-message-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" + integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + jest-message-util@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.5.0.tgz#ea11d93204cc7ae97456e1d8716251185b8880ea" @@ -5182,6 +6238,13 @@ jest-message-util@^25.5.0: slash "^3.0.0" stack-utils "^1.0.1" +jest-mock@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" + integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== + dependencies: + "@jest/types" "^24.9.0" + jest-mock@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.5.0.tgz#a91a54dabd14e37ecd61665d6b6e06360a55387a" @@ -5194,11 +6257,25 @@ jest-pnp-resolver@^1.2.1: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== +jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" + integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== + jest-regex-util@^25.2.6: version "25.2.6" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.2.6.tgz#d847d38ba15d2118d3b06390056028d0f2fd3964" integrity sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw== +jest-resolve-dependencies@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" + integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== + dependencies: + "@jest/types" "^24.9.0" + jest-regex-util "^24.3.0" + jest-snapshot "^24.9.0" + jest-resolve-dependencies@^25.5.4: version "25.5.4" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.5.4.tgz#85501f53957c8e3be446e863a74777b5a17397a7" @@ -5208,6 +6285,17 @@ jest-resolve-dependencies@^25.5.4: jest-regex-util "^25.2.6" jest-snapshot "^25.5.1" +jest-resolve@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" + integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== + dependencies: + "@jest/types" "^24.9.0" + browser-resolve "^1.11.3" + chalk "^2.0.1" + jest-pnp-resolver "^1.2.1" + realpath-native "^1.1.0" + jest-resolve@^25.5.1: version "25.5.1" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.5.1.tgz#0e6fbcfa7c26d2a5fe8f456088dc332a79266829" @@ -5223,6 +6311,31 @@ jest-resolve@^25.5.1: resolve "^1.17.0" slash "^3.0.0" +jest-runner@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" + integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.4.2" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-docblock "^24.3.0" + jest-haste-map "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-leak-detector "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + source-map-support "^0.5.6" + throat "^4.0.0" + jest-runner@^25.5.4: version "25.5.4" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.5.4.tgz#ffec5df3875da5f5c878ae6d0a17b8e4ecd7c71d" @@ -5248,6 +6361,35 @@ jest-runner@^25.5.4: source-map-support "^0.5.6" throat "^5.0.0" +jest-runtime@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" + integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/source-map" "^24.3.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + realpath-native "^1.1.0" + slash "^2.0.0" + strip-bom "^3.0.0" + yargs "^13.3.0" + jest-runtime@^25.5.4: version "25.5.4" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.5.4.tgz#dc981fe2cb2137abcd319e74ccae7f7eeffbfaab" @@ -5280,6 +6422,11 @@ jest-runtime@^25.5.4: strip-bom "^4.0.0" yargs "^15.3.1" +jest-serializer@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" + integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== + jest-serializer@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.5.0.tgz#a993f484e769b4ed54e70e0efdb74007f503072b" @@ -5287,6 +6434,25 @@ jest-serializer@^25.5.0: dependencies: graceful-fs "^4.2.4" +jest-snapshot@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" + integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + expect "^24.9.0" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^24.9.0" + semver "^6.2.0" + jest-snapshot@^25.5.1: version "25.5.1" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.5.1.tgz#1a2a576491f9961eb8d00c2e5fd479bc28e5ff7f" @@ -5308,6 +6474,24 @@ jest-snapshot@^25.5.1: pretty-format "^25.5.0" semver "^6.3.0" +jest-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" + integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== + dependencies: + "@jest/console" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/source-map" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + callsites "^3.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.15" + is-ci "^2.0.0" + mkdirp "^0.5.1" + slash "^2.0.0" + source-map "^0.6.0" + jest-util@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.5.0.tgz#31c63b5d6e901274d264a4fec849230aa3fa35b0" @@ -5319,6 +6503,18 @@ jest-util@^25.5.0: is-ci "^2.0.0" make-dir "^3.0.0" +jest-validate@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" + integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== + dependencies: + "@jest/types" "^24.9.0" + camelcase "^5.3.1" + chalk "^2.0.1" + jest-get-type "^24.9.0" + leven "^3.1.0" + pretty-format "^24.9.0" + jest-validate@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.5.0.tgz#fb4c93f332c2e4cf70151a628e58a35e459a413a" @@ -5331,6 +6527,19 @@ jest-validate@^25.5.0: leven "^3.1.0" pretty-format "^25.5.0" +jest-watcher@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" + integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== + dependencies: + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + jest-util "^24.9.0" + string-length "^2.0.0" + jest-watcher@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.5.0.tgz#d6110d101df98badebe435003956fd4a465e8456" @@ -5343,6 +6552,14 @@ jest-watcher@^25.5.0: jest-util "^25.5.0" string-length "^3.1.0" +jest-worker@^24.6.0, jest-worker@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + jest-worker@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.5.0.tgz#2611d071b79cea0f43ee57a3d118593ac1547db1" @@ -5351,6 +6568,14 @@ jest-worker@^25.5.0: merge-stream "^2.0.0" supports-color "^7.0.0" +jest@^24.7.1: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" + integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== + dependencies: + import-local "^2.0.0" + jest-cli "^24.9.0" + jest@^25.1.0: version "25.5.4" resolved "https://registry.yarnpkg.com/jest/-/jest-25.5.4.tgz#f21107b6489cfe32b076ce2adcadee3587acb9db" @@ -5360,16 +6585,16 @@ jest@^25.1.0: import-local "^3.0.2" jest-cli "^25.5.4" +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" @@ -5391,11 +6616,48 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jsdoctypeparser@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-5.1.1.tgz#99c57412fe736c70024bf54204ed1bb93cf4a49f" + integrity sha512-APGygIJrT5bbz5lsVt8vyLJC0miEbQf/z9ZBfTr4RYvdia8AhWMRlYgivvwHG5zKD/VW3d6qpChCy64hpQET3A== + jsdoctypeparser@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz#8c97e2fb69315eb274b0f01377eaa5c940bd7b26" integrity sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw== +jsdom@^11.5.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" + integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== + dependencies: + abab "^2.0.0" + acorn "^5.5.3" + acorn-globals "^4.1.0" + array-equal "^1.0.0" + cssom ">= 0.3.2 < 0.4.0" + cssstyle "^1.0.0" + data-urls "^1.0.0" + domexception "^1.0.1" + escodegen "^1.9.1" + html-encoding-sniffer "^1.0.2" + left-pad "^1.3.0" + nwsapi "^2.0.7" + parse5 "4.0.0" + pn "^1.1.0" + request "^2.87.0" + request-promise-native "^1.0.5" + sax "^1.2.4" + symbol-tree "^3.2.2" + tough-cookie "^2.3.4" + w3c-hr-time "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.3" + whatwg-mimetype "^2.1.0" + whatwg-url "^6.4.1" + ws "^5.2.0" + xml-name-validator "^3.0.0" + jsdom@^15.2.1: version "15.2.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" @@ -5482,6 +6744,13 @@ json5@2.x, json5@^2.2.0: dependencies: minimist "^1.2.5" +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + json5@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" @@ -5612,6 +6881,11 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +left-pad@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" + integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== + lerna@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" @@ -5641,6 +6915,14 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -5649,14 +6931,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - libnpmaccess@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" @@ -5711,6 +6985,14 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -5818,7 +7100,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.7.0: +lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5837,6 +7119,13 @@ longjohn@^0.2.11: dependencies: source-map-support "0.3.2 - 1.0.0" +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -5981,7 +7270,7 @@ micromatch@4.x, micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" -micromatch@^3.1.4: +micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -6202,6 +7491,11 @@ mute-stream@0.0.8, mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nan@^2.12.1: + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -6314,6 +7608,17 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= +node-notifier@^5.4.2: + version "5.4.5" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.5.tgz#0cbc1a2b0f658493b4025775a13ad938e96091ef" + integrity sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ== + dependencies: + growly "^1.3.0" + is-wsl "^1.1.0" + semver "^5.5.0" + shellwords "^0.1.1" + which "^1.3.0" + node-notifier@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-6.0.0.tgz#cea319e06baa16deec8ce5cd7f133c4a46b68e12" @@ -6494,7 +7799,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -nwsapi@^2.2.0: +nwsapi@^2.0.7, nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== @@ -6523,7 +7828,7 @@ object-inspect@^1.10.3: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== -object-inspect@^1.9.0: +object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.11.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== @@ -6550,6 +7855,11 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.entries-ponyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.entries-ponyfill/-/object.entries-ponyfill-1.0.1.tgz#29abdf77cbfbd26566dd1aa24e9d88f65433d256" + integrity sha1-Kavfd8v70mVm3RqiTp2I9lQz0lY= + object.getownpropertydescriptors@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" @@ -6558,6 +7868,15 @@ object.getownpropertydescriptors@^2.0.3: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" +object.getownpropertydescriptors@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" + integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -6574,6 +7893,15 @@ object.values@^1.1.3: define-properties "^1.1.3" es-abstract "^1.18.2" +object.values@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + oboe@^2.1.3: version "2.1.5" resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" @@ -6615,7 +7943,7 @@ open@^7.0.0: is-docker "^2.0.0" is-wsl "^2.1.1" -optionator@^0.8.1: +optionator@^0.8.1, optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -6665,6 +7993,13 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-each-series@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" + integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= + dependencies: + p-reduce "^1.0.0" + p-each-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" @@ -6687,7 +8022,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -6701,6 +8036,13 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -6733,6 +8075,11 @@ p-queue@^6.6.2: eventemitter3 "^4.0.4" p-timeout "^3.2.0" +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= + p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" @@ -6830,6 +8177,11 @@ parse-url@^5.0.0: parse-path "^4.0.0" protocols "^1.4.0" +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + parse5@5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" @@ -6936,6 +8288,13 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -6950,6 +8309,13 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" +plur@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84" + integrity sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg== + dependencies: + irregular-plurals "^3.2.0" + pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -6994,6 +8360,16 @@ prettier@^1.19.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== +pretty-format@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" + integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== + dependencies: + "@jest/types" "^24.9.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + pretty-format@^25.2.1, pretty-format@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" @@ -7117,7 +8493,7 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-is@^16.12.0: +react-is@^16.12.0, react-is@^16.8.4: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -7184,6 +8560,14 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -7251,6 +8635,13 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" +realpath-native@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== + dependencies: + util.promisify "^1.0.0" + realpath-native@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" @@ -7272,11 +8663,21 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^3.1.0: +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +regexpp@^3.0.0, regexpp@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +regextras@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.6.1.tgz#9689641bbb338e0ff7001a5c507c6a2008df7b36" + integrity sha512-EzIHww9xV2Kpqx+corS/I7OBmf2rZ0pKKJPsw5Dc+l6Zq1TslDmtRIP9maVn3UH+72MIXmn8zzDgP07ihQogUA== + regextras@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.7.1.tgz#be95719d5f43f9ef0b9fa07ad89b7c606995a3b2" @@ -7304,6 +8705,22 @@ request-promise-core@1.1.3: dependencies: lodash "^4.17.15" +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== + dependencies: + lodash "^4.17.19" + +request-promise-native@^1.0.5: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== + dependencies: + request-promise-core "1.1.4" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + request-promise-native@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" @@ -7313,7 +8730,7 @@ request-promise-native@^1.0.7: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.88.0, request@^2.88.2: +request@^2.87.0, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -7354,6 +8771,13 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -7361,6 +8785,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -7419,13 +8848,20 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@2, rimraf@^2.6.3: +rimraf@2, rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -7494,7 +8930,7 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@>=0.6.0: +sax@>=0.6.0, sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -7523,7 +8959,7 @@ semaphore@^1.0.5: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@6.x, semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: +semver@6.x, semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -7617,11 +9053,25 @@ sisteransi@^1.0.4: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -7866,6 +9316,14 @@ stoppable@^1.1.0: resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + string-length@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" @@ -7891,6 +9349,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + string-width@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" @@ -7953,7 +9420,7 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.2.0: +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== @@ -7999,7 +9466,7 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.0.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -8025,6 +9492,13 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + supports-color@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" @@ -8052,6 +9526,16 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + table@^6.0.9: version "6.7.2" resolved "https://registry.yarnpkg.com/table/-/table-6.7.2.tgz#a8d39b9f5966693ca8b0feba270a78722cbaf3b0" @@ -8143,6 +9627,16 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -8162,6 +9656,11 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= + throat@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -8243,7 +9742,7 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -tough-cookie@^2.3.3, tough-cookie@~2.5.0: +tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -8304,6 +9803,16 @@ ts-jest@^25.2.1: semver "6.x" yargs-parser "18.x" +tsconfig-paths@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b" + integrity sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + tsconfig-paths@^3.9.0: version "3.10.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz#79ae67a68c15289fdf5c51cb74f397522d795ed7" @@ -8392,7 +9901,7 @@ tsutils@^2.29.0: dependencies: tslib "^1.8.1" -tsutils@^3.21.0: +tsutils@^3.17.1, tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== @@ -8477,11 +9986,21 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@*: + version "4.5.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" + integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== + typescript@^4.1.0: version "4.3.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +typescript@next: + version "4.6.0-dev.20211126" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.0-dev.20211126.tgz#d27ce3a360dc4da1dcdebd80efe42b51afdeebdb" + integrity sha512-m+LKstqVv6FYW363aIbO6bm8awsLbeSUCzU6FxPtzUF/WJkFieQfYmdVwEIzigeTpw4E2GETBXnk6P6AixcQJQ== + uglify-js@^3.1.4: version "3.13.5" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.5.tgz#5d71d6dbba64cf441f32929b1efce7365bb4f113" @@ -8607,6 +10126,17 @@ util-promisify@^2.1.0: dependencies: object.getownpropertydescriptors "^2.0.3" +util.promisify@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" + integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + for-each "^0.3.3" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.1" + uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" @@ -8695,18 +10225,27 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" -whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: +whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-url@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" @@ -8741,7 +10280,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.9, which@^1.3.1: +which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -8779,6 +10318,15 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -8802,6 +10350,15 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" + integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + write-file-atomic@^2.4.2: version "2.4.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" @@ -8854,6 +10411,20 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^5.2.0: + version "5.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d" + integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA== + dependencies: + async-limiter "~1.0.0" + ws@^7.0.0: version "7.5.2" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.2.tgz#09cc8fea3bec1bc5ed44ef51b42f945be36900f6" @@ -8925,6 +10496,14 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" @@ -8947,6 +10526,22 @@ yargs@15.3.1, yargs@^15.1.0, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.1" +yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + yargs@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"