Skip to content

Commit af70a59

Browse files
authored
feat(eslint-plugin): Add rule no-reference-import (#625)
* feat(eslint-plugin): added no-reference-import rule * docs: added docs for no-reference-import * fix(eslint-plugin): collect references when visiting the program node * feat(eslint-plugin): updated rule to cover more reference directives * fix(eslint-plugin): deprecated rule and added coverage * Update README.md
1 parent 34e7d1e commit af70a59

9 files changed

+327
-6
lines changed

packages/eslint-plugin/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e
159159
| [`@typescript-eslint/no-parameter-properties`](./docs/rules/no-parameter-properties.md) | Disallow the use of parameter properties in class constructors | :heavy_check_mark: | | |
160160
| [`@typescript-eslint/no-require-imports`](./docs/rules/no-require-imports.md) | Disallows invocation of `require()` | | | |
161161
| [`@typescript-eslint/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` | | | |
162-
| [`@typescript-eslint/no-triple-slash-reference`](./docs/rules/no-triple-slash-reference.md) | Disallow `/// <reference path="" />` comments | :heavy_check_mark: | | |
163162
| [`@typescript-eslint/no-type-alias`](./docs/rules/no-type-alias.md) | Disallow the use of type aliases | | | |
164163
| [`@typescript-eslint/no-unnecessary-qualifier`](./docs/rules/no-unnecessary-qualifier.md) | Warns when a namespace qualifier is unnecessary | | :wrench: | :thought_balloon: |
165164
| [`@typescript-eslint/no-unnecessary-type-assertion`](./docs/rules/no-unnecessary-type-assertion.md) | Warns if a type assertion does not change the type of an expression | | :wrench: | :thought_balloon: |
@@ -179,6 +178,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e
179178
| [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string | | | :thought_balloon: |
180179
| [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | |
181180
| [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Restricts the types allowed in boolean expressions | | | :thought_balloon: |
181+
| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | | | |
182182
| [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | :heavy_check_mark: | :wrench: | |
183183
| [`@typescript-eslint/unbound-method`](./docs/rules/unbound-method.md) | Enforces unbound methods are called with their expected scope | | | :thought_balloon: |
184184
| [`@typescript-eslint/unified-signatures`](./docs/rules/unified-signatures.md) | Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter | | | |

packages/eslint-plugin/docs/rules/no-triple-slash-reference.md

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ A triple-slash reference directive is a comment beginning with three slashes fol
1212
ES6 Modules handle this now:
1313
`import animal from "./Animal"`
1414

15+
## DEPRECATED - this rule has been deprecated in favour of [`triple-slash-reference`](./triple-slash-reference.md)
16+
1517
## Rule Details
1618

1719
Does not allow the use of `/// <reference />` comments.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Sets preference level for triple slash directives versus ES6-style import declarations. (triple-slash-reference)
2+
3+
Use of triple-slash reference type directives is discouraged in favor of the newer `import` style. This rule allows you to ban use of `/// <reference path="" />`, `/// <reference types="" />`, or `/// <reference lib="" />` directives.
4+
5+
Consider using this rule in place of [`no-triple-slash-reference`](./no-triple-slash-reference.md) which has been deprecated.
6+
7+
## Rule Details
8+
9+
With `{ "path": "never", "types": "never", "lib": "never" }` options set, the following will all be **incorrect** usage:
10+
11+
```ts
12+
/// <reference path="foo" />
13+
/// <reference types="bar" />
14+
/// <reference lib="baz" />
15+
```
16+
17+
Examples of **incorrect** code for the `{ "types": "prefer-import" }` option. Note that these are only errors when **both** stlyes are used for the **same** module:
18+
19+
```ts
20+
/// <reference types="foo" />
21+
import * as foo from 'foo';
22+
```
23+
24+
```ts
25+
/// <reference types="foo" />
26+
import foo = require('foo');
27+
```
28+
29+
With `{ "path": "always", "types": "always", "lib": "always" }` options set, the following will all be **correct** usage:
30+
31+
```ts
32+
/// <reference path="foo" />
33+
/// <reference types="bar" />
34+
/// <reference lib="baz" />
35+
```
36+
37+
Examples of **correct** code for the `{ "types": "prefer-import" }` option:
38+
39+
```ts
40+
import * as foo from 'foo';
41+
```
42+
43+
```ts
44+
import foo = require('foo');
45+
```
46+
47+
## When To Use It
48+
49+
If you want to ban use of one or all of the triple slash reference directives, or any time you might use triple-slash type reference directives and ES6 import declarations in the same file.
50+
51+
## When Not To Use It
52+
53+
If you want to use all flavors of triple slash reference directives.
54+
55+
## Compatibility
56+
57+
- TSLint: [no-reference](http://palantir.github.io/tslint/rules/no-reference/)
58+
- TSLint: [no-reference-import](https://palantir.github.io/tslint/rules/no-reference-import/)

packages/eslint-plugin/src/configs/all.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
"@typescript-eslint/no-parameter-properties": "error",
4343
"@typescript-eslint/no-require-imports": "error",
4444
"@typescript-eslint/no-this-alias": "error",
45-
"@typescript-eslint/no-triple-slash-reference": "error",
4645
"@typescript-eslint/no-type-alias": "error",
4746
"@typescript-eslint/no-unnecessary-qualifier": "error",
4847
"@typescript-eslint/no-unnecessary-type-assertion": "error",
@@ -65,6 +64,7 @@
6564
"@typescript-eslint/restrict-plus-operands": "error",
6665
"semi": "off",
6766
"@typescript-eslint/semi": "error",
67+
"@typescript-eslint/triple-slash-reference": "error",
6868
"@typescript-eslint/type-annotation-spacing": "error",
6969
"@typescript-eslint/unbound-method": "error",
7070
"@typescript-eslint/unified-signatures": "error"

packages/eslint-plugin/src/rules/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import requireArraySortCompare from './require-array-sort-compare';
5454
import restrictPlusOperands from './restrict-plus-operands';
5555
import semi from './semi';
5656
import strictBooleanExpressions from './strict-boolean-expressions';
57+
import tripleSlashReference from './triple-slash-reference';
5758
import typeAnnotationSpacing from './type-annotation-spacing';
5859
import unboundMethod from './unbound-method';
5960
import unifiedSignatures from './unified-signatures';
@@ -115,6 +116,7 @@ export default {
115116
'restrict-plus-operands': restrictPlusOperands,
116117
semi: semi,
117118
'strict-boolean-expressions': strictBooleanExpressions,
119+
'triple-slash-reference': tripleSlashReference,
118120
'type-annotation-spacing': typeAnnotationSpacing,
119121
'unbound-method': unboundMethod,
120122
'unified-signatures': unifiedSignatures,

packages/eslint-plugin/src/rules/no-triple-slash-reference.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ export default util.createRule({
1010
recommended: 'error',
1111
},
1212
schema: [],
13+
deprecated: true,
14+
replacedBy: ['triple-slash-reference'],
1315
messages: {
14-
tripleSlashReference: 'Do not use a triple slash reference.',
16+
noTripleSlashReference: 'Do not use a triple slash reference.',
1517
},
1618
},
1719
defaultOptions: [],
@@ -30,7 +32,7 @@ export default util.createRule({
3032
if (referenceRegExp.test(comment.value)) {
3133
context.report({
3234
node: comment,
33-
messageId: 'tripleSlashReference',
35+
messageId: 'noTripleSlashReference',
3436
});
3537
}
3638
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import * as util from '../util';
2+
import {
3+
Literal,
4+
Node,
5+
TSExternalModuleReference,
6+
} from '@typescript-eslint/typescript-estree/dist/ts-estree/ts-estree';
7+
import { TSESTree } from '@typescript-eslint/typescript-estree';
8+
9+
type Options = [
10+
{
11+
lib?: 'always' | 'never';
12+
path?: 'always' | 'never';
13+
types?: 'always' | 'never' | 'prefer-import';
14+
}
15+
];
16+
type MessageIds = 'tripleSlashReference';
17+
18+
export default util.createRule<Options, MessageIds>({
19+
name: 'triple-slash-reference',
20+
meta: {
21+
type: 'suggestion',
22+
docs: {
23+
description:
24+
'Sets preference level for triple slash directives versus ES6-style import declarations',
25+
category: 'Best Practices',
26+
recommended: false,
27+
},
28+
messages: {
29+
tripleSlashReference:
30+
'Do not use a triple slash reference for {{module}}, use `import` style instead.',
31+
},
32+
schema: [
33+
{
34+
type: 'object',
35+
properties: {
36+
lib: {
37+
enum: ['always', 'never'],
38+
},
39+
path: {
40+
enum: ['always', 'never'],
41+
},
42+
types: {
43+
enum: ['always', 'never', 'prefer-import'],
44+
},
45+
},
46+
additionalProperties: false,
47+
},
48+
],
49+
},
50+
defaultOptions: [
51+
{
52+
lib: 'always',
53+
path: 'never',
54+
types: 'prefer-import',
55+
},
56+
],
57+
create(context, [{ lib, path, types }]) {
58+
let programNode: Node;
59+
const sourceCode = context.getSourceCode();
60+
const references: ({
61+
comment: TSESTree.Comment;
62+
importName: string;
63+
})[] = [];
64+
65+
function hasMatchingReference(source: Literal) {
66+
references.forEach(reference => {
67+
if (reference.importName === source.value) {
68+
context.report({
69+
node: reference.comment,
70+
messageId: 'tripleSlashReference',
71+
data: {
72+
module: reference.importName,
73+
},
74+
});
75+
}
76+
});
77+
}
78+
return {
79+
ImportDeclaration(node) {
80+
if (programNode) {
81+
const source = node.source as Literal;
82+
hasMatchingReference(source);
83+
}
84+
},
85+
TSImportEqualsDeclaration(node) {
86+
if (programNode) {
87+
const source = (node.moduleReference as TSExternalModuleReference)
88+
.expression as Literal;
89+
hasMatchingReference(source);
90+
}
91+
},
92+
Program(node) {
93+
if (lib === 'always' && path === 'always' && types == 'always') {
94+
return;
95+
}
96+
programNode = node;
97+
const referenceRegExp = /^\/\s*<reference\s*(types|path|lib)\s*=\s*["|'](.*)["|']/;
98+
const commentsBefore = sourceCode.getCommentsBefore(programNode);
99+
100+
commentsBefore.forEach(comment => {
101+
if (comment.type !== 'Line') {
102+
return;
103+
}
104+
const referenceResult = referenceRegExp.exec(comment.value);
105+
106+
if (referenceResult) {
107+
if (
108+
(referenceResult[1] === 'types' && types === 'never') ||
109+
(referenceResult[1] === 'path' && path === 'never') ||
110+
(referenceResult[1] === 'lib' && lib === 'never')
111+
) {
112+
context.report({
113+
node: comment,
114+
messageId: 'tripleSlashReference',
115+
data: {
116+
module: referenceResult[2],
117+
},
118+
});
119+
return;
120+
}
121+
if (referenceResult[1] === 'types' && types === 'prefer-import') {
122+
references.push({ comment, importName: referenceResult[2] });
123+
}
124+
}
125+
});
126+
},
127+
};
128+
},
129+
});

packages/eslint-plugin/tests/rules/no-triple-slash-reference.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ let a
2222
code: '/// <reference path="Animal" />',
2323
errors: [
2424
{
25-
messageId: 'tripleSlashReference',
25+
messageId: 'noTripleSlashReference',
2626
line: 1,
2727
column: 1,
2828
},
@@ -36,7 +36,7 @@ let a
3636
parser: '@typescript-eslint/parser',
3737
errors: [
3838
{
39-
messageId: 'tripleSlashReference',
39+
messageId: 'noTripleSlashReference',
4040
line: 2,
4141
column: 1,
4242
},

0 commit comments

Comments
 (0)