Skip to content

Commit 376d583

Browse files
authored
feat(prefer-import-tags): add new rule; fixes #1314 (#1536)
1 parent 0c4c654 commit 376d583

File tree

10 files changed

+1622
-13
lines changed

10 files changed

+1622
-13
lines changed

.README/rules/prefer-import-tag.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# `prefer-import-tag`
2+
3+
Prefer `@import` tags to inline `import()` statements.
4+
5+
## Fixer
6+
7+
Creates `@import` tags if an already existing matching `@typedef` or
8+
`@import` is not found.
9+
10+
## Options
11+
12+
{"gitdown": "options"}
13+
14+
|||
15+
|---|---|
16+
|Context|everywhere|
17+
|Tags|`augments`, `class`, `constant`, `enum`, `implements`, `member`, `module`, `namespace`, `param`, `property`, `returns`, `throws`, `type`, `typedef`, `yields`|
18+
|Aliases|`constructor`, `const`, `extends`, `var`, `arg`, `argument`, `prop`, `return`, `exception`, `yield`|
19+
|Closure-only|`package`, `private`, `protected`, `public`, `static`|
20+
|Recommended|false|
21+
|Settings|`mode`|
22+
|Options|`enableFixer`, `exemptTypedefs`, `outputType`|
23+
24+
## Failing examples
25+
26+
<!-- assertions-failing preferImportTag -->
27+
28+
## Passing examples
29+
30+
<!-- assertions-passing preferImportTag -->

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ non-default-recommended fixer).
456456
||| [no-restricted-syntax](./docs/rules/no-restricted-syntax.md#readme) | Reports when certain comment structures are present. |
457457
|On in TS; Off in TS flavor|:wrench:| [no-types](./docs/rules/no-types.md#readme) | This rule reports types being used on `@param` or `@returns` (redundant with TypeScript). |
458458
|:heavy_check_mark: (Off in TS; Off in TS flavor)|| [no-undefined-types](./docs/rules/no-undefined-types.md#readme) | Besides some expected built-in types, prohibits any types not specified as globals or within `@typedef`. |
459+
||:wrench:| [prefer-import-tag](./docs/rules/prefer-import-tag.md#readme) | Prefer `@import` tags to inline `import()` statements. |
459460
|:heavy_check_mark:|| [reject-any-type](./docs/rules/reject-any-type.md#readme) | Reports use of `any` or `*` type |
460461
|:heavy_check_mark:|| [reject-function-type](./docs/rules/reject-function-type.md#readme) | Reports use of `Function` type |
461462
||:wrench:| [require-asterisk-prefix](./docs/rules/require-asterisk-prefix.md#readme) | Requires that each JSDoc line starts with an `*`. |

docs/rules/prefer-import-tag.md

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
<a name="user-content-prefer-import-tag"></a>
2+
<a name="prefer-import-tag"></a>
3+
# <code>prefer-import-tag</code>
4+
5+
Prefer `@import` tags to inline `import()` statements.
6+
7+
<a name="user-content-prefer-import-tag-fixer"></a>
8+
<a name="prefer-import-tag-fixer"></a>
9+
## Fixer
10+
11+
Creates `@import` tags if an already existing matching `@typedef` or
12+
`@import` is not found.
13+
14+
<a name="user-content-prefer-import-tag-options"></a>
15+
<a name="prefer-import-tag-options"></a>
16+
## Options
17+
18+
A single options object has the following properties.
19+
20+
<a name="user-content-prefer-import-tag-options-enablefixer"></a>
21+
<a name="prefer-import-tag-options-enablefixer"></a>
22+
### <code>enableFixer</code>
23+
24+
Whether or not to enable the fixer to add `@import` tags.
25+
<a name="user-content-prefer-import-tag-options-exempttypedefs"></a>
26+
<a name="prefer-import-tag-options-exempttypedefs"></a>
27+
### <code>exemptTypedefs</code>
28+
29+
Whether to allow `import()` statements within `@typedef`
30+
<a name="user-content-prefer-import-tag-options-outputtype"></a>
31+
<a name="prefer-import-tag-options-outputtype"></a>
32+
### <code>outputType</code>
33+
34+
What kind of `@import` to generate when no matching `@typedef` or `@import` is found
35+
36+
37+
|||
38+
|---|---|
39+
|Context|everywhere|
40+
|Tags|`augments`, `class`, `constant`, `enum`, `implements`, `member`, `module`, `namespace`, `param`, `property`, `returns`, `throws`, `type`, `typedef`, `yields`|
41+
|Aliases|`constructor`, `const`, `extends`, `var`, `arg`, `argument`, `prop`, `return`, `exception`, `yield`|
42+
|Closure-only|`package`, `private`, `protected`, `public`, `static`|
43+
|Recommended|false|
44+
|Settings|`mode`|
45+
|Options|`enableFixer`, `exemptTypedefs`, `outputType`|
46+
47+
<a name="user-content-prefer-import-tag-failing-examples"></a>
48+
<a name="prefer-import-tag-failing-examples"></a>
49+
## Failing examples
50+
51+
The following patterns are considered problems:
52+
53+
````ts
54+
/**
55+
* @type {import('eslint').Rule.Node}
56+
*/
57+
// Message: Inline `import()` found; prefer `@import`
58+
59+
/**
60+
* @type {import('eslint').Rule.Node}
61+
*/
62+
// Settings: {"jsdoc":{"mode":"permissive"}}
63+
// Message: Inline `import()` found; prefer `@import`
64+
65+
/**
66+
* @type {import('eslint').Rule.Node}
67+
*/
68+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"enableFixer":false}]
69+
// Message: Inline `import()` found; prefer `@import`
70+
71+
/**
72+
* @type {import('eslint').Rule.Node}
73+
*/
74+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"outputType":"named-import"}]
75+
// Message: Inline `import()` found; prefer `@import`
76+
77+
/**
78+
* @type {import('eslint').Rule.Node}
79+
*/
80+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"outputType":"namespaced-import"}]
81+
// Message: Inline `import()` found; prefer `@import`
82+
83+
/**
84+
* @type {import('eslint').Rule['Node']}
85+
*/
86+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"outputType":"named-import"}]
87+
// Message: Inline `import()` found; prefer `@import`
88+
89+
/**
90+
* @type {import('eslint').Rule['Node']}
91+
*/
92+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"outputType":"namespaced-import"}]
93+
// Message: Inline `import()` found; prefer `@import`
94+
95+
/** @typedef {import('eslint2').Rule.Node} RuleNode */
96+
/**
97+
* @type {import('eslint').Rule.Node}
98+
*/
99+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":false}]
100+
// Message: Inline `import()` found; prefer `@import`
101+
102+
/**
103+
* @type {import('eslint')}
104+
*/
105+
// Message: Inline `import()` found; prefer `@import`
106+
107+
/**
108+
* @type {import('eslint')}
109+
*/
110+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"enableFixer":false}]
111+
// Message: Inline `import()` found; prefer `@import`
112+
113+
/**
114+
* @type {import('eslint').default}
115+
*/
116+
// Message: Inline `import()` found; prefer `@import`
117+
118+
/**
119+
* @type {import('eslint').default}
120+
*/
121+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"enableFixer":false}]
122+
// Message: Inline `import()` found; prefer `@import`
123+
124+
/** @import * as eslint2 from 'eslint'; */
125+
/**
126+
* @type {import('eslint')}
127+
*/
128+
// Message: Inline `import()` found; prefer `@import`
129+
130+
/** @import eslint2 from 'eslint'; */
131+
/**
132+
* @type {import('eslint').default}
133+
*/
134+
// Message: Inline `import()` found; prefer `@import`
135+
136+
/** @import eslint2 from 'eslint'; */
137+
/**
138+
* @type {import('eslint').default}
139+
*/
140+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"enableFixer":false}]
141+
// Message: Inline `import()` found; prefer `@import`
142+
143+
/** @import {Rule} from 'eslint' */
144+
/**
145+
* @type {import('eslint').Rule.Node}
146+
*/
147+
// Message: Inline `import()` found; prefer `@import`
148+
149+
/** @import {Rule} from 'eslint' */
150+
/**
151+
* @type {import('eslint').Rule.Node}
152+
*/
153+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"enableFixer":false}]
154+
// Message: Inline `import()` found; prefer `@import`
155+
156+
/** @import * as eslint2 from 'eslint' */
157+
/**
158+
* @type {import('eslint').Rule.Node}
159+
*/
160+
// Message: Inline `import()` found; prefer `@import`
161+
162+
/** @import * as eslint2 from 'eslint' */
163+
/**
164+
* @type {import('eslint').Rule.Node}
165+
*/
166+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"enableFixer":false}]
167+
// Message: Inline `import()` found; prefer `@import`
168+
169+
/** @import LinterDef2, * as LinterDef3 from "eslint" */
170+
/**
171+
* @type {import('eslint').Rule.Node}
172+
*/
173+
// Message: Inline `import()` found; prefer `@import`
174+
175+
/**
176+
* @import LinterDef2, * as LinterDef3 from "eslint"
177+
*/
178+
/**
179+
* @type {import('eslint').Rule.Node}
180+
*/
181+
// Message: Inline `import()` found; prefer `@import`
182+
183+
/**
184+
* @import LinterDef2,
185+
* * as LinterDef3 from "eslint"
186+
*/
187+
/**
188+
* @type {import('eslint').Rule.Node}
189+
*/
190+
// Message: Inline `import()` found; prefer `@import`
191+
192+
/**
193+
* @import {
194+
* ESLint
195+
* } from "eslint"
196+
*/
197+
/**
198+
* @type {import('eslint').ESLint.Node}
199+
*/
200+
// Message: Inline `import()` found; prefer `@import`
201+
202+
/** @typedef {import('eslint').Rule} Rule */
203+
/**
204+
* @type {import('eslint').Rule.Node}
205+
*/
206+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":true}]
207+
// Message: Inline `import()` found; using `@typedef`
208+
209+
/** @typedef {import('eslint').Rule} Rule */
210+
/**
211+
* @type {import('eslint').Rule.Node.Abc.Def}
212+
*/
213+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":true}]
214+
// Message: Inline `import()` found; using `@typedef`
215+
216+
/** @typedef {import('eslint').Rule} Rule */
217+
/**
218+
* @type {import('eslint').Rule.Node.Abc['Def']}
219+
*/
220+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":true}]
221+
// Message: Inline `import()` found; using `@typedef`
222+
223+
/** @typedef {import('eslint').Rule.Node} RuleNode */
224+
/**
225+
* @type {import('eslint').Rule.Node}
226+
*/
227+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":true}]
228+
// Message: Inline `import()` found; using `@typedef`
229+
230+
/**
231+
* @type {number|import('eslint').Rule.Node}
232+
*/
233+
// Message: Inline `import()` found; prefer `@import`
234+
235+
/** @typedef {import('eslint').Rule.Node} Rule */
236+
/**
237+
* @type {import('eslint').Rule}
238+
*/
239+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":true}]
240+
// Message: Inline `import()` found; prefer `@import`
241+
242+
/** @typedef {import('eslint').Rule.Node} Rule */
243+
/**
244+
* @type {import('eslint').Rule.Abc}
245+
*/
246+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":true}]
247+
// Message: Inline `import()` found; prefer `@import`
248+
249+
/** @typedef {import('eslint').Rule} Rule */
250+
/**
251+
* @type {import('eslint').Rule.Node.Abc.Rule}
252+
*/
253+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":true}]
254+
// Message: Inline `import()` found; using `@typedef`
255+
256+
/** @typedef {import('eslint').Rule} Rule */
257+
/**
258+
* @type {import('eslint').Rule.Node.Abc.Rule}
259+
*/
260+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"enableFixer":false,"exemptTypedefs":true}]
261+
// Message: Inline `import()` found; using `@typedef`
262+
263+
/** @typedef {import('eslint').Rule.Rule} Rule */
264+
/**
265+
* @type {import('eslint').Abc.Rule}
266+
*/
267+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":true}]
268+
// Message: Inline `import()` found; prefer `@import`
269+
````
270+
271+
272+
273+
<a name="user-content-prefer-import-tag-passing-examples"></a>
274+
<a name="prefer-import-tag-passing-examples"></a>
275+
## Passing examples
276+
277+
The following patterns are not considered problems:
278+
279+
````ts
280+
/** @typedef {import('eslint').Rule.Node} RuleNode */
281+
/**
282+
* @type {RuleNode}
283+
*/
284+
// "jsdoc/prefer-import-tag": ["error"|"warn", {"exemptTypedefs":true}]
285+
286+
/** @import {Rule} from 'eslint' */
287+
/**
288+
* @type {Rule.Node}
289+
*/
290+
291+
/** @import * as eslint from 'eslint' */
292+
/**
293+
* @type {eslint.Rule.Node}
294+
*/
295+
296+
/**
297+
* @type {Rule['Node']}
298+
*/
299+
300+
/**
301+
* Silently ignores error
302+
* @type {Rule['Node'}
303+
*/
304+
````
305+

