Skip to content

Commit 34ba2e0

Browse files
committedJun 23, 2024·
fix(no-undefined-types): treat variables imported by @import tags as defined; fixes #1244
1 parent 0bea154 commit 34ba2e0

File tree

5 files changed

+227
-5
lines changed

5 files changed

+227
-5
lines changed
 

‎docs/rules/no-undefined-types.md

+67
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,36 @@ const a = new Todo();
307307
*/
308308
// Settings: {"jsdoc":{"structuredTags":{"namepathOrURLReferencer":{"name":"namepath-or-url-referencing"}}}}
309309
// Message: The type 'SomeType' is undefined.
310+
311+
/**
312+
* @import BadImportIgnoredByThisRule
313+
*/
314+
/**
315+
* @import LinterDef, { Sth as Something, Another as Another2 } from "eslint"
316+
*/
317+
/**
318+
* @import { Linter } from "eslint"
319+
*/
320+
/**
321+
* @import LinterDefault from "eslint"
322+
*/
323+
/**
324+
* @import {Linter as Lintee} from "eslint"
325+
*/
326+
/**
327+
* @import * as Linters from "eslint"
328+
*/
329+
330+
/**
331+
* @type {BadImportIgnoredByThisRule}
332+
*/
333+
/**
334+
* @type {Sth}
335+
*/
336+
/**
337+
* @type {Another}
338+
*/
339+
// Message: The type 'BadImportIgnoredByThisRule' is undefined.
310340
````
311341

312342

@@ -777,8 +807,45 @@ function quux(foo) {
777807

778808
quux(0);
779809

810+
/**
811+
* @import BadImportIgnoredByThisRule
812+
*/
813+
/**
814+
* @import LinterDef, { Sth as Something, Another as Another2 } from "eslint"
815+
*/
780816
/**
781817
* @import { Linter } from "eslint"
782818
*/
819+
/**
820+
* @import LinterDefault from "eslint"
821+
*/
822+
/**
823+
* @import {Linter as Lintee} from "eslint"
824+
*/
825+
/**
826+
* @import * as Linters from "eslint"
827+
*/
828+
829+
/**
830+
* @type {LinterDef}
831+
*/
832+
/**
833+
* @type {Something}
834+
*/
835+
/**
836+
* @type {Another2}
837+
*/
838+
/**
839+
* @type {Linter}
840+
*/
841+
/**
842+
* @type {LinterDefault}
843+
*/
844+
/**
845+
* @type {Lintee}
846+
*/
847+
/**
848+
* @type {Linters}
849+
*/
783850
````
784851

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"debug": "^4.3.4",
1212
"escape-string-regexp": "^4.0.0",
1313
"esquery": "^1.5.0",
14+
"parse-imports": "^2.0.0",
1415
"semver": "^7.6.2",
1516
"spdx-expression-parse": "^4.0.0"
1617
},

‎pnpm-lock.yaml

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/rules/noUndefinedTypes.js

+54-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import iterateJsdoc, {
2-
parseComment,
3-
} from '../iterateJsdoc.js';
1+
import { parseImportsSync } from 'parse-imports';
42
import {
53
getJSDocComment,
64
parse as parseType,
75
traverse,
86
tryParse as tryParseType,
97
} from '@es-joy/jsdoccomment';
8+
import iterateJsdoc, {
9+
parseComment,
10+
} from '../iterateJsdoc.js';
1011

