@definitelytyped/eslint-plugin 0.0.187 → 0.0.188

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 (124) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/configs/all.js +1 -0
  3. package/dist/configs/all.js.map +1 -1
  4. package/dist/rules/index.d.ts +1 -1
  5. package/dist/rules/no-bad-reference.d.ts +1 -1
  6. package/dist/rules/no-bad-reference.js +172 -28
  7. package/dist/rules/no-bad-reference.js.map +1 -1
  8. package/dist/rules/no-declare-current-package.js +4 -1
  9. package/dist/rules/no-declare-current-package.js.map +1 -1
  10. package/dist/rules/no-import-default-of-export-equals.js +4 -3
  11. package/dist/rules/no-import-default-of-export-equals.js.map +1 -1
  12. package/dist/rules/no-import-of-dev-dependencies.js +32 -60
  13. package/dist/rules/no-import-of-dev-dependencies.js.map +1 -1
  14. package/dist/rules/no-old-dt-header.js +2 -1
  15. package/dist/rules/no-old-dt-header.js.map +1 -1
  16. package/dist/rules/no-relative-import-in-test.js +2 -3
  17. package/dist/rules/no-relative-import-in-test.js.map +1 -1
  18. package/dist/rules/no-self-import.js +3 -0
  19. package/dist/rules/no-self-import.js.map +1 -1
  20. package/dist/rules/prefer-declare-function.js +2 -1
  21. package/dist/rules/prefer-declare-function.js.map +1 -1
  22. package/dist/util.d.ts +15 -0
  23. package/dist/util.js +50 -8
  24. package/dist/util.js.map +1 -1
  25. package/package.json +2 -2
  26. package/src/configs/all.ts +1 -0
  27. package/src/rules/no-bad-reference.ts +176 -33
  28. package/src/rules/no-declare-current-package.ts +4 -1
  29. package/src/rules/no-import-default-of-export-equals.ts +2 -1
  30. package/src/rules/no-import-of-dev-dependencies.ts +40 -59
  31. package/src/rules/no-old-dt-header.ts +2 -1
  32. package/src/rules/no-relative-import-in-test.ts +2 -5
  33. package/src/rules/no-self-import.ts +3 -0
  34. package/src/rules/prefer-declare-function.ts +2 -1
  35. package/src/util.ts +69 -9
  36. package/test/fixtureTester.ts +64 -0
  37. package/test/fixtures/notNeededPackages.json +3 -0
  38. package/test/fixtures/package.json +4 -0
  39. package/test/fixtures/types/foo/foo-tests.ts +0 -0
  40. package/test/fixtures/types/foo/index.d.ts +1 -0
  41. package/test/fixtures/types/foo/package.json +5 -0
  42. package/test/fixtures/types/foo/tsconfig.json +20 -0
  43. package/test/fixtures/types/foo/v1/foo-tests.ts +0 -0
  44. package/test/fixtures/types/foo/v1/index.d.ts +1 -0
  45. package/test/fixtures/types/foo/v1/package.json +5 -0
  46. package/test/fixtures/types/foo/v1/tsconfig.json +20 -0
  47. package/test/fixtures/types/no-bad-reference/index.d.ts +16 -0
  48. package/test/fixtures/types/no-bad-reference/no-bad-reference-tests.ts +2 -0
  49. package/test/fixtures/types/no-bad-reference/package.json +5 -0
  50. package/test/fixtures/types/no-bad-reference/tsconfig.json +20 -0
  51. package/test/fixtures/types/no-bad-reference/v0.1/index.d.ts +15 -0
  52. package/test/fixtures/types/no-bad-reference/v0.1/no-bad-reference-tests.ts +0 -0
  53. package/test/fixtures/types/no-bad-reference/v0.1/package.json +5 -0
  54. package/test/fixtures/types/no-bad-reference/v0.1/tsconfig.json +20 -0
  55. package/test/fixtures/types/no-bad-reference/v11/index.d.ts +15 -0
  56. package/test/fixtures/types/no-bad-reference/v11/no-bad-reference-tests.ts +0 -0
  57. package/test/fixtures/types/no-bad-reference/v11/package.json +5 -0
  58. package/test/fixtures/types/no-bad-reference/v11/tsconfig.json +20 -0
  59. package/test/fixtures/types/no-declare-current-package/index.d.ts +1 -0
  60. package/test/fixtures/types/no-declare-current-package/no-declare-current-package-tests.ts +0 -0
  61. package/test/fixtures/types/no-declare-current-package/package.json +8 -0
  62. package/test/fixtures/types/no-declare-current-package/test/deep/import.d.ts +1 -0
  63. package/test/fixtures/types/no-declare-current-package/tsconfig.json +20 -0
  64. package/test/fixtures/types/no-declare-current-package-other/index.d.ts +2 -0
  65. package/test/fixtures/types/no-declare-current-package-other/no-declare-current-package-other-tests.ts +0 -0
  66. package/test/fixtures/types/no-declare-current-package-other/package.json +8 -0
  67. package/test/fixtures/types/no-declare-current-package-other/tsconfig.json +20 -0
  68. package/test/fixtures/types/no-import-of-dev-dependencies/bad.d.ts +5 -0
  69. package/test/fixtures/types/no-import-of-dev-dependencies/index.d.ts +4 -0
  70. package/test/fixtures/types/no-import-of-dev-dependencies/no-import-of-dev-dependencies-tests.ts +9 -0
  71. package/test/fixtures/types/no-import-of-dev-dependencies/package.json +14 -0
  72. package/test/fixtures/types/no-import-of-dev-dependencies/tsconfig.json +20 -0
  73. package/test/fixtures/types/no-relative-references/index.d.ts +12 -0
  74. package/test/fixtures/types/no-relative-references/no-relative-references-tests.ts +0 -0
  75. package/test/fixtures/types/no-relative-references/other/other.d.ts +7 -0
  76. package/test/fixtures/types/no-relative-references/package.json +5 -0
  77. package/test/fixtures/types/no-relative-references/tsconfig.json +20 -0
  78. package/test/fixtures/types/no-relative-references/v1/index.d.ts +15 -0
  79. package/test/fixtures/types/no-relative-references/v1/no-relative-references-tests.ts +0 -0
  80. package/test/fixtures/types/no-relative-references/v1/other/other.d.ts +7 -0
  81. package/test/fixtures/types/no-relative-references/v1/package.json +5 -0
  82. package/test/fixtures/types/no-relative-references/v1/tsconfig.json +20 -0
  83. package/test/fixtures/types/no-self-import/bad.d.ts +8 -0
  84. package/test/fixtures/types/no-self-import/index.d.ts +7 -0
  85. package/test/fixtures/types/no-self-import/no-self-import-tests.ts +9 -0
  86. package/test/fixtures/types/no-self-import/package.json +11 -0
  87. package/test/fixtures/types/no-self-import/tsconfig.json +20 -0
  88. package/test/fixtures/types/other/index.d.ts +1 -0
  89. package/test/fixtures/types/other/other-tests.ts +0 -0
  90. package/test/fixtures/types/other/package.json +5 -0
  91. package/test/fixtures/types/other/tsconfig.json +20 -0
  92. package/test/fixtures/types/other/v1/index.d.ts +1 -0
  93. package/test/fixtures/types/other/v1/other-tests.ts +0 -0
  94. package/test/fixtures/types/other/v1/package.json +5 -0
  95. package/test/fixtures/types/other/v1/tsconfig.json +20 -0
  96. package/test/fixtures/types/scoped__foo/index.d.ts +1 -0
  97. package/test/fixtures/types/scoped__foo/package.json +5 -0
  98. package/test/fixtures/types/scoped__foo/scoped__foo-tests.ts +0 -0
  99. package/test/fixtures/types/scoped__foo/tsconfig.json +20 -0
  100. package/test/fixtures/types/scoped__foo/v1/index.d.ts +1 -0
  101. package/test/fixtures/types/scoped__foo/v1/package.json +5 -0
  102. package/test/fixtures/types/scoped__foo/v1/scoped__foo-tests.ts +0 -0
  103. package/test/fixtures/types/scoped__foo/v1/tsconfig.json +20 -0
  104. package/test/fixtures/types/scoped__no-declare-current-package/index.d.ts +1 -0
  105. package/test/fixtures/types/scoped__no-declare-current-package/no-declare-current-package-tests.ts +0 -0
  106. package/test/fixtures/types/scoped__no-declare-current-package/package.json +8 -0
  107. package/test/fixtures/types/scoped__no-declare-current-package/test/deep/import.d.ts +1 -0
  108. package/test/fixtures/types/scoped__no-declare-current-package/tsconfig.json +20 -0
  109. package/test/fixtures/types/scoped__no-relative-references/index.d.ts +12 -0
  110. package/test/fixtures/types/scoped__no-relative-references/no-relative-references-tests.ts +0 -0
  111. package/test/fixtures/types/scoped__no-relative-references/other/other.d.ts +7 -0
  112. package/test/fixtures/types/scoped__no-relative-references/package.json +5 -0
  113. package/test/fixtures/types/scoped__no-relative-references/tsconfig.json +20 -0
  114. package/test/fixtures/types/scoped__no-relative-references/v1/index.d.ts +15 -0
  115. package/test/fixtures/types/scoped__no-relative-references/v1/no-relative-references-tests.ts +0 -0
  116. package/test/fixtures/types/scoped__no-relative-references/v1/other/other.d.ts +7 -0
  117. package/test/fixtures/types/scoped__no-relative-references/v1/package.json +5 -0
  118. package/test/fixtures/types/scoped__no-relative-references/v1/tsconfig.json +20 -0
  119. package/test/no-bad-reference.test.ts +87 -94
  120. package/test/no-declare-current-package.test.ts +31 -31
  121. package/test/no-import-of-dev-dependencies.test.ts +14 -59
  122. package/test/no-self-import.test.ts +16 -100
  123. package/test/util.test.ts +29 -18
  124. package/tsconfig.tsbuildinfo +1 -1