src/index-cjs.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import noMultiAsterisks from './rules/noMultiAsterisks.js';
3737
import noRestrictedSyntax from './rules/noRestrictedSyntax.js';
3838
import noTypes from './rules/noTypes.js';
3939
import noUndefinedTypes from './rules/noUndefinedTypes.js';
40+
import preferImportTag from './rules/preferImportTag.js';
4041
import requireAsteriskPrefix from './rules/requireAsteriskPrefix.js';
4142
import requireDescription from './rules/requireDescription.js';
4243
import requireDescriptionCompleteSentence from './rules/requireDescriptionCompleteSentence.js';
@@ -111,6 +112,7 @@ index.rules = {
111112
'no-restricted-syntax': noRestrictedSyntax,
112113
'no-types': noTypes,
113114
'no-undefined-types': noUndefinedTypes,
115+
'prefer-import-tag': preferImportTag,
114116
'reject-any-type': buildRejectOrPreferRuleDefinition({
115117
description: 'Reports use of `any` or `*` type',
116118
overrideSettings: {
@@ -283,6 +285,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
283285
'jsdoc/no-restricted-syntax': 'off',
284286
'jsdoc/no-types': 'off',
285287
'jsdoc/no-undefined-types': warnOrError,
288+
'jsdoc/prefer-import-tag': 'off',
286289
'jsdoc/reject-any-type': warnOrError,
287290
'jsdoc/reject-function-type': warnOrError,
288291
'jsdoc/require-asterisk-prefix': 'off',

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import noMultiAsterisks from './rules/noMultiAsterisks.js';
4343
import noRestrictedSyntax from './rules/noRestrictedSyntax.js';
4444
import noTypes from './rules/noTypes.js';
4545
import noUndefinedTypes from './rules/noUndefinedTypes.js';
46+
import preferImportTag from './rules/preferImportTag.js';
4647
import requireAsteriskPrefix from './rules/requireAsteriskPrefix.js';
4748
import requireDescription from './rules/requireDescription.js';
4849
import requireDescriptionCompleteSentence from './rules/requireDescriptionCompleteSentence.js';
@@ -117,6 +118,7 @@ index.rules = {
117118
'no-restricted-syntax': noRestrictedSyntax,
118119
'no-types': noTypes,
119120
'no-undefined-types': noUndefinedTypes,
121+
'prefer-import-tag': preferImportTag,
120122
'reject-any-type': buildRejectOrPreferRuleDefinition({
121123
description: 'Reports use of `any` or `*` type',
122124
overrideSettings: {
@@ -289,6 +291,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
289291
'jsdoc/no-restricted-syntax': 'off',
290292
'jsdoc/no-types': 'off',
291293
'jsdoc/no-undefined-types': warnOrError,
294+
'jsdoc/prefer-import-tag': 'off',
292295
'jsdoc/reject-any-type': warnOrError,
293296
'jsdoc/reject-function-type': warnOrError,
294297
'jsdoc/require-asterisk-prefix': 'off',

0 commit comments

Comments
 (0)