Skip to content

Commit 370ac72

Browse files
authored
feat: make utils/TSESLint export typed classes instead of just types (typescript-eslint#526)
1 parent 67537b8 commit 370ac72

32 files changed

+479
-343
lines changed

.eslintrc.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
"es6": true,
66
"node": true
77
},
8-
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
8+
"extends": [
9+
"eslint:recommended",
10+
"plugin:@typescript-eslint/eslint-recommended",
11+
"plugin:@typescript-eslint/recommended"
12+
],
913
"rules": {
1014
"comma-dangle": ["error", "always-multiline"],
1115
"curly": ["error", "all"],
12-
"no-dupe-class-members": "off",
1316
"no-mixed-operators": "error",
1417
"no-console": "off",
1518
"no-dupe-class-members": "off",

packages/eslint-plugin-tslint/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@
2727
"typecheck": "tsc --noEmit"
2828
},
2929
"dependencies": {
30+
"@typescript-eslint/experimental-utils": "1.9.0",
3031
"lodash.memoize": "^4.1.2"
3132
},
3233
"peerDependencies": {
3334
"eslint": "^5.0.0",
3435
"tslint": "^5.0.0"
3536
},
3637
"devDependencies": {
37-
"@types/eslint": "^4.16.3",
38+
"@types/json-schema": "^7.0.3",
3839
"@types/lodash.memoize": "^4.1.4",
3940
"@typescript-eslint/parser": "1.9.0"
4041
}
+4-155
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,9 @@
1-
import { Rule } from 'eslint';
2-
import memoize from 'lodash.memoize';
3-
import { Configuration, RuleSeverity } from 'tslint';
4-
import { Program } from 'typescript';
5-
import { CustomLinter } from './custom-linter';
6-
import { ParserServices } from '@typescript-eslint/typescript-estree';
7-
8-
//------------------------------------------------------------------------------
9-
// Plugin Definition
10-
//------------------------------------------------------------------------------
11-
12-
type RawRuleConfig =
13-
| null
14-
| undefined
15-
| boolean
16-
| any[]
17-
| {
18-
severity?: RuleSeverity | 'warn' | 'none' | 'default';
19-
options?: any;
20-
};
21-
22-
interface RawRulesConfig {
23-
[key: string]: RawRuleConfig;
24-
}
1+
import configRule from './rules/config';
252