package/dist/util.js CHANGED
@@ -1,21 +1,22 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.commentsMatching = exports.getTypesPackageForDeclarationFile = exports.createRule = void 0;
6
+ exports.findTypesPackage = exports.commentsMatching = exports.getTypesPackageForDeclarationFile = exports.createRule = void 0;
4
7
  const utils_1 = require("@definitelytyped/utils");
5
8
  const utils_2 = require("@typescript-eslint/utils");
9
+ const path_1 = __importDefault(require("path"));
10
+ const fs_1 = __importDefault(require("fs"));
6
11
  // Possible TS bug can't figure out how to do declaration emit of created rules
7
12
  // without an explicit type annotation here due to pnpm symlink stuff
8
13
  exports.createRule = utils_2.ESLintUtils.RuleCreator((name) => `https://github.com/microsoft/DefinitelyTyped-tools/tree/master/packages/eslint-plugin/docs/rules/${name}.md`);
9
14
  function getTypesPackageForDeclarationFile(file) {
10
- var _a, _b;
11
- if (!file.endsWith(".d.ts")) {
15
+ var _a;
16
+ if (!(0, utils_1.isDeclarationPath)(file)) {
12
17
  return undefined;
13
18
  }
14
- const match = (_a = file.match(/types\/([^\/]+)\//)) === null || _a === void 0 ? void 0 : _a[1];
15
- if (!match) {
16
- return undefined;
17
- }
18
- return (_b = (0, utils_1.unmangleScopedPackage)(match)) !== null && _b !== void 0 ? _b : match;
19
+ return (_a = findTypesPackage(file)) === null || _a === void 0 ? void 0 : _a.realName;
19
20
  }
20
21
  exports.getTypesPackageForDeclarationFile = getTypesPackageForDeclarationFile;
21
22
  function commentsMatching(sourceFile, regex, f) {
@@ -26,4 +27,45 @@ function commentsMatching(sourceFile, regex, f) {
26
27
  }
27
28
  }
28
29
  exports.commentsMatching = commentsMatching;
30
+ function findUp(p, fn) {
31
+ p = path_1.default.resolve(p);
32
+ const root = path_1.default.parse(p).root;
33
+ while (true) {
34
+ const v = fn(p);
35
+ if (v !== undefined) {
36
+ return v;
37
+ }
38
+ if (p === root) {
39
+ break;
40
+ }
41
+ p = path_1.default.dirname(p);
42
+ }
43
+ return undefined;
44
+ }
45
+ // TODO(jakebailey): pull this helper out to util package?
46
+ function isTypesPackage(packageJson) {
47
+ return (typeof packageJson.name === "string" &&
48
+ packageJson.name.startsWith("@types/") &&
49
+ typeof packageJson.version === "string" &&
50
+ Array.isArray(packageJson.owners));
51
+ }
52
+ function findTypesPackage(file) {
53
+ return findUp(file, (p) => {
54
+ const packageJsonPath = path_1.default.join(p, "package.json");
55
+ if (!fs_1.default.existsSync(packageJsonPath)) {
56
+ return undefined;
57
+ }
58
+ const packageJsonContents = fs_1.default.readFileSync(packageJsonPath, "utf8");
59
+ const packageJson = JSON.parse(packageJsonContents);
60
+ if (!isTypesPackage(packageJson)) {
61
+ return undefined;
62
+ }
63
+ return {
64
+ dir: p,
65
+ packageJson,
66
+ realName: (0, utils_1.typesPackageNameToRealName)(packageJson.name),
67
+ };
68
+ });
69
+ }
70
+ exports.findTypesPackage = findTypesPackage;
29
71
  //# sourceMappingURL=util.js.map
package/dist/util.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;;AAAA,kDAA+D;AAC/D,oDAAiE;AAIjE,+EAA+E;AAC/E,qEAAqE;AACxD,QAAA,UAAU,GAEkB,mBAAW,CAAC,WAAW,CAC9D,CAAC,IAAI,EAAE,EAAE,CACP,oGAAoG,IAAI,KAAK,CAChH,CAAC;AAEF,SAAgB,iCAAiC,CAAC,IAAY;;IAC5D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;QAC3B,OAAO,SAAS,CAAC;KAClB;IAED,MAAM,KAAK,GAAG,MAAA,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,0CAAG,CAAC,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK,EAAE;QACV,OAAO,SAAS,CAAC;KAClB;IAED,OAAO,MAAA,IAAA,6BAAqB,EAAC,KAAK,CAAC,mCAAI,KAAK,CAAC;AAC/C,CAAC;AAXD,8EAWC;AAED,SAAgB,gBAAgB,CAC9B,UAAgC,EAChC,KAAa,EACb,CAA+C;IAE/C,KAAK,MAAM,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE;QAC7C,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,CAAC;YAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;KACzB;AACH,CAAC;AATD,4CASC"}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;;;;;AAAA,kDAAuF;AACvF,oDAAiE;AAGjE,gDAAwB;AACxB,4CAAoB;AAEpB,+EAA+E;AAC/E,qEAAqE;AACxD,QAAA,UAAU,GAEkB,mBAAW,CAAC,WAAW,CAC9D,CAAC,IAAI,EAAE,EAAE,CACP,oGAAoG,IAAI,KAAK,CAChH,CAAC;AAEF,SAAgB,iCAAiC,CAAC,IAAY;;IAC5D,IAAI,CAAC,IAAA,yBAAiB,EAAC,IAAI,CAAC,EAAE;QAC5B,OAAO,SAAS,CAAC;KAClB;IACD,OAAO,MAAA,gBAAgB,CAAC,IAAI,CAAC,0CAAE,QAAQ,CAAC;AAC1C,CAAC;AALD,8EAKC;AAED,SAAgB,gBAAgB,CAC9B,UAAgC,EAChC,KAAa,EACb,CAA+C;IAE/C,KAAK,MAAM,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE;QAC7C,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,CAAC;YAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;KACzB;AACH,CAAC;AATD,4CASC;AAED,SAAS,MAAM,CAAe,CAAS,EAAE,EAAgC;IACvE,CAAC,GAAG,cAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,IAAI,GAAG,cAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEhC,OAAO,IAAI,EAAE;QACX,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QAChB,IAAI,CAAC,KAAK,SAAS,EAAE;YACnB,OAAO,CAAC,CAAC;SACV;QACD,IAAI,CAAC,KAAK,IAAI,EAAE;YACd,MAAM;SACP;QACD,CAAC,GAAG,cAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;KACrB;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAkBD,0DAA0D;AAC1D,SAAS,cAAc,CAAC,WAAiC;IACvD,OAAO,CACL,OAAO,WAAW,CAAC,IAAI,KAAK,QAAQ;QACpC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QACtC,OAAO,WAAW,CAAC,OAAO,KAAK,QAAQ;QACvC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAClC,CAAC;AACJ,CAAC;AAED,SAAgB,gBAAgB,CAAC,IAAY;IAC3C,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;QACxB,MAAM,eAAe,GAAG,cAAI,CAAC,IAAI,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;QACrD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE;YACnC,OAAO,SAAS,CAAC;SAClB;QAED,MAAM,mBAAmB,GAAG,YAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QACrE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACpD,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE;YAChC,OAAO,SAAS,CAAC;SAClB;QACD,OAAO;YACL,GAAG,EAAE,CAAC;YACN,WAAW;YACX,QAAQ,EAAE,IAAA,kCAA0B,EAAC,WAAW,CAAC,IAAI,CAAC;SACvD,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAlBD,4CAkBC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@definitelytyped/eslint-plugin",
3
- "version": "0.0.187",
3
+ "version": "0.0.188",
4
4
  "description": "ESLint rules for DefinitelyTyped",
5
5
  "main": "./dist/index.js",
6
6
  "publishConfig": {
@@ -20,7 +20,7 @@
20
20
  "dependencies": {
21
21
  "@typescript-eslint/types": "^5.56.0",
22
22
  "@typescript-eslint/utils": "^5.55.0",
23
- "@definitelytyped/utils": "0.0.183"
23
+ "@definitelytyped/utils": "0.0.184"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "@typescript-eslint/eslint-plugin": "^5.0.0",
@@ -136,6 +136,7 @@ export const all: Linter.BaseConfig = {
136
136
  },
137
137
  rules: {
138
138
  ...Object.fromEntries(Object.keys(rules).map((name) => [`@definitelytyped/${name}`, "error"])),
139
+ "unicode-bom": ["error", "never"],
139
140
  "@typescript-eslint/ban-ts-comment": [
140
141
  "error",
141
142
  {
@@ -1,58 +1,201 @@
1
- import { TSESTree } from "@typescript-eslint/utils";
2
- import { commentsMatching, createRule } from "../util";
1
+ import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
2
+ import { createRule, findTypesPackage } from "../util";
3
+ import * as ts from "typescript";
4
+ import path from "path";
5
+ import { isDeclarationPath } from "@definitelytyped/utils";
6
+
7
+ interface Reference {
8
+ kind: "path" | "import" | "types";
9
+ text: string;
10
+ range: ts.TextRange;
11
+ }
3
12
 
4
- type MessageId = "referencePathPackage" | "referencePathTest" | "referencePathOldVersion";
5
13
  const rule = createRule({
6
14
  name: "no-bad-reference",
7
15
  defaultOptions: [],
8
16
  meta: {
9
17
  type: "problem",
10
18
  docs: {
11
- description: `Forbids <reference path="./vNN"/> in all files, <reference path="../etc"/> in declaration files, and all <reference path> in test files.`,
19
+ description:
20
+ "Forbids bad references, including those that resolve outside of the package or path references in non-declaration files.",
12
21
  recommended: "error",
13
22
  },
14
23
  messages: {
15
- referencePathPackage:
16
- "Don't use <reference path> to reference another package. Use an import or <reference types> instead.",
17
- referencePathTest:
18
- "Don't use <reference path> in test files. Use <reference types> or include the file in 'tsconfig.json'.",
19
- referencePathOldVersion: "Don't use <reference path> to reference an old version of the current package.",
24
+ importOutside:
25
+ 'The import "{{text}}" resolves outside of the package. Use a bare import to reference other packages.',
26
+ importLeaves: 'The import "{{text}}" resolves to the current package, but uses relative paths.',
27
+ referenceOutside:
28
+ 'The reference "{{text}}" resolves outside of the package. Use a global reference to reference other packages.',
29
+ referenceLeaves: 'The reference "{{text}}" resolves to the current package, but uses relative paths.',
30
+ testReference:
31
+ 'The path reference "{{text}}" is disallowed outside declaration files. Use "<reference types>" or include the file in tsconfig instead.',
32
+ backslashes: "Use forward slashes in paths.",
20
33
  },
21
34
  schema: [],
22
35
  },
23
36
  create(context) {
24
- const isDeclarationFile = context.getFilename().endsWith(".d.ts");
25
- commentsMatching(context.getSourceCode(), /<reference\s+path\s*=\s*"(.+)"\s*\/>/, (ref, comment) => {
26
- if (ref.match(/^\.\/v\d+(?:\.\d+)?(?:\/.*)?$/)) {
27
- report(comment, "referencePathOldVersion");
28
- }
29
- if (isDeclarationFile) {
30
- if (ref.startsWith("..")) {
31
- report(comment, "referencePathPackage");
32
- }
37
+ const containingFileName = context.getFilename();
38
+ const typesPackage = findTypesPackage(containingFileName);
39
+ if (!typesPackage) {
40
+ return {};
41
+ }
42
+
43
+ const containingDirectory = path.dirname(containingFileName);
44
+
45
+ const realNamePlusSlash = typesPackage.realName + "/";
46
+ function isRelativeOrSelf(name: string) {
47
+ return name.startsWith(".") || name.startsWith(realNamePlusSlash);
48
+ }
49
+
50
+ const ast = context.getSourceCode().ast;
51
+ const parserServices = ESLintUtils.getParserServices(context, true);
52
+ const sourceFile = parserServices.esTreeNodeToTSNodeMap.get(ast);
53
+
54
+ const refs: Reference[] = [];
55
+ for (const ref of sourceFile.referencedFiles) {
56
+ if (isDeclarationPath(containingFileName)) {
57
+ refs.push({ kind: "path", text: ref.fileName, range: ref });
33
58
  } else {
34
- report(comment, "referencePathTest");
59
+ context.report({
60
+ messageId: "testReference",
61
+ loc: tsRangeToESLintLocation(ref, sourceFile),
62
+ data: { text: ref.fileName },
63
+ });
64
+ }
65
+ }
66
+ for (const ref of sourceFile.typeReferenceDirectives) {
67
+ if (isRelativeOrSelf(ref.fileName)) {
68
+ refs.push({ kind: "types", text: ref.fileName, range: ref });
35
69
  }
36
- });
70
+ }
71
+ for (const ref of imports(sourceFile)) {
72
+ if (isRelativeOrSelf(ref.text)) {
73
+ refs.push({ kind: "import", text: ref.text, range: ref });
74
+ }
75
+ }
37
76
 
38
- return {};
77
+ for (const ref of refs) {
78
+ if (ref.text.includes("\\")) {
79
+ context.report({
80
+ messageId: "backslashes",
81
+ loc: tsRangeToESLintLocation(ref.range, sourceFile),
82
+ });
83
+ }
84
+
85
+ const p = ref.text.startsWith(realNamePlusSlash)
86
+ ? path.posix.join(
87
+ path.posix.relative(containingDirectory, typesPackage.dir),
88
+ ref.text.slice(realNamePlusSlash.length)
89
+ )
90
+ : ref.text;
91
+
92
+ const resolved = path.resolve(containingDirectory, p);
93
+ const otherPackage = findTypesPackage(resolved);
94
+
95
+ if (otherPackage && otherPackage.dir === typesPackage.dir) {
96
+ // Perf trick; if a path doesn't have ".." anywhere, then it can't have resolved
97
+ // up and out of a package dir so we can skip this work.
98
+ if (p.includes("..")) {
99
+ // If we resolved to something in the correct package, we still could have
100
+ // gotten here by leaving the package (up into a parent, or down into a versioned dir).
101
+ // Manually walk the path to see if that happened.
102
+ const parts = p.split("/");
103
+ let cwd = containingDirectory;
104
+ for (const part of parts) {
105
+ if (part === "" || part === ".") {
106
+ continue;
107
+ }
108
+ if (part === "..") {
109
+ cwd = path.posix.dirname(cwd);
110
+ } else {
111
+ cwd = path.posix.join(cwd, part);
112
+ }
113
+ const otherPackage = findTypesPackage(cwd);
114
+ if (otherPackage && otherPackage.dir === typesPackage.dir) {
115
+ continue;
116
+ }
117
+
118
+ context.report({
119
+ messageId: ref.kind === "import" ? "importLeaves" : "referenceLeaves",
120
+ loc: tsRangeToESLintLocation(ref.range, sourceFile),
121
+ data: { text: ref.text },
122
+ });
123
+ }
124
+ }
125
+
126
+ continue;
127
+ }
39
128
 
40
- function report(comment: TSESTree.Comment, messageId: MessageId) {
41
129
  context.report({
42
- loc: {
43
- end: {
44
- column: comment.value.lastIndexOf(`"`),
45
- line: comment.loc.end.line,
46
- },
47
- start: {
48
- column: comment.value.indexOf(`"`) + 1,
49
- line: comment.loc.start.line,
50
- },
51
- },
52
- messageId,
130
+ messageId: ref.kind === "import" ? "importOutside" : "referenceOutside",
131
+ loc: tsRangeToESLintLocation(ref.range, sourceFile),
132
+ data: { text: ref.text },
53
133
  });
54
134
  }
135
+
136
+ return {};
55
137
  },
56
138
  });
57
139
 
140
+ function tsRangeToESLintLocation(range: ts.TextRange, sourceFile: ts.SourceFile): TSESTree.SourceLocation {
141
+ const pos = sourceFile.getLineAndCharacterOfPosition(range.pos);
142
+ const end = sourceFile.getLineAndCharacterOfPosition(range.end);
143
+ return {
144
+ start: { line: pos.line + 1, column: pos.character + 1 },
145
+ end: { line: end.line + 1, column: end.character + 1 },
146
+ };
147
+ }
148
+
149
+ /**
150
+ * All strings referenced in `import` statements.
151
+ * Does *not* include <reference> directives.
152
+ */
153
+ function imports({ statements }: ts.SourceFile): Iterable<ts.StringLiteralLike> {
154
+ const result: ts.StringLiteralLike[] = [];
155
+ for (const node of statements) {
156
+ recur(node);
157
+ }
158
+ return result;
159
+
160
+ function recur(node: ts.Node) {
161
+ switch (node.kind) {
162
+ case ts.SyntaxKind.ImportDeclaration:
163
+ case ts.SyntaxKind.ExportDeclaration: {
164
+ const { moduleSpecifier } = node as ts.ImportDeclaration | ts.ExportDeclaration;
165
+ if (moduleSpecifier && moduleSpecifier.kind === ts.SyntaxKind.StringLiteral) {
166
+ result.push(moduleSpecifier as ts.StringLiteral);
167
+ }
168
+ break;
169
+ }
170
+
171
+ case ts.SyntaxKind.ImportEqualsDeclaration: {
172
+ const { moduleReference } = node as ts.ImportEqualsDeclaration;
173
+ if (moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
174
+ result.push(parseRequire(moduleReference));
175
+ }
176
+ break;
177
+ }
178
+
179
+ case ts.SyntaxKind.ImportType: {
180
+ const { argument } = node as ts.ImportTypeNode;
181
+ if (ts.isLiteralTypeNode(argument) && ts.isStringLiteral(argument.literal)) {
182
+ result.push(argument.literal);
183
+ }
184
+ break;
185
+ }
186
+
187
+ default:
188
+ ts.forEachChild(node, recur);
189
+ }
190
+ }
191
+ }
192
+
193
+ function parseRequire(reference: ts.ExternalModuleReference): ts.StringLiteralLike {
194
+ const { expression } = reference;
195
+ if (!expression || !ts.isStringLiteral(expression)) {
196
+ throw new Error(`Bad 'import =' reference: ${reference.getText()}`);
197
+ }
198
+ return expression;
199
+ }
200
+
58
201
  export = rule;
@@ -19,6 +19,9 @@ const rule = createRule({
19
19
  },
20
20
  create(context) {
21
21
  const packageName = getTypesPackageForDeclarationFile(context.getFilename());
22
+ if (!packageName) {
23
+ return {};
24
+ }
22
25
 
23
26
  return {
24
27
  // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -28,7 +31,7 @@ const rule = createRule({
28
31
  (node.id.value === packageName || node.id.value.startsWith(packageName + "/"))
29
32
  ) {
30
33
  const text = node.id.value;
31
- const preferred = text === packageName ? '"index.d.ts"' : `"${text}.d.ts" or "${text}/index.d.ts`;
34
+ const preferred = text === packageName ? '"index.d.ts"' : `"${text}.d.ts" or "${text}/index.d.ts"`;
32
35
  context.report({
33
36
  messageId: "noDeclareCurrentPackage",
34
37
  data: { text, preferred },
@@ -1,3 +1,4 @@
1
+ import { isDeclarationPath } from "@definitelytyped/utils";
1
2
  import { createRule } from "../util";
2
3
  import { ESLintUtils } from "@typescript-eslint/utils";
3
4
  import * as ts from "typescript";
@@ -19,7 +20,7 @@ const rule = createRule({
19
20
  create(context) {
20
21
  const parserServices = ESLintUtils.getParserServices(context);
21
22
  const checker = parserServices.program.getTypeChecker();
22
- if (context.getFilename().endsWith(".d.ts")) {
23
+ if (isDeclarationPath(context.getFilename())) {
23
24
  return {
24
25
  // eslint-disable-next-line @typescript-eslint/naming-convention
25
26
  ImportDeclaration(node) {
@@ -1,8 +1,6 @@
1
1
  import { TSESTree } from "@typescript-eslint/utils";
2
- import { createRule, commentsMatching, getTypesPackageForDeclarationFile } from "../util";
3
- import fs from "fs";
4
- import path from "path";
5
- import { unmangleScopedPackage } from "@definitelytyped/utils";
2
+ import { createRule, commentsMatching, findTypesPackage } from "../util";
3
+ import { isDeclarationPath, isTypesPackageName, typesPackageNameToRealName } from "@definitelytyped/utils";
6
4
 
7
5
  type MessageId = "noImportOfDevDependencies" | "noReferenceOfDevDependencies";
8
6
  const rule = createRule({
@@ -21,45 +19,47 @@ const rule = createRule({
21
19
  schema: [],
22
20
  },
23
21
  create(context) {
24
- const packageName = getTypesPackageForDeclarationFile(context.getFilename());
25
- if (context.getFilename().endsWith(".d.ts")) {
26
- const packageJson = getPackageJson(context.getPhysicalFilename?.() ?? context.getFilename());
27
- const devdeps = packageJson
28
- ? Object.keys(packageJson.devDependencies).map((dep) => {
29
- const withoutTypes = dep.replace(/^@types\//, "");
30
- return unmangleScopedPackage(withoutTypes) || withoutTypes;
31
- })
32
- : [];
33
- const deps = packageJson
34
- ? Object.keys(packageJson.dependencies ?? {}).map((dep) => {
35
- const withoutTypes = dep.replace(/^@types\//, "");
36
- return unmangleScopedPackage(withoutTypes) || withoutTypes;
37
- })
38
- : [];
39
- commentsMatching(context.getSourceCode(), /<reference\s+types\s*=\s*"(.+)"\s*\/>/, (ref, comment) => {
40
- if (devdeps.includes(ref) && ref !== packageName && !deps.includes(ref)) {
41
- report(comment, "noReferenceOfDevDependencies");
42
- }
43
- });
22
+ if (!isDeclarationPath(context.getFilename())) {
23
+ return {};
24
+ }
44
25
 
45
- return {
46
- // eslint-disable-next-line @typescript-eslint/naming-convention
47
- ImportDeclaration(node) {
48
- if (
49
- devdeps.includes(node.source.value) &&
50
- node.source.value !== packageName &&
51
- !deps.includes(node.source.value)
52
- ) {
53
- context.report({
54
- messageId: "noImportOfDevDependencies",
55
- node,
56
- });
57
- }
58
- },
59
- };
60
- } else {
26
+ const info = findTypesPackage(context.getFilename());
27
+ if (!info) {
28
+ return {};
29
+ }
30
+
31
+ const packageJson = info.packageJson;
32
+ if (!packageJson.devDependencies) {
61
33
  return {};
62
34
  }
35
+
36
+ const devDeps = Object.keys(packageJson.devDependencies)
37
+ .map((dep) => {
38
+ if (isTypesPackageName(dep)) {
39
+ return typesPackageNameToRealName(dep);
40
+ }
41
+ return dep;
42
+ })
43
+ .filter((dep) => dep !== info.realName && packageJson.dependencies?.[dep] === undefined); // TODO(jakebailey): add test for this case from https://github.com/microsoft/DefinitelyTyped-tools/pull/773
44
+
45
+ commentsMatching(context.getSourceCode(), /<reference\s+types\s*=\s*"(.+)"\s*\/>/, (ref, comment) => {
46
+ if (devDeps.includes(ref)) {
47
+ report(comment, "noReferenceOfDevDependencies");
48
+ }
49
+ });
50
+
51
+ return {
52
+ // eslint-disable-next-line @typescript-eslint/naming-convention
53
+ ImportDeclaration(node) {
54
+ if (devDeps.includes(node.source.value)) {
55
+ context.report({
56
+ messageId: "noImportOfDevDependencies",
57
+ node,
58
+ });
59
+ }
60
+ },
61
+ };
62
+
63
63
  function report(comment: TSESTree.Comment, messageId: MessageId) {
64
64
  context.report({
65
65
  loc: {
@@ -77,24 +77,5 @@ const rule = createRule({
77
77
  }
78
78
  },
79
79
  });
80
- function getPackageJson(
81
- sourceFile: string
82
- ): { dependencies?: Record<string, string>; devDependencies: Record<string, string> } | undefined {
83
- let dir = path.dirname(sourceFile);
84
- let text: string | undefined;
85
- while (dir !== "/") {
86
- try {
87
- text = fs.readFileSync(path.join(dir, "package.json"), "utf8");
88
- break;
89
- } catch {
90
- // presumably because file does not exist, so continue
91
- }
92
- dir = path.dirname(dir);
93
- }
94
- if (!text) return undefined;
95
- const json = JSON.parse(text);
96
- if ("devDependencies" in json) return json;
97
- return undefined;
98
- }
99
80
 
100
81
  export = rule;
@@ -1,3 +1,4 @@
1
+ import { isDeclarationPath } from "@definitelytyped/utils";
1
2
  import { createRule } from "../util";
2
3
 
3
4
  const rule = createRule({
@@ -18,7 +19,7 @@ const rule = createRule({
18
19
  create(context) {
19
20
  const text = context.getSourceCode().text;
20
21
  if (
21
- context.getFilename().endsWith(".d.ts") &&
22
+ isDeclarationPath(context.getFilename()) &&
22
23
  text.indexOf("// Type definitions for ") === 0 &&
23
24
  text.indexOf("// Definitions by: ") > 0
24
25
  ) {
@@ -1,6 +1,7 @@
1
1
  import { ESLintUtils } from "@typescript-eslint/utils";
2
2
  import * as ts from "typescript";
3
3
  import { createRule } from "../util";
4
+ import { isDeclarationPath } from "@definitelytyped/utils";
4
5
 
5
6
  const rule = createRule({
6
7
  name: "no-relative-import-in-test",
@@ -18,11 +19,7 @@ const rule = createRule({
18
19
  schema: [],
19
20
  },
20
21
  create(context) {
21
- if (
22
- context.getFilename().endsWith(".d.ts") ||
23
- context.getFilename().endsWith(".d.mts") ||
24
- context.getFilename().endsWith(".d.cts")
25
- ) {
22
+ if (isDeclarationPath(context.getFilename())) {
26
23
  return {};
27
24
  }
28
25
 
@@ -17,6 +17,9 @@ const rule = createRule({
17
17
  },
18
18
  create(context) {
19
19
  const packageName = getTypesPackageForDeclarationFile(context.getFilename());
20
+ if (!packageName) {
21
+ return {};
22
+ }
20
23
 
21
24
  return {
22
25
  // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -1,6 +1,7 @@
1
1
  import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
2
2
 
3
3
  import { createRule } from "../util";
4
+ import { isDeclarationPath } from "@definitelytyped/utils";
4
5
 
5
6
  const rule = createRule({
6
7
  defaultOptions: [],
@@ -22,7 +23,7 @@ const rule = createRule({
22
23
  "VariableDeclaration > VariableDeclarator"(node: TSESTree.VariableDeclarator) {
23
24
  if (
24
25
  node.id.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES.TSFunctionType &&
25
- context.getFilename().endsWith(".d.ts")
26
+ isDeclarationPath(context.getFilename())
26
27
  ) {
27
28
  context.report({
28
29
  messageId: "variableFunction",
package/src/util.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { unmangleScopedPackage } from "@definitelytyped/utils";
1
+ import { isDeclarationPath, typesPackageNameToRealName } from "@definitelytyped/utils";
2
2
  import { TSESTree, ESLintUtils } from "@typescript-eslint/utils";
3
3
  import { RuleWithMetaAndName } from "@typescript-eslint/utils/dist/eslint-utils";
4
4
  import { RuleListener, RuleModule, SourceCode } from "@typescript-eslint/utils/dist/ts-eslint";
5
+ import path from "path";
6
+ import fs from "fs";
5
7
 
6
8
  // Possible TS bug can't figure out how to do declaration emit of created rules
7
9
  // without an explicit type annotation here due to pnpm symlink stuff
@@ -13,16 +15,10 @@ export const createRule: <TOptions extends readonly unknown[], TMessageIds exten
13
15
  );
14
16
 
15
17
  export function getTypesPackageForDeclarationFile(file: string) {
16
- if (!file.endsWith(".d.ts")) {
18
+ if (!isDeclarationPath(file)) {
17
19
  return undefined;
18
20
  }
19
-
20
- const match = file.match(/types\/([^\/]+)\//)?.[1];
21
- if (!match) {
22
- return undefined;
23
- }
24
-
25
- return unmangleScopedPackage(match) ?? match;
21
+ return findTypesPackage(file)?.realName;
26
22
  }
27
23
 
28
24
  export function commentsMatching(
@@ -35,3 +31,67 @@ export function commentsMatching(
35
31
  if (m) f(m[1], comment);
36
32
  }
37
33
  }
34
+
35
+ function findUp<T extends {}>(p: string, fn: (p: string) => T | undefined): T | undefined {
36
+ p = path.resolve(p);
37
+ const root = path.parse(p).root;
38
+
39
+ while (true) {
40
+ const v = fn(p);
41
+ if (v !== undefined) {
42
+ return v;
43
+ }
44
+ if (p === root) {
45
+ break;
46
+ }
47
+ p = path.dirname(p);
48
+ }
49
+
50
+ return undefined;
51
+ }
52
+
53
+ export interface TypesPackageInfo {
54
+ dir: string;
55
+ /** package.json with name="@types/foo__bar-baz" */
56
+ packageJson: PackageJSON;
57
+ /** real package name being typed, like "@foo/bar-baz" */
58
+ realName: string;
59
+ }
60
+
61
+ export interface PackageJSON {
62
+ name: string;
63
+ version: string;
64
+ owners: string[];
65
+ dependencies?: Record<string, string | undefined>;
66
+ devDependencies?: Record<string, string | undefined>;
67
+ }
68
+
69
+ // TODO(jakebailey): pull this helper out to util package?
70
+ function isTypesPackage(packageJson: Partial<PackageJSON>): boolean {
71
+ return (
72
+ typeof packageJson.name === "string" &&
73
+ packageJson.name.startsWith("@types/") &&
74
+ typeof packageJson.version === "string" &&
75
+ Array.isArray(packageJson.owners)
76
+ );
77
+ }
78
+
79
+ export function findTypesPackage(file: string): TypesPackageInfo | undefined {
80
+ return findUp(file, (p) => {
81
+ const packageJsonPath = path.join(p, "package.json");
82
+ if (!fs.existsSync(packageJsonPath)) {
83
+ return undefined;
84
+ }
85
+
86
+ const packageJsonContents = fs.readFileSync(packageJsonPath, "utf8");
87
+ const packageJson = JSON.parse(packageJsonContents);
88
+ if (!isTypesPackage(packageJson)) {
89
+ return undefined;
90
+ }
91
+ return {
92
+ dir: p,
93
+ packageJson,
94
+ realName: typesPackageNameToRealName(packageJson.name),
95
+ };
96
+ });
97
+ }