@definitelytyped/eslint-plugin 0.0.177 → 0.0.179

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/rules/dt-header.d.ts +1 -1
  3. package/dist/rules/export-just-namespace.d.ts +1 -1
  4. package/dist/rules/index.d.ts +17 -38
  5. package/dist/rules/index.js +2 -0
  6. package/dist/rules/index.js.map +1 -1
  7. package/dist/rules/no-any-union.d.ts +1 -3
  8. package/dist/rules/no-bad-reference.d.ts +1 -1
  9. package/dist/rules/no-const-enum.d.ts +1 -3
  10. package/dist/rules/no-dead-reference.d.ts +1 -1
  11. package/dist/rules/no-declare-current-package.d.ts +1 -3
  12. package/dist/rules/no-import-default-of-export-equals.d.ts +1 -3
  13. package/dist/rules/no-outside-dependencies.d.ts +1 -1
  14. package/dist/rules/no-relative-import-in-test.d.ts +1 -3
  15. package/dist/rules/no-self-import.d.ts +1 -3
  16. package/dist/rules/no-single-element-tuple-type.d.ts +1 -4
  17. package/dist/rules/no-unnecessary-generics.d.ts +1 -7
  18. package/dist/rules/no-useless-files.d.ts +1 -1
  19. package/dist/rules/prefer-declare-function.d.ts +1 -4
  20. package/dist/rules/redundant-undefined.d.ts +1 -3
  21. package/dist/rules/strict-export-declare-modifiers.d.ts +2 -0
  22. package/dist/rules/strict-export-declare-modifiers.js +191 -0
  23. package/dist/rules/strict-export-declare-modifiers.js.map +1 -0
  24. package/dist/util.d.ts +3 -2
  25. package/dist/util.js +2 -0
  26. package/dist/util.js.map +1 -1
  27. package/package.json +10 -11
  28. package/src/rules/index.ts +2 -0
  29. package/src/rules/strict-export-declare-modifiers.ts +193 -0
  30. package/src/util.ts +7 -1
  31. package/test/strict-export-declare-modifiers.test.ts +158 -0
  32. package/tsconfig.json +2 -0
  33. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,193 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+ import * as ts from "typescript";
