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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace TSLint with ESLint #119

Merged
merged 1 commit into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .eslintignore
@@ -0,0 +1 @@
source/test/fixtures
9 changes: 9 additions & 0 deletions .eslintrc
@@ -0,0 +1,9 @@
{
"extends": ["xo", "xo-typescript"],
"parserOptions": {
"project": "tsconfig.tsd.json"
},
"rules": {
"@typescript-eslint/comma-dangle": "off"
}
}
12 changes: 8 additions & 4 deletions package.json
Expand Up @@ -21,7 +21,8 @@
"test": "npm run lint && ava",
"build": "npm run clean && tsc --project tsconfig.tsd.json && chmod +x dist/cli.js",
"clean": "del-cli dist",
"lint": "tslint -p tsconfig.tsd.json --format stylish"
"lint": "eslint 'source/**/*'",
"lint:fix": "eslint --fix 'source/**/*'"
},
"files": [
"dist/index.js",
Expand Down Expand Up @@ -51,15 +52,18 @@
"@ava/typescript": "^1.1.1",
"@types/node": "^14.0.0",
"@types/react": "^16.9.2",
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"ava": "^3.8.2",
"cpy-cli": "^3.0.0",
"del-cli": "^3.0.0",
"eslint": "^7.27.0",
"eslint-config-xo": "^0.36.0",
"eslint-config-xo-typescript": "^0.41.1",
"execa": "^5.0.0",
"react": "^16.9.0",
"rxjs": "^6.5.3",
"tslint": "^5.11.0",
"tslint-xo": "^0.9.0",
"typescript": "^4.1.5"
"typescript": "^4.3.2"
},
"ava": {
"timeout": "2m",
Expand Down
9 changes: 6 additions & 3 deletions source/cli.ts
Expand Up @@ -16,7 +16,7 @@ const cli = meow(`
✖ 10:20 Argument of type string is not assignable to parameter of type number.
`);

(async () => { // tslint:disable-line:no-floating-promises
(async () => {
try {
const options = cli.input.length > 0 ? {cwd: cli.input[0]} : undefined;

Expand All @@ -25,8 +25,11 @@ const cli = meow(`
if (diagnostics.length > 0) {
throw new Error(formatter(diagnostics));
}
} catch (error) {
console.error(error.message);
} catch (error: unknown) {
if (error && typeof (error as Error).message === 'string') {
console.error((error as Error).message);
}

process.exit(1);
}
})();
35 changes: 19 additions & 16 deletions source/lib/assertions/assert.ts
@@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */

/**
* Check that the type of `value` is identical to type `T`.
*
* @param value - Value that should be identical to type `T`.
*/
// @ts-ignore
export const expectType = <T>(value: T) => { // tslint:disable-line:no-unused
// @ts-expect-error
export const expectType = <T>(value: T) => {
// Do nothing, the TypeScript compiler handles this for us
};

Expand All @@ -13,8 +15,9 @@ export const expectType = <T>(value: T) => { // tslint:disable-line:no-unused
*
* @param value - Value that should be identical to type `T`.
*/
// @ts-ignore
export const expectNotType = <T>(value: any) => { // tslint:disable-line:no-unused
// @ts-expect-error
export const expectNotType = <T>(value: any) => {
// eslint-disable-next-line no-warning-comments
// TODO Use a `not T` type when possible https://github.com/microsoft/TypeScript/pull/29317
// Do nothing, the TypeScript compiler handles this for us
};
Expand All @@ -24,8 +27,8 @@ export const expectNotType = <T>(value: any) => { // tslint:disable-line:no-unu
*
* @param value - Value that should be assignable to type `T`.
*/
// @ts-ignore
export const expectAssignable = <T>(value: T) => { // tslint:disable-line:no-unused
// @ts-expect-error
export const expectAssignable = <T>(value: T) => {
// Do nothing, the TypeScript compiler handles this for us
};

Expand All @@ -34,8 +37,8 @@ export const expectAssignable = <T>(value: T) => { // tslint:disable-line:no-un
*
* @param value - Value that should not be assignable to type `T`.
*/
// @ts-ignore
export const expectNotAssignable = <T>(value: any) => { // tslint:disable-line:no-unused
// @ts-expect-error
export const expectNotAssignable = <T>(value: any) => {
// Do nothing, the TypeScript compiler handles this for us
};

Expand All @@ -44,8 +47,8 @@ export const expectNotAssignable = <T>(value: any) => { // tslint:disable-line:
*
* @param value - Value that should be checked.
*/
// @ts-ignore
export const expectError = <T = any>(value: T) => { // tslint:disable-line:no-unused
// @ts-expect-error
export const expectError = <T = any>(value: T) => {
// Do nothing, the TypeScript compiler handles this for us
};

Expand All @@ -54,8 +57,8 @@ export const expectError = <T = any>(value: T) => { // tslint:disable-line:no-u
*
* @param expression - Expression that should be marked as `@deprecated`.
*/
// @ts-ignore
export const expectDeprecated = (expression: any) => { // tslint:disable-line:no-unused
// @ts-expect-error
export const expectDeprecated = (expression: any) => {
// Do nothing, the TypeScript compiler handles this for us
};

Expand All @@ -64,8 +67,8 @@ export const expectDeprecated = (expression: any) => { // tslint:disable-line:n
*
* @param expression - Expression that should not be marked as `@deprecated`.
*/
// @ts-ignore
export const expectNotDeprecated = (expression: any) => { // tslint:disable-line:no-unused
// @ts-expect-error
export const expectNotDeprecated = (expression: any) => {
// Do nothing, the TypeScript compiler handles this for us
};

Expand All @@ -74,7 +77,7 @@ export const expectNotDeprecated = (expression: any) => { // tslint:disable-lin
*
* @param expression - Expression whose type should be printed as a warning.
*/
// @ts-ignore
export const printType = (expression: any) => { // tslint:disable-line:no-unused
// @ts-expect-error
export const printType = (expression: any) => {
// Do nothing, the TypeScript compiler handles this for us
};
2 changes: 1 addition & 1 deletion source/lib/assertions/handlers/expect-deprecated.ts
Expand Up @@ -29,7 +29,7 @@ const expectDeprecatedHelper = (options: Options): Handler => {

const message = tsutils.expressionToString(checker, argument);

diagnostics.push(makeDiagnostic(node, options.message(message || '?')));
diagnostics.push(makeDiagnostic(node, options.message(message ?? '?')));
}

return diagnostics;
Expand Down
12 changes: 7 additions & 5 deletions source/lib/compiler.ts
@@ -1,8 +1,7 @@
import {
flattenDiagnosticMessageText,
createProgram,
Diagnostic as TSDiagnostic,
SourceFile
Diagnostic as TSDiagnostic
} from '@tsd/typescript';
import {ExpectedError, extractAssertions, parseErrorAssertionToLocation} from './parser';
import {Diagnostic, DiagnosticCode, Context, Location} from './interfaces';
Expand Down Expand Up @@ -63,10 +62,12 @@ const ignoreDiagnostic = (
return 'preserve';
}

const diagnosticFileName = (diagnostic.file as SourceFile).fileName;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const diagnosticFileName = diagnostic.file!.fileName;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we guard diagnostic.file? Or is its optionality incorrect?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem to have caused any problems yet. I don't really know whether it may actually be undefined.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's not really related to this PR. Just thought I'd bring it up. We should be careful about ! in general unless we are 100% sure it cannot be undefined or null.


for (const [location] of expectedErrors) {
const start = diagnostic.start as number;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const start = diagnostic.start!;

if (diagnosticFileName === location.fileName && start > location.start && start < location.end) {
return location;
Expand Down Expand Up @@ -116,7 +117,8 @@ export const getDiagnostics = (context: Context): Diagnostic[] => {
continue;
}

const position = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start as number);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const position = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);

diagnostics.push({
fileName: diagnostic.file.fileName,
Expand Down
10 changes: 5 additions & 5 deletions source/lib/config.ts
Expand Up @@ -10,20 +10,20 @@ import {
parseJsonSourceFileConfigFileContent,
ModuleKind
} from '@tsd/typescript';
import {Config, RawConfig, RawCompilerOptions} from './interfaces';
import {Config, PackageJsonWithTsdConfig, RawCompilerOptions} from './interfaces';

/**
* Load the configuration settings.
*
* @param pkg - The package.json object.
* @returns The config object.
*/
export default (pkg: {tsd?: RawConfig}, cwd: string): Config => {
const pkgConfig = pkg.tsd || {};
export default (pkg: PackageJsonWithTsdConfig, cwd: string): Config => {
const pkgConfig = pkg.tsd ?? {};

const tsConfigCompilerOptions = getOptionsFromTsConfig(cwd);
const packageJsonCompilerOptions = parseCompilerConfigObject(
pkgConfig.compilerOptions || {},
pkgConfig.compilerOptions ?? {},
cwd
);

Expand Down Expand Up @@ -70,5 +70,5 @@ function parseCompilerConfigObject(compilerOptions: RawCompilerOptions, cwd: str
}

function parseRawLibs(libs: string[], cwd: string): string[] {
return parseCompilerConfigObject({lib: libs}, cwd).lib || [];
return parseCompilerConfigObject({lib: libs}, cwd).lib ?? [];
}
14 changes: 11 additions & 3 deletions source/lib/formatter.ts
@@ -1,14 +1,21 @@
import * as formatter from 'eslint-formatter-pretty';
import {Diagnostic} from './interfaces';

interface FileWithDiagnostics {
filePath: string;
errorCount: number;
warningCount: number;
messages: Diagnostic[];
}

/**
* Format the TypeScript diagnostics to a human readable output.
*
* @param diagnostics - List of TypeScript diagnostics.
* @returns Beautiful diagnostics output
*/
export default (diagnostics: Diagnostic[]) => {
const fileMap = new Map<string, any>();
export default (diagnostics: Diagnostic[]): string => {
const fileMap = new Map<string, FileWithDiagnostics>();

for (const diagnostic of diagnostics) {
let entry = fileMap.get(diagnostic.fileName);
Expand All @@ -28,5 +35,6 @@ export default (diagnostics: Diagnostic[]) => {
entry.messages.push(diagnostic);
}

return formatter(Array.from(fileMap.values()));
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
return String(formatter([...fileMap.values()]));
};
10 changes: 5 additions & 5 deletions source/lib/index.ts
Expand Up @@ -5,15 +5,15 @@ import * as globby from 'globby';
import {getDiagnostics as getTSDiagnostics} from './compiler';
import loadConfig from './config';
import getCustomDiagnostics from './rules';
import {Context, Config} from './interfaces';
import {Context, Config, Diagnostic, PackageJsonWithTsdConfig} from './interfaces';

export interface Options {
cwd: string;
typingsFile?: string;
testFiles?: readonly string[];
}

const findTypingsFile = async (pkg: any, options: Options) => {
const findTypingsFile = async (pkg: PackageJsonWithTsdConfig, options: Options): Promise<string> => {
const typings =
options.typingsFile ||
pkg.types ||
Expand Down Expand Up @@ -78,15 +78,15 @@ const findTestFiles = async (typingsFilePath: string, options: Options & {config
*
* @returns A promise which resolves the diagnostics of the type definition.
*/
export default async (options: Options = {cwd: process.cwd()}) => {
export default async (options: Options = {cwd: process.cwd()}): Promise<Diagnostic[]> => {
const pkgResult = await readPkgUp({cwd: options.cwd});

if (!pkgResult) {
throw new Error('No `package.json` file found. Make sure you are running the command in a Node.js project.');
}

const pkg = pkgResult.packageJson;
const config = loadConfig(pkg as any, options.cwd);
const pkg = pkgResult.packageJson as PackageJsonWithTsdConfig;
const config = loadConfig(pkg, options.cwd);

// Look for a typings file, otherwise use `index.d.ts` in the root directory. If the file is not found, throw an error.
const typingsFile = await findTypingsFile(pkg, options);
Expand Down
11 changes: 7 additions & 4 deletions source/lib/interfaces.ts
@@ -1,8 +1,7 @@
import {CompilerOptions} from '@tsd/typescript';
import {NormalizedPackageJson} from 'read-pkg-up';

export interface RawCompilerOptions {
[option: string]: any;
}
export type RawCompilerOptions = Record<string, any>;

export interface Config<Options = CompilerOptions> {
directory: string;
Expand All @@ -11,9 +10,13 @@ export interface Config<Options = CompilerOptions> {

export type RawConfig = Partial<Config<RawCompilerOptions>>;

export type PackageJsonWithTsdConfig = NormalizedPackageJson & {
tsd?: RawConfig;
};

export interface Context {
cwd: string;
pkg: any;
pkg: PackageJsonWithTsdConfig;
typingsFile: string;
testFiles: string[];
config: Config;
Expand Down
2 changes: 1 addition & 1 deletion source/lib/parser.ts
Expand Up @@ -23,7 +23,7 @@ export const extractAssertions = (program: Program): Map<Assertion, Set<CallExpr
if (assertionFnNames.has(identifier)) {
const assertion = identifier as Assertion;

const nodes = assertions.get(assertion) || new Set<CallExpression>();
const nodes = assertions.get(assertion) ?? new Set<CallExpression>();

nodes.add(node);

Expand Down
2 changes: 1 addition & 1 deletion source/lib/rules/files-property.ts
Expand Up @@ -43,7 +43,7 @@ export default (context: Context): Diagnostic[] => {
function processGitIgnoreStylePatterns(patterns: readonly string[]): string[] {
const processedPatterns = patterns
.map(pattern => {
const [negatePatternMatch] = pattern.match(/^!+/) || [];
const [negatePatternMatch] = /^!+/.exec(pattern) ?? [];
const negationMarkersCount = negatePatternMatch ? negatePatternMatch.length : 0;

return [
Expand Down
6 changes: 3 additions & 3 deletions source/lib/utils/typescript.ts
Expand Up @@ -8,9 +8,9 @@ import {TypeChecker, Expression, isCallLikeExpression, JSDocTagInfo} from '@tsd/
* @return A unique Set of JSDoc tags or `undefined` if they couldn't be resolved.
*/
export const resolveJSDocTags = (checker: TypeChecker, expression: Expression): Map<string, JSDocTagInfo> | undefined => {
const ref = isCallLikeExpression(expression)
? checker.getResolvedSignature(expression)
: checker.getSymbolAtLocation(expression);
const ref = isCallLikeExpression(expression) ?
checker.getResolvedSignature(expression) :
checker.getSymbolAtLocation(expression);

if (!ref) {
return;
Expand Down
5 changes: 3 additions & 2 deletions source/test/test.ts
Expand Up @@ -370,7 +370,7 @@ test('includes extended config files along with found ones', async t => {
test('errors in libs from node_modules are not reported', async t => {
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/exclude-node-modules')});

const [nodeModuleDiagnostics, testFileDiagnostics, otherDiagnostics] = diagnostics.reduce(
const [nodeModuleDiagnostics, testFileDiagnostics, otherDiagnostics] = diagnostics.reduce<Diagnostic[][]>(
([nodeModuleDiags, testFileDiags, otherDiags], diagnostic) => {
if (/[/\\]node_modules[/\\]/.test(diagnostic.fileName)) {
nodeModuleDiags.push(diagnostic);
Expand All @@ -379,9 +379,10 @@ test('errors in libs from node_modules are not reported', async t => {
} else {
otherDiags.push(diagnostic);
}

return [nodeModuleDiags, testFileDiags, otherDiags];
},
[[], [], []] as Diagnostic[][]
[[], [], []]
);

t.deepEqual(
Expand Down
1 change: 1 addition & 0 deletions source/types.d.ts
@@ -0,0 +1 @@
declare module 'eslint-formatter-pretty';