Skip to content

Commit

Permalink
feat(eslint-plugin): updated rule to cover more reference directives
Browse files Browse the repository at this point in the history
  • Loading branch information
Jesse Trinity committed Jun 22, 2019
1 parent ac3cd5d commit ecc65e1
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 171 deletions.
2 changes: 1 addition & 1 deletion packages/eslint-plugin/README.md
Expand Up @@ -157,7 +157,6 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e
| [`@typescript-eslint/no-non-null-assertion`](./docs/rules/no-non-null-assertion.md) | Disallows non-null assertions using the `!` postfix operator | :heavy_check_mark: | | |
| [`@typescript-eslint/no-object-literal-type-assertion`](./docs/rules/no-object-literal-type-assertion.md) | Forbids an object literal to appear in a type assertion expression | :heavy_check_mark: | | |
| [`@typescript-eslint/no-parameter-properties`](./docs/rules/no-parameter-properties.md) | Disallow the use of parameter properties in class constructors | :heavy_check_mark: | | |
| [`@typescript-eslint/no-reference-import`](./docs/rules/no-reference-import.md) | Disallow simultaneous use of `/// <reference type="" />` comments and ES6 style imports for the same module | | | |
| [`@typescript-eslint/no-require-imports`](./docs/rules/no-require-imports.md) | Disallows invocation of `require()` | | | |
| [`@typescript-eslint/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` | | | |
| [`@typescript-eslint/no-triple-slash-reference`](./docs/rules/no-triple-slash-reference.md) | Disallow `/// <reference path="" />` comments | :heavy_check_mark: | | |
Expand All @@ -178,6 +177,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e
| [`@typescript-eslint/require-array-sort-compare`](./docs/rules/require-array-sort-compare.md) | Enforce giving `compare` argument to `Array#sort` | | | :thought_balloon: |
| [`@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: |
| [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | |
| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | | | |
| [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | :heavy_check_mark: | :wrench: | |
| [`@typescript-eslint/unbound-method`](./docs/rules/unbound-method.md) | Enforces unbound methods are called with their expected scope | | | :thought_balloon: |
| [`@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 | | | |
Expand Down
37 changes: 0 additions & 37 deletions packages/eslint-plugin/docs/rules/no-reference-import.md

This file was deleted.

58 changes: 58 additions & 0 deletions packages/eslint-plugin/docs/rules/triple-slash-reference.md
@@ -0,0 +1,58 @@
# Sets preference level for triple slash directives versus ES6-style import declarations. (triple-slash-reference)

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.

If you use the `no-triple-slash-reference` rule, consider using this rule instead.

## Rule Details

With `{ "path": "never", "types": "never", "lib": "never" }` options set, the following will all be **incorrect** usage:

```ts
/// <reference path="foo" />
/// <reference types="bar" />
/// <reference lib="baz" />
```

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:

```ts
/// <reference types="foo" />
import * as foo from 'foo';
```

```ts
/// <reference types="foo" />
import foo = require('foo');
```

With `{ "path": "always", "types": "always", "lib": "always" }` options set, the following will all be **correct** usage:

```ts
/// <reference path="foo" />
/// <reference types="bar" />
/// <reference lib="baz" />
```

Examples of **correct** code for the `{ "types": "prefer-import" }` option:

```ts
import * as foo from 'foo';
```

```ts
import foo = require('foo');
```

## When To Use It

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.

## When Not To Use It

If you want to use all flavors of triple slash reference directives.

## Compatibility

- TSLint: [no-reference](http://palantir.github.io/tslint/rules/no-reference/)
- TSLint: [no-reference-import](https://palantir.github.io/tslint/rules/no-reference-import/)
4 changes: 2 additions & 2 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -31,7 +31,6 @@ import noNamespace from './no-namespace';
import noNonNullAssertion from './no-non-null-assertion';
import noObjectLiteralTypeAssertion from './no-object-literal-type-assertion';
import noParameterProperties from './no-parameter-properties';
import noReferenceImport from './no-reference-import';
import noRequireImports from './no-require-imports';
import noThisAlias from './no-this-alias';
import noTripleSlashReference from './no-triple-slash-reference';
Expand All @@ -53,6 +52,7 @@ import promiseFunctionAsync from './promise-function-async';
import requireArraySortCompare from './require-array-sort-compare';
import restrictPlusOperands from './restrict-plus-operands';
import semi from './semi';
import tripleSlashReference from './triple-slash-reference';
import typeAnnotationSpacing from './type-annotation-spacing';
import unboundMethod from './unbound-method';
import unifiedSignatures from './unified-signatures';
Expand Down Expand Up @@ -91,7 +91,6 @@ export default {
'no-non-null-assertion': noNonNullAssertion,
'no-object-literal-type-assertion': noObjectLiteralTypeAssertion,
'no-parameter-properties': noParameterProperties,
'no-reference-import': noReferenceImport,
'no-require-imports': noRequireImports,
'no-this-alias': noThisAlias,
'no-triple-slash-reference': noTripleSlashReference,
Expand All @@ -113,6 +112,7 @@ export default {
'require-array-sort-compare': requireArraySortCompare,
'restrict-plus-operands': restrictPlusOperands,
semi: semi,
'triple-slash-reference': tripleSlashReference,
'type-annotation-spacing': typeAnnotationSpacing,
'unbound-method': unboundMethod,
'unified-signatures': unifiedSignatures,
Expand Down
78 changes: 0 additions & 78 deletions packages/eslint-plugin/src/rules/no-reference-import.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/eslint-plugin/src/rules/no-triple-slash-reference.ts
Expand Up @@ -11,7 +11,7 @@ export default util.createRule({
},
schema: [],
messages: {
tripleSlashReference: 'Do not use a triple slash reference.',
noTripleSlashReference: 'Do not use a triple slash reference.',
},
},
defaultOptions: [],
Expand All @@ -30,7 +30,7 @@ export default util.createRule({
if (referenceRegExp.test(comment.value)) {
context.report({
node: comment,
messageId: 'tripleSlashReference',
messageId: 'noTripleSlashReference',
});
}
});
Expand Down
129 changes: 129 additions & 0 deletions packages/eslint-plugin/src/rules/triple-slash-reference.ts
@@ -0,0 +1,129 @@
import * as util from '../util';
import {
Literal,
Node,
TSExternalModuleReference,
} from '@typescript-eslint/typescript-estree/dist/ts-estree/ts-estree';
import { TSESTree } from '@typescript-eslint/typescript-estree';

type Options = [
{
lib?: 'always' | 'never';
path?: 'always' | 'never';
types?: 'always' | 'never' | 'prefer-import';
}
];
type MessageIds = 'tripleSlashReference';

export default util.createRule<Options, MessageIds>({
name: 'triple-slash-reference',
meta: {
type: 'suggestion',
docs: {
description:
'Sets preference level for triple slash directives versus ES6-style import declarations',
category: 'Best Practices',
recommended: false,
},
messages: {
tripleSlashReference:
'Do not use a triple slash reference for {{module}}, use `import` style instead.',
},
schema: [
{
type: 'object',
properties: {
lib: {
enum: ['always', 'never'],
},
path: {
enum: ['always', 'never'],
},
types: {
enum: ['always', 'never', 'prefer-import'],
},
},
additionalProperties: false,
},
],
},
defaultOptions: [
{
lib: 'always',
path: 'never',
types: 'prefer-import',
},
],
create(context, [{ lib, path, types }]) {
let programNode: Node;
const sourceCode = context.getSourceCode();
const references: ({
comment: TSESTree.Comment;
importName: string;
})[] = [];

function hasMatchingReference(source: Literal) {
references.forEach(reference => {
if (reference.importName === source.value) {
context.report({
node: reference.comment,
messageId: 'tripleSlashReference',
data: {
module: reference.importName,
},
});
}
});
}
return {
ImportDeclaration(node) {
if (programNode) {
const source = node.source as Literal;
hasMatchingReference(source);
}
},
TSImportEqualsDeclaration(node) {
if (programNode) {
const source = (node.moduleReference as TSExternalModuleReference)
.expression as Literal;
hasMatchingReference(source);
}
},
Program(node) {
if (lib === 'always' && path === 'always' && types == 'always') {
return;
}
programNode = node;
const referenceRegExp = /^\/\s*<reference\s*(types|path|lib)\s*=\s*["|'](.*)["|']/;
const commentsBefore = sourceCode.getCommentsBefore(programNode);

commentsBefore.forEach(comment => {
if (comment.type !== 'Line') {
return;
}
const referenceResult = referenceRegExp.exec(comment.value);

if (referenceResult) {
if (
(referenceResult[1] === 'types' && types === 'never') ||
(referenceResult[1] === 'path' && path === 'never') ||
(referenceResult[1] === 'lib' && lib === 'never')
) {
context.report({
node: comment,
messageId: 'tripleSlashReference',
data: {
module: referenceResult[2],
},
});
return;
}
if (referenceResult[1] === 'types' && types === 'prefer-import') {
references.push({ comment, importName: referenceResult[2] });
}
}
});
},
};
},
});

0 comments on commit ecc65e1

Please sign in to comment.