Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove TSLint #866

Merged
merged 7 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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), make sure a package is correct for DT
- [dtslint-runner](packages/dtslint-runner): [DefinitelyTyped/dtslint-runner](https://github.com/DefinitelyTyped/dtslint-runner), test all packages, or all changed packages, on DT with dtslint
- [eslint-plugin](packages/eslint-plugin): provides DT-specific lint rules (except for the few remaining tslint rules, still in dtslint)
- [eslint-plugin](packages/eslint-plugin): provides DT-specific lint rules
- [dts-critic](packages/dts-critic): [DefinitelyTyped/dts-critic](https://github.com/DefinitelyTyped/dts-critic), issue errors when a types packages mismatches its original Javascript package.
- [header-parser](packages/header-parser): [microsoft/definitelytyped-header-parser](https://github.com/microsoft/definitelytyped-header-parser), check and extract DT-related info from package.json
- [publisher](packages/publisher): the rest of [microsoft/types-publisher](https://github.com/microsoft/types-publisher)
Expand Down
3 changes: 1 addition & 2 deletions packages/definitions-parser/src/lib/definition-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ export async function getTypingInfo(
);
}

// tslint:disable-next-line:non-literal-fs-path -- Not a reference to the fs package
const ls = fs.readdir(directoryName);
const result = await getPackageJsonInfoForPackage(
packageNameOrTypesDirectoryName,
Expand Down Expand Up @@ -179,7 +178,7 @@ export function parseVersionFromDirectoryName(
}
return {
major: Number(match[1]),
minor: match[3] !== undefined ? Number(match[3]) : undefined, // tslint:disable-line strict-type-predicates (false positive)
minor: match[3] !== undefined ? Number(match[3]) : undefined,
};
}

Expand Down
2 changes: 1 addition & 1 deletion packages/definitions-parser/src/packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ export interface PackageIdWithDefiniteVersion {
}

export function readNotNeededPackages(dt: FS): readonly NotNeededPackage[] {
const rawJson = dt.readJson("notNeededPackages.json"); // tslint:disable-line await-promise (tslint bug)
const rawJson = dt.readJson("notNeededPackages.json");
return Object.entries((rawJson as { readonly packages: readonly NotNeededPackageRaw[] }).packages).map((entry) =>
NotNeededPackage.fromRaw(...entry),
);
Expand Down
22 changes: 7 additions & 15 deletions packages/dtslint/README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down Expand Up @@ -101,24 +101,18 @@ For types that do not have a matching NPM package, add two properties:
1. `"nonNpm": true`
2. `"nonNpmDescription"`, a human-readable name for the project that is being typed.

#### `types/tslint.json` or `types/.eslintrc.json`
#### `types/.eslintrc.json`

Definitely Typed is in the process of migrating from tslint to eslint.
If you are using the default rules, .eslintrc.json is optional; tslint.json should be
An `.eslintrc.json` file is optional.
You can skip it if you don't need to modify any lint rule settings.
We recommend not adding an `.eslintrc.json` file.

```json5
{ "extends": "@definitelytyped/dtslint/dt.json" }
```

If present, this will override `dtslint`'s [default](https://github.com/microsoft/DefinitelyTyped-tools/blob/master/packages/dtslint/dtslint.json) settings.
You can specify new lint [rules](https://palantir.github.io/tslint/rules/), or disable some. An example:
If you absolutely need to, you can create an `.eslintrc.json` to disable rules:

JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
```json5
{
"extends": "@definitelytyped/dtslint/dtslint.json", // Or "@definitelytyped/dtslint/dt.json" if on DefinitelyTyped
"rules": {
"semicolon": false,
"indent": [true, "tabs"]
"@definitelytyped/no-unnecessary-generics": "off"
}
}
```
Expand Down Expand Up @@ -199,8 +193,6 @@ npm run watch

Use `pnpm test` to run all tests.

To run a single test: `node node_modules/tslint/bin/tslint --rules-dir dist/rules --test test/expect`.
jakebailey marked this conversation as resolved.
Show resolved Hide resolved

## 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.
Expand Down
1 change: 0 additions & 1 deletion packages/dtslint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"eslint-plugin-import": "^2.29.0",
"json-stable-stringify": "^1.0.2",
"strip-json-comments": "^3.1.1",
"tslint": "5.14.0",
"yargs": "^17.7.2"
},
"peerDependencies": {
Expand Down
47 changes: 47 additions & 0 deletions packages/dtslint/src/createProgram.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as fs from "fs";
import * as path from "path";
import * as ts from "typescript";

export function createProgram(configFile: string): ts.Program {
const config = ts.readConfigFile(configFile, ts.sys.readFile);
if (config.error !== undefined) {
throw new Error(
ts.formatDiagnostics([config.error], {
getCanonicalFileName: (f) => f,
getCurrentDirectory: process.cwd,
getNewLine: () => "\n",
}),
);
}

const parseConfigHost: ts.ParseConfigHost = {
fileExists: fs.existsSync,
readDirectory: ts.sys.readDirectory,
readFile: (file) => fs.readFileSync(file, "utf8"),
useCaseSensitiveFileNames: true,
};

const projectDirectory = path.dirname(configFile);
const parsed = ts.parseJsonConfigFileContent(config.config, parseConfigHost, path.resolve(projectDirectory), {
noEmit: true,
});

if (parsed.errors !== undefined) {
// ignore warnings and 'TS18003: No inputs were found in config file ...'
const errors = parsed.errors.filter((d) => d.category === ts.DiagnosticCategory.Error && d.code !== 18003);
if (errors.length !== 0) {
throw new Error(
ts.formatDiagnostics(errors, {
getCanonicalFileName: (f) => f,
getCurrentDirectory: process.cwd,
getNewLine: () => "\n",
}),
);
}
}

const host = ts.createCompilerHost(parsed.options, true);
const program = ts.createProgram(parsed.fileNames, parsed.options, host);

return program;
}
3 changes: 1 addition & 2 deletions packages/dtslint/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
installTypeScriptNext,
} from "@definitelytyped/utils";
import { checkPackageJson, checkTsconfig } from "./checks";
import { checkTslintJson, lint, TsVersion } from "./lint";
import { lint, TsVersion } from "./lint";
import { getCompilerOptions, packageNameFromPath } from "./util";
import { getTypesVersions } from "@definitelytyped/header-parser";

Expand Down Expand Up @@ -209,7 +209,6 @@ async function testTypesVersion(
tsLocal: string | undefined,
isLatest: boolean,
): Promise<void> {
checkTslintJson(dirPath);
const tsconfigErrors = checkTsconfig(dirPath, getCompilerOptions(dirPath));
if (tsconfigErrors.length > 0) {
throw new Error("\n\t* " + tsconfigErrors.join("\n\t* "));
Expand Down
43 changes: 11 additions & 32 deletions packages/dtslint/src/lint.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { TypeScriptVersion } from "@definitelytyped/typescript-versions";
import { typeScriptPath, withoutStart } from "@definitelytyped/utils";
import assert = require("assert");
import fs from "fs";
import { join as joinPaths, normalize } from "path";
import { Linter } from "tslint";
import { ESLint } from "eslint";
import * as TsType from "typescript";

import { readJson } from "./util";
import { createProgram } from "./createProgram";

export async function lint(
dirPath: string,
Expand All @@ -22,12 +20,11 @@ export async function lint(
require.resolve("@typescript-eslint/typescript-estree", { paths: [dirPath] }),
) as typeof import("@typescript-eslint/typescript-estree");
process.env.TSESTREE_SINGLE_RUN = "true";
// TODO: To remove tslint, replace this with a ts.createProgram (probably)
const lintProgram = Linter.createProgram(tsconfigPath);
const lintProgram = createProgram(tsconfigPath);

// TODO: To port expect-rule, eslint's config will also need to include [minVersion, maxVersion]
// Also: expect-rule should be renamed to expect-type or check-type or something
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
const esfiles = [];
const files = [];

for (const file of lintProgram.getSourceFiles()) {
if (lintProgram.isSourceFileDefaultLibrary(file)) {
Expand All @@ -36,7 +33,7 @@ export async function lint(

const { fileName, text } = file;
if (!fileName.includes("node_modules")) {
const err = testNoLintDisables("tslint:disable", text) || testNoLintDisables("eslint-disable", text);
const err = testNoLintDisables(text);
if (err) {
const { pos, message } = err;
const place = file.getLineAndCharacterOfPosition(pos);
Expand All @@ -47,7 +44,7 @@ export async function lint(
// 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))) {
esfiles.push(fileName);
files.push(fileName);
}
}
let output = "";
Expand Down Expand Up @@ -96,8 +93,8 @@ export async function lint(

const eslint = new ESLint(options);
const formatter = await eslint.loadFormatter("stylish");
const eresults = await eslint.lintFiles(esfiles);
output += formatter.format(eresults);
const results = await eslint.lintFiles(files);
output += formatter.format(results);
estree.clearCaches();

return output;
Expand Down Expand Up @@ -131,7 +128,8 @@ interface Err {
pos: number;
message: string;
}
function testNoLintDisables(disabler: "tslint:disable" | "eslint-disable", text: string): Err | undefined {
function testNoLintDisables(text: string): Err | undefined {
const disabler = "eslint-disable";
let lastIndex = 0;
while (true) {
const pos = text.indexOf(disabler, lastIndex);
Expand All @@ -141,36 +139,17 @@ function testNoLintDisables(disabler: "tslint:disable" | "eslint-disable", text:
const end = pos + disabler.length;
const nextChar = text.charAt(end);
const nextChar2 = text.charAt(end + 1);
if (
nextChar !== "-" &&
!(disabler === "tslint:disable" && nextChar === ":") &&
!(disabler === "eslint-disable" && nextChar === " " && nextChar2 !== "*")
) {
if (nextChar !== "-" && !(nextChar === " " && nextChar2 !== "*")) {
const message =
`'${disabler}' is forbidden. ` +
"Per-line and per-rule disabling is allowed, for example: " +
"'tslint:disable:rulename', tslint:disable-line' and 'tslint:disable-next-line' are allowed.";
"'eslint-disable:rulename', eslint-disable-line' and 'eslint-disable-next-line' are allowed.";
return { pos, message };
}
lastIndex = end;
}
}

export function checkTslintJson(dirPath: string): void {
const configPath = getConfigPath(dirPath);
const shouldExtend = "@definitelytyped/dtslint/dt.json";
if (!fs.existsSync(configPath)) {
throw new Error(`Missing \`tslint.json\` that contains \`{ "extends": "${shouldExtend}" }\`.`);
}
if (readJson(configPath).extends !== shouldExtend) {
throw new Error(`'tslint.json' must extend "${shouldExtend}"`);
}
}

function getConfigPath(dirPath: string): string {
return joinPaths(dirPath, "tslint.json");
}

function range(minVersion: TsVersion, maxVersion: TsVersion): readonly TsVersion[] {
if (minVersion === "local") {
assert(maxVersion === "local");
Expand Down