diff --git a/README.md b/README.md index 691933ca..6f1c1af0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ `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. +It will install `typescript` and `eslint` 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. @@ -16,7 +16,6 @@ Use [`--declaration`](http://www.typescriptlang.org/docs/handbook/compiler-optio 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. @@ -25,14 +24,12 @@ 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 @@ -57,31 +54,26 @@ Refer to the [handbook](http://www.typescriptlang.org/docs/handbook/declaration- 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` +#### `types/eslintrc.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: +You can specify new lint [configuration](https://eslint.org/docs/user-guide/configuring), or disable some. An example: ```json5 { - "extends": "dtslint/dtslint.json", // Or "dtslint/dt.json" if on DefinitelyTyped + "extends": "dtslint/dtslint.json", "rules": { - "semicolon": false, - "indent": [true, "tabs"] + "@typescript-eslint/no-floating-promises": false } } ``` - #### `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. @@ -102,7 +94,6 @@ f(2); // $ExpectType void f("one"); ``` - ## Specify a TypeScript version Normally packages will be tested using TypeScript 2.0. @@ -115,7 +106,6 @@ To use a newer version, specify it by including a comment like so: 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` @@ -131,14 +121,6 @@ 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 @@ -152,7 +134,6 @@ 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 @@ -164,8 +145,11 @@ To run a single test: `node node_modules/tslint/bin/tslint --rules-dir bin/rules 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` ``` + Package lock files such as `yarn.lock` and `package-lock.json` may cause this issue because of our github dependency on `"definitelytyped-header-parser": "github:Microsoft/definitelytyped-header-parser#production"`, which contains the list of typescript versions to install. To fix this, try deleting your lock file and re-installing. diff --git a/dtslint.json b/dtslint.json index 5bdb8202..3c9f6ee2 100644 --- a/dtslint.json +++ b/dtslint.json @@ -1,131 +1,30 @@ { - "extends": "tslint:all", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], "rulesDirectory": "./bin/rules", "rules": { // Custom rules "expect": true, "export-just-namespace": true, + "no-any-union": 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, + "npm-naming": true, "prefer-declare-function": true, + "redundant-undefined": true, + "strict-export-declare-modifiers": 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 + "void-return": true } } diff --git a/package-lock.json b/package-lock.json index 95d5d610..d140e035 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "dts-critic": "^3.3.9", "fs-extra": "^6.0.1", "json-stable-stringify": "^1.0.1", - "strip-json-comments": "^2.0.1", "tslint": "5.14.0", "tsutils": "^2.29.0", "yargs": "^15.1.0" @@ -2730,14 +2729,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -5606,11 +5597,6 @@ "ansi-regex": "^2.0.0" } }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", diff --git a/package.json b/package.json index 886ae186..e5230d77 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,11 @@ "@definitelytyped/header-parser": "latest", "@definitelytyped/typescript-versions": "latest", "@definitelytyped/utils": "latest", + "@typescript-eslint/eslint-plugin": "^4.19.1", + "@typescript-eslint/parser": "^4.19.0", "dts-critic": "latest", "fs-extra": "^6.0.1", "json-stable-stringify": "^1.0.1", - "strip-json-comments": "^2.0.1", "tslint": "5.14.0", "tsutils": "^2.29.0", "yargs": "^15.1.0" @@ -42,14 +43,18 @@ "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/eslint": "^7.2.7", "@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-eslint/eslint-plugin": "^4.19.1", + "@typescript-eslint/eslint-plugin-tslint": "^4.19.0", + "@typescript-eslint/parser": "^4.19.0", + "eslint": "^7.32.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jsdoc": "^32.3.0", "typescript": "next" }, "engines": { diff --git a/src/checks.ts b/src/checks.ts deleted file mode 100644 index dc725362..00000000 --- a/src/checks.ts +++ /dev/null @@ -1,148 +0,0 @@ -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: ReadonlyArray, -): 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": - // "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 Array) { - 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/src/index.ts b/src/index.ts index 2b57b51e..ac94e0c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,22 +1,14 @@ #!/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 { 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"; +import { lint } from "./lint"; 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; @@ -42,17 +34,9 @@ async function main(): Promise { 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}'`); @@ -73,12 +57,8 @@ async function main(): Promise { 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); - } + await installTypeScriptAsNeeded(tsLocal, onlyTestTsNext); + await runTests(dirPath); } async function installTypeScriptAsNeeded(tsLocal: string | undefined, onlyTestTsNext: boolean): Promise { @@ -90,174 +70,23 @@ async function installTypeScriptAsNeeded(tsLocal: string | undefined, onlyTestTs } function usage(): void { - console.error("Usage: dtslint [--version] [--installAll] [--onlyTestTsNext] [--expectOnly] [--localTs path]"); + console.error("Usage: dtslint [--version] [--installAll] [--onlyTestTsNext] [--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); +async function runTests(dirPath: string): Promise { + const err = await lint(dirPath); 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); diff --git a/src/lint.ts b/src/lint.ts index 97fe372c..a59d609f 100644 --- a/src/lint.ts +++ b/src/lint.ts @@ -1,229 +1,24 @@ -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 { join as joinPaths } from "path"; +import { ESLint } from "eslint"; -import { getProgram, Options as ExpectOptions } from "./rules/expectRule"; +import { parseTsconfigFiles } from "./parseTsconfigFiles"; -import { readJson, withoutPrefix } from "./util"; - -export async function lint( - dirPath: string, - minVersion: TsVersion, - maxVersion: TsVersion, - isLatest: boolean, - expectOnly: boolean, - tsLocal: string | undefined): Promise { +export async function lint(dirPath: string): 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 files = parseTsconfigFiles(dirPath, tsconfigPath); + if (typeof files === "string") { + return files; } - 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}`; - } + const eslint = new ESLint({ + baseConfig: { + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint/eslint-plugin'], + root: true, } - - // 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 results = await eslint.lintFiles(files); + const formatter = await eslint.loadFormatter("stylish"); - 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); + return formatter.format(results); } - -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): ReadonlyArray { - 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/src/parseTsconfigFiles.ts b/src/parseTsconfigFiles.ts new file mode 100644 index 00000000..7d28b3e6 --- /dev/null +++ b/src/parseTsconfigFiles.ts @@ -0,0 +1,42 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as ts from "typescript"; + +function formatErrors(diagnostics: ts.Diagnostic[]) { + return ts.formatDiagnostics(diagnostics, { + getCanonicalFileName: file => file, + getCurrentDirectory: process.cwd, + getNewLine: () => "\n", + }); +} + +export function parseTsconfigFiles(dirPath: string, configFile: string) { + const config = ts.readConfigFile(configFile, ts.sys.readFile); + if (config.error) { + return formatErrors([config.error]); + } + + const parsed = ts.parseJsonConfigFileContent( + config.config, + { + fileExists: fs.existsSync, + readDirectory: ts.sys.readDirectory, + readFile: file => fs.readFileSync(file, "utf8"), + useCaseSensitiveFileNames: true, + }, + path.resolve(dirPath), + { noEmit: true }, + ); + + if (parsed.errors) { + // Ignore warnings and "TS18003: No inputs were found in config file ..." + const errors = parsed.errors.filter( + diagnostic => diagnostic.category === ts.DiagnosticCategory.Error && diagnostic.code !== 18003, + ); + if (errors.length) { + return formatErrors(errors); + } + } + + return parsed.fileNames; +} \ No newline at end of file diff --git a/src/rules/npmNamingRule.ts b/src/rules/npmNamingRule.ts index 0411f4b6..e304a0ad 100644 --- a/src/rules/npmNamingRule.ts +++ b/src/rules/npmNamingRule.ts @@ -1,7 +1,6 @@ import { CheckOptions as CriticOptions, CriticError, - defaultErrors, dtsCritic as critic, ErrorKind, ExportErrorKind, @@ -271,38 +270,3 @@ you can disable this check by adding the following options to your project's tsl 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: Array<[ExportErrorKind, boolean]> = []; - disabledErrors.forEach(error => errors.push([error, false])); - return [true, { mode: Mode.Code, errors }]; -} diff --git a/src/updateConfig.ts b/src/updateConfig.ts deleted file mode 100644 index 2db746ba..00000000 --- a/src/updateConfig.ts +++ /dev/null @@ -1,249 +0,0 @@ -// 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/src/util.ts b/src/util.ts index 1a0e9791..92da33c2 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,14 +1,7 @@ import assert = require("assert"); -import { pathExists, readFile } from "fs-extra"; -import { basename, dirname, join } from "path"; -import stripJsonComments = require("strip-json-comments"); +import { basename, dirname } from "path"; 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`; } @@ -53,14 +46,6 @@ export function getModuleDeclarationStatements(node: ts.ModuleDeclaration): Read 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; } diff --git a/test/test.js b/test/test.js index 42167065..af7e4677 100755 --- a/test/test.js +++ b/test/test.js @@ -1,26 +1,2 @@ #! /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); - } -} +throw new Error("Pending all rules being converted to ESLint."); diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 8259bc81..00000000 --- a/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "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 - } -}