3
+ import { createRule } from "../util";
4
+
5
+ const rule = createRule({
6
+ name: "strict-export-declare-modifiers",
7
+ defaultOptions: [],
8
+ meta: {
9
+ type: "problem",
10
+ docs: {
11
+ description: "Enforces strict rules about where the 'export' and 'declare' modifiers may appear.",
12
+ recommended: "error",
13
+ },
14
+ messages: {
15
+ missingExplicitExport:
16
+ "All declarations in this module are exported automatically. " +
17
+ "Prefer to explicitly write 'export' for clarity. " +
18
+ "If you have a good reason not to export this declaration, " +
19
+ "add 'export {}' to the module to shut off automatic exporting.",
20
+ redundantDeclare: "'declare' keyword is redundant here.",
21
+ redundantExport:
22
+ "'export' keyword is redundant here because " +
23
+ "all declarations in this module are exported automatically. " +
24
+ "If you have a good reason to export some declarations and not others, " +
25
+ "add 'export {}' to the module to shut off automatic exporting.",
26
+ },
27
+ schema: [],
28
+ },
29
+ // TODO: This code is a modified version of the old TSLint rule,
30
+ // and still primarily uses TypeScript nodes. It would be better
31
+ // to switch it to using TSESTree nodes like other ESLint rules.
32
+ create(context) {
33
+ const services = ESLintUtils.getParserServices(context, true);
34
+ const sourceCode = context.getSourceCode();
35
+ const sourceFile = services.esTreeNodeToTSNodeMap.get(sourceCode.ast);
36
+
37
+ const isExternal =
38
+ sourceFile.isDeclarationFile &&
39
+ !sourceFile.statements.some(
40
+ (s) =>
41
+ s.kind === ts.SyntaxKind.ExportAssignment ||
42
+ (s.kind === ts.SyntaxKind.ExportDeclaration && !!(s as ts.ExportDeclaration).exportClause)
43
+ ) &&
44
+ ts.isExternalModule(sourceFile);
45
+
46
+ for (const node of sourceFile.statements) {
47
+ if (isExternal) {
48
+ checkInExternalModule(node, isAutomaticExport(sourceFile));
49
+ } else {
50
+ checkInOther(node, sourceFile.isDeclarationFile);
51
+ }
52
+
53
+ if (isModuleDeclaration(node) && (sourceFile.isDeclarationFile || isDeclare(node))) {
54
+ checkModule(node);
55
+ }
56
+ }
57
+
58
+ function checkInExternalModule(node: ts.Statement, autoExportEnabled: boolean) {
59
+ // Ignore certain node kinds (these can't have 'export' or 'default' modifiers)
60
+ switch (node.kind) {
61
+ case ts.SyntaxKind.ImportDeclaration:
62
+ case ts.SyntaxKind.ImportEqualsDeclaration:
63
+ case ts.SyntaxKind.ExportDeclaration:
64
+ case ts.SyntaxKind.NamespaceExportDeclaration:
65
+ return;
66
+ }
67
+
68
+ // `declare global` and `declare module "foo"` OK. `declare namespace N` not OK, should be `export namespace`.
69
+ if (!isDeclareGlobalOrExternalModuleDeclaration(node)) {
70
+ if (isDeclare(node)) {
71
+ context.report({
72
+ loc: getModifierLoc(node as ts.HasModifiers, ts.SyntaxKind.DeclareKeyword)!,
73
+ messageId: "redundantDeclare",
74
+ });
75
+ }
76
+ if (autoExportEnabled && !isExport(node)) {
77
+ context.report({
78
+ messageId: "missingExplicitExport",
79
+ node: services.tsNodeToESTreeNodeMap.get((node as ts.DeclarationStatement).name || node),
80
+ });
81
+ }
82
+ }
83
+ }
84
+
85
+ function checkInOther(node: ts.Statement, inDeclarationFile: boolean): void {
86
+ // Compiler will enforce presence of 'declare' where necessary. But types do not need 'declare'.
87
+ if (isDeclare(node)) {
88
+ if (
89
+ (isExport(node) && inDeclarationFile) ||
90
+ ts.isInterfaceDeclaration(node) ||
91
+ ts.isTypeAliasDeclaration(node)
92
+ ) {
93
+ context.report({
94
+ loc: getModifierLoc(node as ts.HasModifiers, ts.SyntaxKind.DeclareKeyword),
95
+ messageId: "redundantDeclare",
96
+ });
97
+ }
98
+ }
99
+ }
100
+
101
+ function getModifierLoc(node: ts.HasModifiers, kind: ts.SyntaxKind) {
102
+ const modifier = ts.getModifiers(node)?.find((modifier) => modifier.kind === kind)!;
103
+
104
+ return {
105
+ end: sourceCode.getLocFromIndex(modifier.end),
106
+ start: sourceCode.getLocFromIndex(modifier.getStart(sourceFile)),
107
+ };
108
+ }
109
+
110
+ function checkModule(moduleDeclaration: ts.ModuleDeclaration): void {
111
+ const body = moduleDeclaration.body;
112
+ if (!body) {
113
+ return;
114
+ }
115
+
116
+ switch (body.kind) {
117
+ case ts.SyntaxKind.ModuleDeclaration:
118
+ checkModule(body);
119
+ break;
120
+ case ts.SyntaxKind.ModuleBlock:
121
+ checkBlock(body, isAutomaticExport(moduleDeclaration));
122
+ break;
123
+ }
124
+ }
125
+
126
+ function checkBlock(block: ts.ModuleBlock, autoExportEnabled: boolean): void {
127
+ for (const statement of block.statements) {
128
+ // Compiler will error for 'declare' here anyway, so just check for 'export'.
129
+ if (isExport(statement) && autoExportEnabled && !isDefault(statement)) {
130
+ context.report({
131
+ loc: getModifierLoc(statement as ts.HasModifiers, ts.SyntaxKind.ExportKeyword),
132
+ messageId: "redundantExport",
133
+ });
134
+ }
135
+
136
+ if (isModuleDeclaration(statement)) {
137
+ checkModule(statement);
138
+ }
139
+ }
140
+ }
141
+
142
+ return {};
143
+ },
144
+ });
145
+
146
+ function isDeclareGlobalOrExternalModuleDeclaration(node: ts.Node): boolean {
147
+ return (
148
+ isModuleDeclaration(node) &&
149
+ (node.name.kind === ts.SyntaxKind.StringLiteral ||
150
+ (node.name.kind === ts.SyntaxKind.Identifier && node.name.text === "global"))
151
+ );
152
+ }
153
+
154
+ function isModuleDeclaration(node: ts.Node): node is ts.ModuleDeclaration {
155
+ return node.kind === ts.SyntaxKind.ModuleDeclaration;
156
+ }
157
+
158
+ function isDeclare(node: ts.Node): boolean {
159
+ return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.DeclareKeyword);
160
+ }
161
+
162
+ function isExport(node: ts.Node): boolean {
163
+ return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
164
+ }
165
+
166
+ function isDefault(node: ts.Node): boolean {
167
+ return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword);
168
+ }
169
+
170
+ // tslint:disable-next-line:max-line-length
171
+ // Copied from https://github.com/Microsoft/TypeScript/blob/dd9b8cab34a3e389e924d768eb656cf50656f582/src/compiler/binder.ts#L1571-L1581
172
+ function hasExportDeclarations(node: ts.SourceFile | ts.ModuleDeclaration): boolean {
173
+ const body = node.kind === ts.SyntaxKind.SourceFile ? node : node.body;
174
+ if (body && (body.kind === ts.SyntaxKind.SourceFile || body.kind === ts.SyntaxKind.ModuleBlock)) {
175
+ for (const stat of (body as ts.BlockLike).statements) {
176
+ if (stat.kind === ts.SyntaxKind.ExportDeclaration || stat.kind === ts.SyntaxKind.ExportAssignment) {
177
+ return true;
178
+ }
179
+ }
180
+ }
181
+ return false;
182
+ }
183
+
184
+ function isAutomaticExport(node: ts.SourceFile | ts.ModuleDeclaration): boolean {
185
+ // We'd like to just test ts.NodeFlags.ExportContext, but we don't run the
186
+ // binder, so that flag won't be set, so duplicate the logic instead. :(
187
+ //
188
+ // ts.NodeFlags.Ambient is @internal, but all modules that get here should
189
+ // be ambient.
190
+ return !hasExportDeclarations(node);
191
+ }
192
+
193
+ export = rule;
package/src/util.ts CHANGED
@@ -1,8 +1,14 @@
1
1
  import { unmangleScopedPackage } from "@definitelytyped/utils";