1112
const extraTypes = [
1213
'null', 'undefined', 'void', 'string', 'boolean', 'object',
@@ -109,13 +110,15 @@ export default iterateJsdoc(({
109110
.filter(Boolean));
110111
}
111112

112-
const typedefDeclarations = sourceCode.getAllComments()
113+
const comments = sourceCode.getAllComments()
113114
.filter((comment) => {
114115
return (/^\*\s/u).test(comment.value);
115116
})
116117
.map((commentNode) => {
117118
return parseComment(commentNode, '');
118-
})
119+
});
120+
121+
const typedefDeclarations = comments
119122
.flatMap((doc) => {
120123
return doc.tags.filter(({
121124
tag,
@@ -127,6 +130,51 @@ export default iterateJsdoc(({
127130
return tag.name;
128131
});
129132

133+
134+
const importTags = /** @type {string[]} */ (comments.flatMap((doc) => {
135+
return doc.tags.filter(({
136+
tag,
137+
}) => {
138+
return tag === 'import';
139+
});
140+
}).flatMap((tag) => {
141+
const {
142+
type, name, description
143+
} = tag;
144+
const typePart = type ? `{${type}} `: '';
145+
const imprt = 'import ' + (description
146+
? `${typePart}${name} ${description}`
147+
: `${typePart}${name}`);
148+
149+
let imports;
150+
try {
151+
// Should technically await non-sync, but ESLint doesn't support sync rules;
152+
// thankfully, the Wasm load time is safely fast
153+
imports = parseImportsSync(imprt);
154+
} catch (err) {
155+
return null;
156+
}
157+
158+
return [...imports].flatMap(({importClause}) => {
159+
/* c8 ignore next */
160+
const {default: dflt, named, namespace} = importClause || {};
161+
const types = [];
162+
if (dflt) {
163+
types.push(dflt);
164+
}
165+
if (namespace) {
166+
types.push(namespace);
167+
}
168+
if (named) {
169+
for (const {binding} of named) {
170+
types.push(binding);
171+
}
172+
}
173+
174+
return types;
175+
});
176+
}).filter(Boolean));
177+
130178
const ancestorNodes = [];
131179

132180
let currentNode = node;
@@ -193,6 +241,7 @@ export default iterateJsdoc(({
193241
)
194242
.concat(extraTypes)
195243
.concat(typedefDeclarations)
244+
.concat(importTags)
196245
.concat(definedTypes)
197246
.concat(/** @type {string[]} */ (definedPreferredTypes))
198247
.concat(

‎test/rules/assertions/noUndefinedTypes.js

+83
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,52 @@ export default {
558558
},
559559
},
560560
},
561+
{
562+
code: `
563+
/**
564+
* @import BadImportIgnoredByThisRule
565+
*/
566+
/**
567+
* @import LinterDef, { Sth as Something, Another as Another2 } from "eslint"
568+
*/
569+
/**
570+
* @import { Linter } from "eslint"
571+
*/
572+
/**
573+
* @import LinterDefault from "eslint"
574+
*/
575+
/**
576+
* @import {Linter as Lintee} from "eslint"
577+
*/
578+
/**
579+
* @import * as Linters from "eslint"
580+
*/
581+
582+
/**
583+
* @type {BadImportIgnoredByThisRule}
584+
*/
585+
/**
586+
* @type {Sth}
587+
*/
588+
/**
589+
* @type {Another}
590+
*/
591+
`,
592+
errors: [
593+
{
594+
line: 22,
595+
message: 'The type \'BadImportIgnoredByThisRule\' is undefined.',
596+
},
597+
{
598+
line: 25,
599+
message: 'The type \'Sth\' is undefined.',
600+
},
601+
{
602+
line: 28,
603+
message: 'The type \'Another\' is undefined.',
604+
},
605+
],
606+
},
561607
],
562608
valid: [
563609
{
@@ -1438,9 +1484,46 @@ export default {
14381484
},
14391485
{
14401486
code: `
1487+
/**
1488+
* @import BadImportIgnoredByThisRule
1489+
*/
1490+
/**
1491+
* @import LinterDef, { Sth as Something, Another as Another2 } from "eslint"
1492+
*/
14411493
/**
14421494
* @import { Linter } from "eslint"
14431495
*/
1496+
/**
1497+
* @import LinterDefault from "eslint"
1498+
*/
1499+
/**
1500+
* @import {Linter as Lintee} from "eslint"
1501+
*/
1502+
/**
1503+
* @import * as Linters from "eslint"
1504+
*/
1505+
1506+
/**
1507+
* @type {LinterDef}
1508+
*/
1509+
/**
1510+
* @type {Something}
1511+
*/
1512+
/**
1513+
* @type {Another2}
1514+
*/
1515+
/**
1516+
* @type {Linter}
1517+
*/
1518+
/**
1519+
* @type {LinterDefault}
1520+
*/
1521+
/**
1522+
* @type {Lintee}
1523+
*/
1524+
/**
1525+
* @type {Linters}
1526+
*/
14441527
`,
14451528
},
14461529
],

0 commit comments

Comments
 (0)
Please sign in to comment.