263
/**
27-
* Construct a configFile for TSLint
4+
* Expose a single rule called "config", which will be accessed in the user's eslint config files
5+
* via "tslint/config"
286
*/
29-
const tslintConfig = memoize(
30-
(
31-
lintFile: string,
32-
tslintRules: RawRulesConfig,
33-
tslintRulesDirectory: string[],
34-
) => {
35-
if (lintFile != null) {
36-
return Configuration.loadConfigurationFromPath(lintFile);
37-
}
38-
return Configuration.parseConfigFile({
39-
rules: tslintRules || {},
40-
rulesDirectory: tslintRulesDirectory || [],
41-
});
42-
},
43-
(lintFile: string | undefined, tslintRules = {}, tslintRulesDirectory = []) =>
44-
`${lintFile}_${Object.keys(tslintRules).join(',')}_${
45-
tslintRulesDirectory.length
46-
}`,
47-
);
48-
497
export const rules = {
50-
/**
51-
* Expose a single rule called "config", which will be accessed in the user's eslint config files
52-
* via "tslint/config"
53-
*/
54-
config: {
55-
meta: {
56-
docs: {
57-
description:
58-
'Wraps a TSLint configuration and lints the whole source using TSLint',
59-
category: 'TSLint',
60-
},
61-
schema: [
62-
{
63-
type: 'object',
64-
properties: {
65-
rules: {
66-
type: 'object',
67-
/**
68-
* No fixed schema properties for rules, as this would be a permanently moving target
69-
*/
70-
additionalProperties: true,
71-
},
72-
rulesDirectory: {
73-
type: 'array',
74-
items: {
75-
type: 'string',
76-
},
77-
},
78-
lintFile: {
79-
type: 'string',
80-
},
81-
},
82-
additionalProperties: false,
83-
},
84-
],
85-
},
86-
create(context: Rule.RuleContext) {
87-
const fileName = context.getFilename();
88-
const sourceCode = context.getSourceCode().text;
89-
const parserServices: ParserServices | undefined = context.parserServices;
90-
91-
/**
92-
* The user needs to have configured "project" in their parserOptions
93-
* for @typescript-eslint/parser
94-
*/
95-
if (!parserServices || !parserServices.program) {
96-
throw new Error(
97-
`You must provide a value for the "parserOptions.project" property for @typescript-eslint/parser`,
98-
);
99-
}
100-
101-
/**
102-
* The TSLint rules configuration passed in by the user
103-
*/
104-
const {
105-
rules: tslintRules,
106-
rulesDirectory: tslintRulesDirectory,
107-
lintFile,
108-
} = context.options[0];
109-
110-
const program: Program = parserServices.program;
111-
112-
/**
113-
* Create an instance of TSLint
114-
* Lint the source code using the configured TSLint instance, and the rules which have been
115-
* passed via the ESLint rule options for this rule (using "tslint/config")
116-
*/
117-
const tslintOptions = {
118-
formatter: 'json',
119-
fix: false,
120-
};
121-
const tslint = new CustomLinter(tslintOptions, program);
122-
const configuration = tslintConfig(
123-
lintFile,
124-
tslintRules,
125-
tslintRulesDirectory,
126-
);
127-
tslint.lint(fileName, sourceCode, configuration);
128-
129-
const result = tslint.getResult();
130-
131-
/**
132-
* Format the TSLint results for ESLint
133-
*/
134-
if (result.failures && result.failures.length) {
135-
result.failures.forEach(failure => {
136-
const start = failure.getStartPosition().getLineAndCharacter();
137-
const end = failure.getEndPosition().getLineAndCharacter();
138-
context.report({
139-
message: `${failure.getFailure()} (tslint:${failure.getRuleName()})`,
140-
loc: {
141-
start: {
142-
line: start.line + 1,
143-
column: start.character,
144-
},
145-
end: {
146-
line: end.line + 1,
147-
column: end.character,
148-
},
149-
},
150-
});
151-
});
152-
}
153-
154-
/**
155-
* Return an empty object for the ESLint rule
156-
*/
157-
return {};
158-
},
159-
},
8+
config: configRule,
1609
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import {
2+
ESLintUtils,
3+
ParserServices,
4+
} from '@typescript-eslint/experimental-utils';
5+
import memoize from 'lodash.memoize';
6+
import { Configuration, RuleSeverity } from 'tslint';
7+
import { CustomLinter } from '../custom-linter';
8+
9+
// note - cannot migrate this to an import statement because it will make TSC copy the package.json to the dist folder
10+
const version = require('../../package.json').version;
11+
12+
const createRule = ESLintUtils.RuleCreator(
13+
() =>
14+
`https://github.com/typescript-eslint/typescript-eslint/blob/v${version}/packages/eslint-plugin-tslint/README.md`,
15+
);
16+
export type RawRulesConfig = Record<
17+
string,
18+
| null
19+
| undefined
20+
| boolean
21+
| any[]
22+
| {
23+
severity?: RuleSeverity | 'warn' | 'none' | 'default';
24+
options?: any;
25+
}
26+
>;
27+
28+
export type MessageIds = 'failure';
29+
export type Options = [
30+
{
31+
rules?: RawRulesConfig;
32+
rulesDirectory?: string[];
33+
lintFile?: string;
34+
}
35+
];
36+
37+
/**
38+
* Construct a configFile for TSLint
39+
*/
40+
const tslintConfig = memoize(
41+
(
42+
lintFile?: string,
43+
tslintRules?: RawRulesConfig,
44+
tslintRulesDirectory?: string[],
45+
) => {
46+
if (lintFile != null) {
47+
return Configuration.loadConfigurationFromPath(lintFile);
48+
}
49+
return Configuration.parseConfigFile({
50+
rules: tslintRules || {},
51+
rulesDirectory: tslintRulesDirectory || [],
52+
});
53+
},
54+
(lintFile: string | undefined, tslintRules = {}, tslintRulesDirectory = []) =>
55+
`${lintFile}_${Object.keys(tslintRules).join(',')}_${
56+
tslintRulesDirectory.length
57+
}`,
58+
);
59+
60+
export default createRule<Options, MessageIds>({
61+
name: 'config',
62+
meta: {
63+
docs: {
64+
description:
65+
'Wraps a TSLint configuration and lints the whole source using TSLint',
66+
category: 'TSLint' as any,
67+
recommended: false,
68+
},
69+
type: 'problem',
70+
messages: {
71+
failure: '{{message}} (tslint:{{ruleName}})`',
72+
},
73+
schema: [
74+
{
75+
type: 'object',
76+
properties: {
77+
rules: {
78+
type: 'object',
79+
/**
80+
* No fixed schema properties for rules, as this would be a permanently moving target
81+
*/
82+
additionalProperties: true,
83+
},
84+
rulesDirectory: {
85+
type: 'array',
86+
items: {
87+
type: 'string',
88+
},
89+
},
90+
lintFile: {
91+
type: 'string',
92+
},
93+
},
94+
additionalProperties: false,
95+
},
96+
],
97+
},
98+
defaultOptions: [] as any,
99+
create(context) {
100+
const fileName = context.getFilename();
101+
const sourceCode = context.getSourceCode().text;
102+
const parserServices: ParserServices | undefined = context.parserServices;
103+
104+
/**
105+
* The user needs to have configured "project" in their parserOptions
106+
* for @typescript-eslint/parser
107+
*/
108+
if (!parserServices || !parserServices.program) {
109+
throw new Error(
110+
`You must provide a value for the "parserOptions.project" property for @typescript-eslint/parser`,
111+
);
112+
}
113+
114+
/**
115+
* The TSLint rules configuration passed in by the user
116+
*/
117+
const {
118+
rules: tslintRules,
119+
rulesDirectory: tslintRulesDirectory,
120+
lintFile,
121+
} = context.options[0];
122+
123+
const program = parserServices.program;
124+
125+
/**
126+
* Create an instance of TSLint
127+
* Lint the source code using the configured TSLint instance, and the rules which have been
128+
* passed via the ESLint rule options for this rule (using "tslint/config")
129+
*/
130+
const tslintOptions = {
131+
formatter: 'json',
132+
fix: false,
133+
};
134+
const tslint = new CustomLinter(tslintOptions, program);
135+
const configuration = tslintConfig(
136+
lintFile,
137+
tslintRules,
138+
tslintRulesDirectory,
139+
);
140+
tslint.lint(fileName, sourceCode, configuration);
141+
142+
const result = tslint.getResult();
143+
144+
/**
145+
* Format the TSLint results for ESLint
146+
*/
147+
if (result.failures && result.failures.length) {
148+
result.failures.forEach(failure => {
149+
const start = failure.getStartPosition().getLineAndCharacter();
150+
const end = failure.getEndPosition().getLineAndCharacter();
151+
context.report({
152+
messageId: 'failure',
153+
data: {
154+
message: failure.getFailure(),
155+
ruleName: failure.getRuleName(),
156+
},
157+
loc: {
158+
start: {
159+
line: start.line + 1,
160+
column: start.character,
161+
},
162+
end: {
163+
line: end.line + 1,
164+
column: end.character,
165+
},
166+
},
167+
});
168+
});
169+
}
170+
171+
/**
172+
* Return an empty object for the ESLint rule
173+
*/
174+
return {};
175+
},
176+
});

0 commit comments

Comments
 (0)