2
2
  import { ESLintUtils } from "@typescript-eslint/utils";
3
+ import { RuleWithMetaAndName } from "@typescript-eslint/utils/dist/eslint-utils";
4
+ import { RuleListener, RuleModule } from "@typescript-eslint/utils/dist/ts-eslint";
3
5
  import { basename, dirname } from "path";
4
6
 
5
- export const createRule = ESLintUtils.RuleCreator(
7
+ // Possible TS bug can't figure out how to do declaration emit of created rules
8
+ // without an explicit type annotation here due to pnpm symlink stuff
9
+ export const createRule: <TOptions extends readonly unknown[], TMessageIds extends string>(
10
+ opts: Readonly<RuleWithMetaAndName<TOptions, TMessageIds, RuleListener>>
11
+ ) => RuleModule<TMessageIds, TOptions> = ESLintUtils.RuleCreator(
6
12
  (name) =>
7
13
  `https://github.com/microsoft/DefinitelyTyped-tools/tree/master/packages/eslint-plugin/docs/rules/${name}.md`
8
14
  );
@@ -0,0 +1,158 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+
3
+ import * as strictExportDeclareModifiers from "../src/rules/strict-export-declare-modifiers";
4
+
5
+ const ruleTester = new ESLintUtils.RuleTester({
6
+ parser: "@typescript-eslint/parser",
7
+ });
8
+
9
+ ruleTester.run("strict-export-declare-modifiers", strictExportDeclareModifiers, {
10
+ invalid: [
11
+ {
12
+ code: `declare interface I {}`,
13
+ errors: [
14
+ {
15
+ column: 1,
16
+ endColumn: 8,
17
+ line: 1,
18
+ messageId: "redundantDeclare",
19
+ },
20
+ ],
21
+ },
22
+ {
23
+ code: `
24
+ declare namespace M {
25
+ export const x: number;
26
+ }
27
+ `,
28
+ errors: [
29
+ {
30
+ column: 5,
31
+ endColumn: 11,
32
+ line: 3,
33
+ messageId: "redundantExport",
34
+ },
35
+ ],
36
+ },
37
+ {
38
+ code: `export declare function f(): void;`,
39
+ errors: [
40
+ {
41
+ line: 1,
42
+ messageId: "redundantDeclare",
43
+ },
44
+ ],
45
+ filename: "testModuleAutoExport.d.ts",
46
+ },
47
+ {
48
+ code: `
49
+ export namespace M {
50
+ export function f(): void;
51
+ }
52
+ `,
53
+ errors: [
54
+ {
55
+ column: 3,
56
+ endColumn: 9,
57
+ line: 3,
58
+ messageId: "redundantExport",
59
+ },
60
+ ],
61
+ filename: "testModuleAutoExport.d.ts",
62
+ },
63
+ {
64
+ code: `
65
+ interface I {}
66
+ export namespace M {}
67
+ `,
68
+ errors: [
69
+ {
70
+ column: 11,
71
+ endColumn: 12,
72
+ line: 2,
73
+ messageId: "missingExplicitExport",
74
+ },
75
+ ],
76
+ filename: "testModuleAutoExport.d.ts",
77
+ },
78
+ {
79
+ code: `
80
+ declare function g(): void;
81
+ export namespace M {}
82
+ `,
83
+ errors: [
84
+ {
85
+ column: 1,
86
+ endColumn: 8,
87
+ line: 2,
88
+ messageId: "redundantDeclare",
89
+ },
90
+ {
91
+ column: 18,
92
+ endColumn: 19,
93
+ line: 2,
94
+ messageId: "missingExplicitExport",
95
+ },
96
+ ],
97
+ filename: "testModuleAutoExport.d.ts",
98
+ },
99
+ {
100
+ code: `
101
+ declare namespace N {}
102
+ export namespace M {}
103
+ `,
104
+ errors: [
105
+ {
106
+ column: 1,
107
+ endColumn: 8,
108
+ line: 2,
109
+ messageId: "redundantDeclare",
110
+ },
111
+ {
112
+ column: 19,
113
+ endColumn: 20,
114
+ line: 2,
115
+ messageId: "missingExplicitExport",
116
+ },
117
+ ],
118
+ filename: "testModuleAutoExport.d.ts",
119
+ },
120
+ ],
121
+ valid: [
122
+ `export declare class C {}`,
123
+ `export function f() {}`,
124
+ `declare function g(): void;`,
125
+ `declare namespace N {};`,
126
+ `interface J {}`,
127
+ `
128
+ namespace N {
129
+ export const x: number;
130
+ }
131
+ `,
132
+ {
133
+ code: `
134
+ declare class Foo {}
135
+ export { Foo as Bar }
136
+ `,
137
+ filename: "./testValuesDeclareClass.d.ts",
138
+ },
139
+ {
140
+ code: `
141
+ import * as foo from "foo";
142
+ import foo = require("foo");
143
+ export { foo };
144
+ export { foo } from "foo";
145
+ export as namespace Foo;
146
+ `,
147
+ filename: "testModule.d.ts",
148
+ },
149
+ {
150
+ code: `
151
+ import * as foo from "foo";
152
+ import foo = require("foo");
153
+ export as namespace Foo;
154
+ `,
155
+ filename: "testModuleAutoExport.d.ts",
156
+ },
157
+ ],
158
+ });
package/tsconfig.json CHANGED
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "extends": "../../tsconfig.base.json",
3
3
  "compilerOptions": {
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
4
6
  "outDir": "dist",
5
7
  "rootDir": "src"
6
8
  },