@definitelytyped/eslint-plugin 0.0.166-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/configs/all.d.ts +2 -0
- package/dist/configs/all.js +152 -0
- package/dist/configs/all.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/dt-header.d.ts +2 -0
- package/dist/rules/dt-header.js +62 -0
- package/dist/rules/dt-header.js.map +1 -0
- package/dist/rules/export-just-namespace.d.ts +2 -0
- package/dist/rules/export-just-namespace.js +70 -0
- package/dist/rules/export-just-namespace.js.map +1 -0
- package/dist/rules/index.d.ts +38 -0
- package/dist/rules/index.js +61 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/no-any-union.d.ts +4 -0
- package/dist/rules/no-any-union.js +34 -0
- package/dist/rules/no-any-union.js.map +1 -0
- package/dist/rules/no-bad-reference.d.ts +2 -0
- package/dist/rules/no-bad-reference.js +55 -0
- package/dist/rules/no-bad-reference.js.map +1 -0
- package/dist/rules/no-const-enum.d.ts +4 -0
- package/dist/rules/no-const-enum.js +30 -0
- package/dist/rules/no-const-enum.js.map +1 -0
- package/dist/rules/no-dead-reference.d.ts +2 -0
- package/dist/rules/no-dead-reference.js +44 -0
- package/dist/rules/no-dead-reference.js.map +1 -0
- package/dist/rules/no-declare-current-package.d.ts +4 -0
- package/dist/rules/no-declare-current-package.js +43 -0
- package/dist/rules/no-declare-current-package.js.map +1 -0
- package/dist/rules/no-import-default-of-export-equals.d.ts +4 -0
- package/dist/rules/no-import-default-of-export-equals.js +87 -0
- package/dist/rules/no-import-default-of-export-equals.js.map +1 -0
- package/dist/rules/no-outside-dependencies.d.ts +2 -0
- package/dist/rules/no-outside-dependencies.js +41 -0
- package/dist/rules/no-outside-dependencies.js.map +1 -0
- package/dist/rules/no-self-import.d.ts +4 -0
- package/dist/rules/no-self-import.js +38 -0
- package/dist/rules/no-self-import.js.map +1 -0
- package/dist/rules/no-single-element-tuple-type.d.ts +5 -0
- package/dist/rules/no-single-element-tuple-type.js +30 -0
- package/dist/rules/no-single-element-tuple-type.js.map +1 -0
- package/dist/rules/no-unnecessary-generics.d.ts +8 -0
- package/dist/rules/no-unnecessary-generics.js +135 -0
- package/dist/rules/no-unnecessary-generics.js.map +1 -0
- package/dist/rules/no-useless-files.d.ts +2 -0
- package/dist/rules/no-useless-files.js +53 -0
- package/dist/rules/no-useless-files.js.map +1 -0
- package/dist/rules/prefer-declare-function.d.ts +5 -0
- package/dist/rules/prefer-declare-function.js +35 -0
- package/dist/rules/prefer-declare-function.js.map +1 -0
- package/dist/rules/redundant-undefined.d.ts +4 -0
- package/dist/rules/redundant-undefined.js +53 -0
- package/dist/rules/redundant-undefined.js.map +1 -0
- package/dist/rules/trim-file.d.ts +2 -0
- package/dist/rules/trim-file.js +43 -0
- package/dist/rules/trim-file.js.map +1 -0
- package/dist/util.d.ts +4 -0
- package/dist/util.js +38 -0
- package/dist/util.js.map +1 -0
- package/docs/rules/dt-header.md +88 -0
- package/docs/rules/export-just-namespace.md +29 -0
- package/docs/rules/no-any-union.md +27 -0
- package/docs/rules/no-bad-reference.md +28 -0
- package/docs/rules/no-const-enum.md +16 -0
- package/docs/rules/no-dead-reference.md +17 -0
- package/docs/rules/no-declare-current-package.md +35 -0
- package/docs/rules/no-import-default-of-export-equals.md +22 -0
- package/docs/rules/no-outside-dependencies.md +23 -0
- package/docs/rules/no-self-import.md +27 -0
- package/docs/rules/no-single-element-tuple-type.md +15 -0
- package/docs/rules/no-unnecessary-generics.md +69 -0
- package/docs/rules/no-useless-files.md +14 -0
- package/docs/rules/prefer-declare-function.md +15 -0
- package/docs/rules/redundant-undefined.md +15 -0
- package/docs/rules/trim-file.md +17 -0
- package/package.json +45 -0
- package/src/configs/all.ts +151 -0
- package/src/index.ts +6 -0
- package/src/rules/dt-header.ts +74 -0
- package/src/rules/export-just-namespace.ts +83 -0
- package/src/rules/index.ts +35 -0
- package/src/rules/no-any-union.ts +34 -0
- package/src/rules/no-bad-reference.ts +62 -0
- package/src/rules/no-const-enum.ts +30 -0
- package/src/rules/no-dead-reference.ts +46 -0
- package/src/rules/no-declare-current-package.ts +45 -0
- package/src/rules/no-import-default-of-export-equals.ts +68 -0
- package/src/rules/no-outside-dependencies.ts +42 -0
- package/src/rules/no-self-import.ts +40 -0
- package/src/rules/no-single-element-tuple-type.ts +31 -0
- package/src/rules/no-unnecessary-generics.ts +126 -0
- package/src/rules/no-useless-files.ts +58 -0
- package/src/rules/prefer-declare-function.ts +37 -0
- package/src/rules/redundant-undefined.ts +62 -0
- package/src/rules/trim-file.ts +45 -0
- package/src/util.ts +41 -0
- package/test/dt-header.test.ts +189 -0
- package/test/export-just-namespace.test.ts +70 -0
- package/test/no-any-union.test.ts +22 -0
- package/test/no-bad-reference.test.ts +66 -0
- package/test/no-const-enum.test.ts +22 -0
- package/test/no-dead-reference.test.ts +66 -0
- package/test/no-declare-current-package.test.ts +61 -0
- package/test/no-import-default-of-export-equals.test.ts +49 -0
- package/test/no-self-import.test.ts +47 -0
- package/test/no-single-element-tuple-type.test.ts +28 -0
- package/test/no-unnecessary-generics.test.ts +152 -0
- package/test/no-useless-files.test.ts +42 -0
- package/test/prefer-declare-function.test.ts +66 -0
- package/test/redundant-undefined.test.ts +39 -0
- package/test/trim-file.test.ts +46 -0
- package/test/tsconfig.json +10 -0
- package/test/tsconfig.no-declare-current-package.json +11 -0
- package/test/tsconfig.no-declare-current-package2.json +11 -0
- package/test/tsconfig.no-import-default-of-export-equals.json +11 -0
- package/test/tsconfig.no-self-import.json +7 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
import { createRule, getCommonDirectoryName } from "../util";
|
|
3
|
+
|
|
4
|
+
const rule = createRule({
|
|
5
|
+
name: "no-self-import",
|
|
6
|
+
defaultOptions: [],
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Forbids declaration files to import the current package using a global import.",
|
|
11
|
+
recommended: "error",
|
|
12
|
+
},
|
|
13
|
+
messages: {
|
|
14
|
+
useRelativeImport: "Declaration file should not use a global import of itself. Use a relative import.",
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
},
|
|
18
|
+
create(context) {
|
|
19
|
+
if (!context.getFilename().endsWith(".d.ts")) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const services = ESLintUtils.getParserServices(context);
|
|
24
|
+
const packageName = getCommonDirectoryName(services.program.getRootFileNames());
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
28
|
+
ImportDeclaration(node) {
|
|
29
|
+
if (node.source.value === packageName || node.source.value.startsWith(packageName + "/")) {
|
|
30
|
+
context.report({
|
|
31
|
+
messageId: "useRelativeImport",
|
|
32
|
+
node,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export = rule;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createRule } from "../util";
|
|
2
|
+
import { TSESTree } from "@typescript-eslint/utils";
|
|
3
|
+
|
|
4
|
+
const rule = createRule({
|
|
5
|
+
name: "no-single-element-tuple-type",
|
|
6
|
+
defaultOptions: [],
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Forbids `[T]`, which should be `T[]`.",
|
|
11
|
+
recommended: "error",
|
|
12
|
+
},
|
|
13
|
+
messages: {
|
|
14
|
+
singleElementTupleType: `Type [T] is a single-element tuple type. You probably meant T[].`,
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
},
|
|
18
|
+
create(context) {
|
|
19
|
+
return {
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
21
|
+
"TSTupleType[elementTypes.length=1]"(node: TSESTree.TSTupleType) {
|
|
22
|
+
context.report({
|
|
23
|
+
messageId: "singleElementTupleType",
|
|
24
|
+
node,
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export = rule;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
import * as ts from "typescript";
|
|
3
|
+
|
|
4
|
+
import { createRule } from "../util";
|
|
5
|
+
|
|
6
|
+
type ESTreeFunctionLikeWithTypeParameters = TSESTree.FunctionLike & {
|
|
7
|
+
typeParameters: {};
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type TSSignatureDeclarationWithTypeParameters = ts.SignatureDeclaration & {
|
|
11
|
+
typeParameters: {};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const rule = createRule({
|
|
15
|
+
defaultOptions: [],
|
|
16
|
+
meta: {
|
|
17
|
+
docs: {
|
|
18
|
+
description: "Forbids signatures using a generic parameter only once.",
|
|
19
|
+
recommended: "error",
|
|
20
|
+
},
|
|
21
|
+
messages: {
|
|
22
|
+
never: "Type parameter {{name}} is never used.",
|
|
23
|
+
sole: "Type parameter {{name}} is used only once.",
|
|
24
|
+
},
|
|
25
|
+
schema: [],
|
|
26
|
+
type: "problem",
|
|
27
|
+
},
|
|
28
|
+
name: "no-relative-import-in-test",
|
|
29
|
+
create(context) {
|
|
30
|
+
return {
|
|
31
|
+
[[
|
|
32
|
+
"ArrowFunctionExpression[typeParameters]",
|
|
33
|
+
"FunctionDeclaration[typeParameters]",
|
|
34
|
+
"FunctionExpression[typeParameters]",
|
|
35
|
+
"TSCallSignatureDeclaration[typeParameters]",
|
|
36
|
+
"TSConstructorType[typeParameters]",
|
|
37
|
+
"TSDeclareFunction[typeParameters]",
|
|
38
|
+
"TSFunctionType[typeParameters]",
|
|
39
|
+
"TSMethodSignature[typeParameters]",
|
|
40
|
+
].join(", ")](esNode: ESTreeFunctionLikeWithTypeParameters) {
|
|
41
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
42
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(esNode) as TSSignatureDeclarationWithTypeParameters;
|
|
43
|
+
if (!tsNode.typeParameters) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const checker = parserServices.program.getTypeChecker();
|
|
48
|
+
|
|
49
|
+
for (const typeParameter of tsNode.typeParameters) {
|
|
50
|
+
const name = typeParameter.name.text;
|
|
51
|
+
const res = getSoleUse(tsNode, assertDefined(checker.getSymbolAtLocation(typeParameter.name)), checker);
|
|
52
|
+
switch (res.type) {
|
|
53
|
+
case "sole":
|
|
54
|
+
context.report({
|
|
55
|
+
data: { name },
|
|
56
|
+
messageId: "sole",
|
|
57
|
+
node: parserServices.tsNodeToESTreeNodeMap.get(res.soleUse),
|
|
58
|
+
});
|
|
59
|
+
break;
|
|
60
|
+
case "never":
|
|
61
|
+
context.report({
|
|
62
|
+
data: { name },
|
|
63
|
+
messageId: "never",
|
|
64
|
+
node: parserServices.tsNodeToESTreeNodeMap.get(typeParameter),
|
|
65
|
+
});
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
type Result = { type: "ok" | "never" } | { type: "sole"; soleUse: ts.Identifier };
|
|
75
|
+
function getSoleUse(sig: ts.SignatureDeclaration, typeParameterSymbol: ts.Symbol, checker: ts.TypeChecker): Result {
|
|
76
|
+
const exit = {};
|
|
77
|
+
let soleUse: ts.Identifier | undefined;
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
if (sig.typeParameters) {
|
|
81
|
+
for (const tp of sig.typeParameters) {
|
|
82
|
+
if (tp.constraint) {
|
|
83
|
+
recur(tp.constraint);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
for (const param of sig.parameters) {
|
|
88
|
+
if (param.type) {
|
|
89
|
+
recur(param.type);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (sig.type) {
|
|
93
|
+
recur(sig.type);
|
|
94
|
+
}
|
|
95
|
+
} catch (err) {
|
|
96
|
+
if (err === exit) {
|
|
97
|
+
return { type: "ok" };
|
|
98
|
+
}
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return soleUse ? { type: "sole", soleUse } : { type: "never" };
|
|
103
|
+
|
|
104
|
+
function recur(node: ts.Node): void {
|
|
105
|
+
if (ts.isIdentifier(node)) {
|
|
106
|
+
if (checker.getSymbolAtLocation(node) === typeParameterSymbol) {
|
|
107
|
+
if (soleUse === undefined) {
|
|
108
|
+
soleUse = node;
|
|
109
|
+
} else {
|
|
110
|
+
throw exit;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
node.forEachChild(recur);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export = rule;
|
|
120
|
+
|
|
121
|
+
function assertDefined<T>(value: T | undefined): T {
|
|
122
|
+
if (value === undefined) {
|
|
123
|
+
throw new Error("unreachable");
|
|
124
|
+
}
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createRule } from "../util";
|
|
2
|
+
|
|
3
|
+
const rule = createRule({
|
|
4
|
+
name: "no-useless-files",
|
|
5
|
+
defaultOptions: [],
|
|
6
|
+
meta: {
|
|
7
|
+
type: "problem",
|
|
8
|
+
docs: {
|
|
9
|
+
description: "Forbids files with no content.",
|
|
10
|
+
recommended: "error",
|
|
11
|
+
},
|
|
12
|
+
messages: {
|
|
13
|
+
noContent: "File has no content.",
|
|
14
|
+
},
|
|
15
|
+
schema: [],
|
|
16
|
+
},
|
|
17
|
+
create(context) {
|
|
18
|
+
const {
|
|
19
|
+
ast: { tokens, comments },
|
|
20
|
+
} = context.getSourceCode();
|
|
21
|
+
|
|
22
|
+
if (tokens.length === 0) {
|
|
23
|
+
if (comments.length === 0) {
|
|
24
|
+
reportNoContent();
|
|
25
|
+
} else {
|
|
26
|
+
const referenceRegExp = /^\/\s*<reference\s*(types|path)\s*=\s*["|'](.*)["|']/;
|
|
27
|
+
let noReferenceFound = true;
|
|
28
|
+
|
|
29
|
+
for (const comment of comments) {
|
|
30
|
+
const referenceMatch = comment.value.match(referenceRegExp)?.[1];
|
|
31
|
+
if (!referenceMatch) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
noReferenceFound = false;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (noReferenceFound) {
|
|
39
|
+
reportNoContent();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {};
|
|
45
|
+
|
|
46
|
+
function reportNoContent() {
|
|
47
|
+
context.report({
|
|
48
|
+
messageId: "noContent",
|
|
49
|
+
loc: {
|
|
50
|
+
start: { line: 1, column: 0 },
|
|
51
|
+
end: { line: 1, column: 0 },
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export = rule;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import { createRule } from "../util";
|
|
4
|
+
|
|
5
|
+
const rule = createRule({
|
|
6
|
+
defaultOptions: [],
|
|
7
|
+
meta: {
|
|
8
|
+
docs: {
|
|
9
|
+
description: "Forbids `const x: () => void`.",
|
|
10
|
+
recommended: "error",
|
|
11
|
+
},
|
|
12
|
+
messages: {
|
|
13
|
+
variableFunction: "Use a function declaration instead of a variable of function type.",
|
|
14
|
+
},
|
|
15
|
+
schema: [],
|
|
16
|
+
type: "problem",
|
|
17
|
+
},
|
|
18
|
+
name: "prefer-declare-function",
|
|
19
|
+
create(context) {
|
|
20
|
+
return {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
22
|
+
"VariableDeclaration > VariableDeclarator"(node: TSESTree.VariableDeclarator) {
|
|
23
|
+
if (
|
|
24
|
+
node.id.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES.TSFunctionType &&
|
|
25
|
+
context.getFilename().endsWith(".d.ts")
|
|
26
|
+
) {
|
|
27
|
+
context.report({
|
|
28
|
+
messageId: "variableFunction",
|
|
29
|
+
node: node.id,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export = rule;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createRule } from "../util";
|
|
2
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
3
|
+
import { TSESTree } from "@typescript-eslint/types";
|
|
4
|
+
|
|
5
|
+
const rule = createRule({
|
|
6
|
+
name: "redundant-undefined",
|
|
7
|
+
defaultOptions: [],
|
|
8
|
+
meta: {
|
|
9
|
+
type: "problem",
|
|
10
|
+
docs: {
|
|
11
|
+
description:
|
|
12
|
+
"Forbids optional parameters from including an explicit `undefined` in their type; requires it in optional properties.",
|
|
13
|
+
recommended: "error",
|
|
14
|
+
},
|
|
15
|
+
messages: {
|
|
16
|
+
redundantUndefined: `Parameter is optional, so no need to include \`undefined\` in the type.`,
|
|
17
|
+
},
|
|
18
|
+
schema: [],
|
|
19
|
+
},
|
|
20
|
+
create(context) {
|
|
21
|
+
return {
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
23
|
+
TSUnionType(node) {
|
|
24
|
+
const hasUndefinedType = node.types.some((t) => t.type === AST_NODE_TYPES.TSUndefinedKeyword);
|
|
25
|
+
if (
|
|
26
|
+
node.parent!.type === AST_NODE_TYPES.TSTypeAnnotation &&
|
|
27
|
+
isParameter(node.parent!.parent!) &&
|
|
28
|
+
node.parent.parent.optional &&
|
|
29
|
+
isFunctionLike(node.parent!.parent!.parent!) &&
|
|
30
|
+
hasUndefinedType
|
|
31
|
+
) {
|
|
32
|
+
context.report({
|
|
33
|
+
messageId: "redundantUndefined",
|
|
34
|
+
node,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
function isFunctionLike(node: TSESTree.Node): node is TSESTree.FunctionLike {
|
|
43
|
+
return (
|
|
44
|
+
node.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
45
|
+
node.type === AST_NODE_TYPES.FunctionDeclaration ||
|
|
46
|
+
node.type === AST_NODE_TYPES.FunctionExpression ||
|
|
47
|
+
node.type === AST_NODE_TYPES.TSDeclareFunction ||
|
|
48
|
+
node.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
/** Note: Does not include parameter properties because those can't be optional */
|
|
52
|
+
function isParameter(node: TSESTree.Node): node is Exclude<TSESTree.Parameter, TSESTree.TSParameterProperty> {
|
|
53
|
+
return (
|
|
54
|
+
node.type === AST_NODE_TYPES.ArrayPattern ||
|
|
55
|
+
node.type === AST_NODE_TYPES.AssignmentPattern ||
|
|
56
|
+
node.type === AST_NODE_TYPES.Identifier ||
|
|
57
|
+
node.type === AST_NODE_TYPES.ObjectPattern ||
|
|
58
|
+
node.type === AST_NODE_TYPES.RestElement
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export = rule;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createRule } from "../util";
|
|
2
|
+
|
|
3
|
+
const rule = createRule({
|
|
4
|
+
name: "trim-file",
|
|
5
|
+
defaultOptions: [],
|
|
6
|
+
meta: {
|
|
7
|
+
type: "layout",
|
|
8
|
+
docs: {
|
|
9
|
+
description: "Forbids leading/trailing blank lines in a file. Allows file to end in '\n'",
|
|
10
|
+
recommended: "error",
|
|
11
|
+
},
|
|
12
|
+
messages: {
|
|
13
|
+
leadingBlankLine: "File should not begin with a blank line.",
|
|
14
|
+
trailingBlankLine:
|
|
15
|
+
"File should not end with a blank line. (Ending in one newline OK, ending in two newlines not OK.)",
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
const { lines, text } = context.getSourceCode();
|
|
21
|
+
if (text.startsWith("\r") || text.startsWith("\n")) {
|
|
22
|
+
context.report({
|
|
23
|
+
messageId: "leadingBlankLine",
|
|
24
|
+
loc: {
|
|
25
|
+
start: { line: 1, column: 0 },
|
|
26
|
+
end: { line: 1, column: 0 },
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (text.endsWith("\n\n") || text.endsWith("\r\n\r\n")) {
|
|
31
|
+
const line = lines.length;
|
|
32
|
+
context.report({
|
|
33
|
+
messageId: "trailingBlankLine",
|
|
34
|
+
loc: {
|
|
35
|
+
start: { line, column: 0 },
|
|
36
|
+
end: { line, column: 0 },
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {};
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export = rule;
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
import { basename, dirname } from "path";
|
|
3
|
+
|
|
4
|
+
export const createRule = ESLintUtils.RuleCreator(
|
|
5
|
+
(name) =>
|
|
6
|
+
`https://github.com/microsoft/DefinitelyTyped-tools/tree/master/packages/eslint-plugin/docs/rules/${name}.md`
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export function getCommonDirectoryName(files: readonly string[]): string {
|
|
10
|
+
let minLen = 999;
|
|
11
|
+
let minDir = "";
|
|
12
|
+
for (const file of files) {
|
|
13
|
+
const dir = dirname(file);
|
|
14
|
+
if (dir.length < minLen) {
|
|
15
|
+
minDir = dir;
|
|
16
|
+
minLen = dir.length;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return basename(minDir);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function isMainFile(fileName: string, allowNested: boolean) {
|
|
23
|
+
// Linter may be run with cwd of the package. We want `index.d.ts` but not `submodule/index.d.ts` to match.
|
|
24
|
+
if (fileName === "index.d.ts") {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (basename(fileName) !== "index.d.ts") {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let parent = dirname(fileName);
|
|
33
|
+
// May be a directory for an older version, e.g. `v0`.
|
|
34
|
+
// Note a types redirect `foo/ts3.1` should not have its own header.
|
|
35
|
+
if (allowNested && /^v(0\.)?\d+$/.test(basename(parent))) {
|
|
36
|
+
parent = dirname(parent);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Allow "types/foo/index.d.ts", not "types/foo/utils/index.d.ts"
|
|
40
|
+
return basename(dirname(parent)) === "types";
|
|
41
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import * as dtHeader from "../src/rules/dt-header";
|
|
4
|
+
|
|
5
|
+
const ruleTester = new ESLintUtils.RuleTester({
|
|
6
|
+
parser: "@typescript-eslint/parser",
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
ruleTester.run("@definitelytyped/dt-header", dtHeader, {
|
|
10
|
+
invalid: [
|
|
11
|
+
{
|
|
12
|
+
code: ``,
|
|
13
|
+
errors: [
|
|
14
|
+
{
|
|
15
|
+
column: 2,
|
|
16
|
+
data: {
|
|
17
|
+
expected: "/\\/\\/ Type definitions for (non-npm package )?/",
|
|
18
|
+
},
|
|
19
|
+
line: 1,
|
|
20
|
+
messageId: "parseError",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
filename: "types/blank/index.d.ts",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
code: `
|
|
27
|
+
// ...
|
|
28
|
+
`,
|
|
29
|
+
errors: [
|
|
30
|
+
{
|
|
31
|
+
column: 1,
|
|
32
|
+
data: {
|
|
33
|
+
expected: "/\\/\\/ Type definitions for (non-npm package )?/",
|
|
34
|
+
},
|
|
35
|
+
line: 2,
|
|
36
|
+
messageId: "parseError",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
filename: "types/only-comment/index.d.ts",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
code: `
|
|
43
|
+
// Type definitions for dt-header 0.75
|
|
44
|
+
// Project: https://github.com/bobby-headers/dt-header
|
|
45
|
+
// Definitions by: Jane Doe <https://github.com/janedoe>
|
|
46
|
+
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
47
|
+
// Minimum TypeScript Version: 3.1
|
|
48
|
+
|
|
49
|
+
`,
|
|
50
|
+
errors: [
|
|
51
|
+
{
|
|
52
|
+
column: 1,
|
|
53
|
+
data: {
|
|
54
|
+
expected: "/\\/\\/ Type definitions for (non-npm package )?/",
|
|
55
|
+
},
|
|
56
|
+
line: 2,
|
|
57
|
+
messageId: "parseError",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
filename: "types/start-with-whitespace/index.d.ts",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
code: `// Type definitions for dt-header 1.0
|
|
64
|
+
// Project: https://github.com/bobby-headers/dt-header
|
|
65
|
+
// Definitions by: Jane Doe <https://github.ORG/janedoe>
|
|
66
|
+
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
67
|
+
`,
|
|
68
|
+
errors: [
|
|
69
|
+
{
|
|
70
|
+
column: 30,
|
|
71
|
+
data: {
|
|
72
|
+
expected: "/\\<https\\:\\/\\/github\\.com\\/([a-zA-Z\\d\\-]+)\\/?\\>/",
|
|
73
|
+
},
|
|
74
|
+
line: 3,
|
|
75
|
+
messageId: "parseError",
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
filename: "types/bad-url-github-org/index.d.ts",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
code: `// Type definitions for dt-header 1.0
|
|
82
|
+
// Project: https://github.com/bobby-headers/dt-header
|
|
83
|
+
// Definitions by: Jane Doe <https://github.com/jane doe>
|
|
84
|
+
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
85
|
+
`,
|
|
86
|
+
errors: [
|
|
87
|
+
{
|
|
88
|
+
column: 30,
|
|
89
|
+
data: {
|
|
90
|
+
expected: "/\\<https\\:\\/\\/github\\.com\\/([a-zA-Z\\d\\-]+)\\/?\\>/",
|
|
91
|
+
},
|
|
92
|
+
line: 3,
|
|
93
|
+
messageId: "parseError",
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
filename: "types/bad-url-space/index.d.ts",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
code: `// Type definitions for dt-header 1.0
|
|
100
|
+
// Project: https://github.com/not/important
|
|
101
|
+
// Definitions by: My Self <https://github.com/me>
|
|
102
|
+
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
103
|
+
|
|
104
|
+
`,
|
|
105
|
+
errors: [
|
|
106
|
+
{
|
|
107
|
+
column: 1,
|
|
108
|
+
endColumn: 27,
|
|
109
|
+
line: 3,
|
|
110
|
+
messageId: "definitionsBy",
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
filename: "types/bad-username/index.d.ts",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
code: `// Type definitions for dt-header v1.0.3
|
|
117
|
+
// Project: https://github.com/bobby-headers/dt-header
|
|
118
|
+
// Definitions by: Jane Doe <https://github.com/janedoe>
|
|
119
|
+
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
120
|
+
`,
|
|
121
|
+
errors: [
|
|
122
|
+
{
|
|
123
|
+
column: 26,
|
|
124
|
+
data: {
|
|
125
|
+
expected: "foo MAJOR.MINOR (patch version not allowed)",
|
|
126
|
+
},
|
|
127
|
+
line: 1,
|
|
128
|
+
messageId: "parseError",
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
filename: "types/foo/index.d.ts",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
code: `// Type definitions for
|
|
135
|
+
`,
|
|
136
|
+
errors: [
|
|
137
|
+
{
|
|
138
|
+
column: 1,
|
|
139
|
+
endColumn: 24,
|
|
140
|
+
line: 1,
|
|
141
|
+
messageId: "typeDefinitionsFor",
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
filename: "types/foo/notIndex.d.ts",
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
valid: [
|
|
148
|
+
``,
|
|
149
|
+
{
|
|
150
|
+
code: `// This isn't the main index
|
|
151
|
+
`,
|
|
152
|
+
filename: "types/foo/bar/index.d.ts",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
code: `// Type definitions for dt-header 0.75
|
|
156
|
+
// Project: https://github.com/bobby-headers/dt-header
|
|
157
|
+
// Definitions by: Jane Doe <https://github.com/janedoe>
|
|
158
|
+
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
159
|
+
// Minimum TypeScript Version: 3.1
|
|
160
|
+
|
|
161
|
+
`,
|
|
162
|
+
filename: "types/foo/v0.75/index.d.ts",
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
code: `// Type definitions for dt-header 1.0
|
|
166
|
+
// Project: https://github.com/bobby-headers/dt-header
|
|
167
|
+
// Definitions by: Jane Doe <https://github.com/janedoe>
|
|
168
|
+
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
169
|
+
// Minimum TypeScript Version: 3.1
|
|
170
|
+
|
|
171
|
+
`,
|
|
172
|
+
filename: "types/foo/v1/index.d.ts",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
code: `// Type definitions for dt-header 2.0
|
|
176
|
+
// Project: https://github.com/bobby-headers/dt-header
|
|
177
|
+
// Definitions by: Jane Doe <https://github.com/janedoe>
|
|
178
|
+
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
179
|
+
// Minimum TypeScript Version: 3.1
|
|
180
|
+
|
|
181
|
+
`,
|
|
182
|
+
filename: "types/foo/index.d.ts",
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
code: ``,
|
|
186
|
+
filename: "types/foo/notIndex.d.ts",
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import * as exportJustNamespace from "../src/rules/export-just-namespace";
|
|
4
|
+
|
|
5
|
+
const ruleTester = new ESLintUtils.RuleTester({
|
|
6
|
+
parser: "@typescript-eslint/parser",
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
ruleTester.run("@definitelytyped/export-just-namespace", exportJustNamespace, {
|
|
10
|
+
invalid: [
|
|
11
|
+
{
|
|
12
|
+
code: `
|
|
13
|
+
export = Stuff;
|
|
14
|
+
namespace Stuff {}
|
|
15
|
+
`,
|
|
16
|
+
errors: [
|
|
17
|
+
{
|
|
18
|
+
line: 2,
|
|
19
|
+
messageId: "useTheBody",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
code: `
|
|
25
|
+
namespace Stuff {}
|
|
26
|
+
export = Stuff;
|
|
27
|
+
`,
|
|
28
|
+
errors: [
|
|
29
|
+
{
|
|
30
|
+
line: 3,
|
|
31
|
+
messageId: "useTheBody",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
code: `
|
|
37
|
+
namespace Stuff {}
|
|
38
|
+
const other = "code";
|
|
39
|
+
export = Stuff;
|
|
40
|
+
`,
|
|
41
|
+
errors: [
|
|
42
|
+
{
|
|
43
|
+
line: 4,
|
|
44
|
+
messageId: "useTheBody",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
valid: [
|
|
50
|
+
`export const value = 3;`,
|
|
51
|
+
`export default class Hello {}`,
|
|
52
|
+
'import * as fs from "fs";',
|
|
53
|
+
"const value = 123;",
|
|
54
|
+
`export = stuff;`,
|
|
55
|
+
`export = createStuff();`,
|
|
56
|
+
`
|
|
57
|
+
class Stuff {}
|
|
58
|
+
namespace Stuff {}
|
|
59
|
+
export = Stuff;
|
|
60
|
+
`,
|
|
61
|
+
`export = First
|
|
62
|
+
namespace First {}
|
|
63
|
+
declare function First()
|
|
64
|
+
`,
|
|
65
|
+
`declare namespace Second {}
|
|
66
|
+
export = Second
|
|
67
|
+
declare function Second<U, S>(s: U): S
|
|
68
|
+
`,
|
|
69
|
+
],
|
|
70
|
+
});
|