Skip to content

Commit 9be9ec4

Browse files
authored
import-style: use simple selectors (#2127)
1 parent 035fbe0 commit 9be9ec4

File tree

4 files changed

+128
-85
lines changed

4 files changed

+128
-85
lines changed

rules/import-style.js

Lines changed: 88 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22
const {defaultsDeep} = require('lodash');
33
const {getStringIfConstant} = require('@eslint-community/eslint-utils');
4-
const {callExpressionSelector} = require('./selectors/index.js');
4+
const {isCallExpression} = require('./ast/index.js');
55

66
const MESSAGE_ID = 'importStyle';
77
const messages = {
@@ -117,6 +117,12 @@ const joinOr = words => words
117117
})
118118
.join(' ');
119119

120+
const isAssignedDynamicImport = node =>
121+
node.parent.type === 'AwaitExpression'
122+
&& node.parent.argument === node
123+
&& node.parent.parent.type === 'VariableDeclarator'
124+
&& node.parent.parent.init === node.parent;
125+
120126
// Keep this alphabetically sorted for easier maintenance
121127
const defaultStyles = {
122128
chalk: {
@@ -130,19 +136,6 @@ const defaultStyles = {
130136
},
131137
};
132138

133-
const assignedDynamicImportSelector = [
134-
'VariableDeclarator',
135-
'[init.type="AwaitExpression"]',
136-
'[init.argument.type="ImportExpression"]',
137-
].join('');
138-
139-
const assignedRequireSelector = [
140-
'VariableDeclarator',
141-
'[init.type="CallExpression"]',
142-
'[init.callee.type="Identifier"]',
143-
'[init.callee.name="require"]',
144-
].join('');
145-
146139
/** @param {import('eslint').Rule.RuleContext} context */
147140
const create = context => {
148141
let [
@@ -201,106 +194,117 @@ const create = context => {
201194
});
202195
};
203196

204-
let visitor = {};
205-
206197
if (checkImport) {
207-
visitor = {
208-
...visitor,
209-
210-
ImportDeclaration(node) {
211-
const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
198+
context.on('ImportDeclaration', node => {
199+
const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
212200

213-
const allowedImportStyles = styles.get(moduleName);
214-
const actualImportStyles = getActualImportDeclarationStyles(node);
201+
const allowedImportStyles = styles.get(moduleName);
202+
const actualImportStyles = getActualImportDeclarationStyles(node);
215203

216-
report(node, moduleName, actualImportStyles, allowedImportStyles);
217-
},
218-
};
204+
report(node, moduleName, actualImportStyles, allowedImportStyles);
205+
});
219206
}
220207

221208
if (checkDynamicImport) {
222-
visitor = {
223-
...visitor,
209+
context.on('ImportExpression', node => {
210+
if (isAssignedDynamicImport(node)) {
211+
return;
212+
}
224213

225-
'ExpressionStatement > ImportExpression'(node) {
226-
const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
227-
const allowedImportStyles = styles.get(moduleName);
228-
const actualImportStyles = ['unassigned'];
214+
const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
215+
const allowedImportStyles = styles.get(moduleName);
216+
const actualImportStyles = ['unassigned'];
229217

230-
report(node, moduleName, actualImportStyles, allowedImportStyles);
231-
},
218+
report(node, moduleName, actualImportStyles, allowedImportStyles);
219+
});
232220

233-
[assignedDynamicImportSelector](node) {
234-
const assignmentTargetNode = node.id;
235-
const moduleNameNode = node.init.argument.source;
236-
const moduleName = getStringIfConstant(moduleNameNode, sourceCode.getScope(moduleNameNode));
221+
context.on('VariableDeclarator', node => {
222+
if (!(
223+
node.init?.type === 'AwaitExpression'
224+
&& node.init.argument.type === 'ImportExpression'
225+
)) {
226+
return;
227+
}
237228

238-
if (!moduleName) {
239-
return;
240-
}
229+
const assignmentTargetNode = node.id;
230+
const moduleNameNode = node.init.argument.source;
231+
const moduleName = getStringIfConstant(moduleNameNode, sourceCode.getScope(moduleNameNode));
241232

242-
const allowedImportStyles = styles.get(moduleName);
243-
const actualImportStyles = getActualAssignmentTargetImportStyles(assignmentTargetNode);
233+
if (!moduleName) {
234+
return;
235+
}
244236

245-
report(node, moduleName, actualImportStyles, allowedImportStyles);
246-
},
247-
};
237+
const allowedImportStyles = styles.get(moduleName);
238+
const actualImportStyles = getActualAssignmentTargetImportStyles(assignmentTargetNode);
239+
240+
report(node, moduleName, actualImportStyles, allowedImportStyles);
241+
});
248242
}
249243

250244
if (checkExportFrom) {
251-
visitor = {
252-
...visitor,
245+
context.on('ExportAllDeclaration', node => {
246+
const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
253247

254-
ExportAllDeclaration(node) {
255-
const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
248+
const allowedImportStyles = styles.get(moduleName);
249+
const actualImportStyles = ['namespace'];
256250

257-
const allowedImportStyles = styles.get(moduleName);
258-
const actualImportStyles = ['namespace'];
259-
260-
report(node, moduleName, actualImportStyles, allowedImportStyles);
261-
},
251+
report(node, moduleName, actualImportStyles, allowedImportStyles);
252+
});
262253

263-
ExportNamedDeclaration(node) {
264-
const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
254+
context.on('ExportNamedDeclaration', node => {
255+
const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
265256

266-
const allowedImportStyles = styles.get(moduleName);
267-
const actualImportStyles = getActualExportDeclarationStyles(node);
257+
const allowedImportStyles = styles.get(moduleName);
258+
const actualImportStyles = getActualExportDeclarationStyles(node);
268259

269-
report(node, moduleName, actualImportStyles, allowedImportStyles);
270-
},
271-
};
260+
report(node, moduleName, actualImportStyles, allowedImportStyles);
261+
});
272262
}
273263

274264
if (checkRequire) {
275-
visitor = {
276-
...visitor,
265+
context.on('CallExpression', node => {
266+
if (!(
267+
isCallExpression(node, {
268+
name: 'require',
269+
argumentsLength: 1,
270+
optionalCall: false,
271+
optionalMember: false,
272+
})
273+
&& (node.parent.type === 'ExpressionStatement' && node.parent.expression === node)
274+
)) {
275+
return;
276+
}
277277

278-
[`ExpressionStatement > ${callExpressionSelector({name: 'require', argumentsLength: 1})}.expression`](node) {
279-
const moduleName = getStringIfConstant(node.arguments[0], sourceCode.getScope(node.arguments[0]));
280-
const allowedImportStyles = styles.get(moduleName);
281-
const actualImportStyles = ['unassigned'];
278+
const moduleName = getStringIfConstant(node.arguments[0], sourceCode.getScope(node.arguments[0]));
279+
const allowedImportStyles = styles.get(moduleName);
280+
const actualImportStyles = ['unassigned'];
282281

283-
report(node, moduleName, actualImportStyles, allowedImportStyles, true);
284-
},
282+
report(node, moduleName, actualImportStyles, allowedImportStyles, true);
283+
});
285284

286-
[assignedRequireSelector](node) {
287-
const assignmentTargetNode = node.id;
288-
const moduleNameNode = node.init.arguments[0];
289-
const moduleName = getStringIfConstant(moduleNameNode, sourceCode.getScope(moduleNameNode));
285+
context.on('VariableDeclarator', node => {
286+
if (!(
287+
node.init?.type === 'CallExpression'
288+
&& node.init.callee.type === 'Identifier'
289+
&& node.init.callee.name === 'require'
290+
)) {
291+
return;
292+
}
290293

291-
if (!moduleName) {
292-
return;
293-
}
294+
const assignmentTargetNode = node.id;
295+
const moduleNameNode = node.init.arguments[0];
296+
const moduleName = getStringIfConstant(moduleNameNode, sourceCode.getScope(moduleNameNode));
294297

295-
const allowedImportStyles = styles.get(moduleName);
296-
const actualImportStyles = getActualAssignmentTargetImportStyles(assignmentTargetNode);
298+
if (!moduleName) {
299+
return;
300+
}
297301

298-
report(node, moduleName, actualImportStyles, allowedImportStyles, true);
299-
},
300-
};
301-
}
302+
const allowedImportStyles = styles.get(moduleName);
303+
const actualImportStyles = getActualAssignmentTargetImportStyles(assignmentTargetNode);
302304

303-
return visitor;
305+
report(node, moduleName, actualImportStyles, allowedImportStyles, true);
306+
});
307+
}
304308
};
305309

306310
const schema = {

test/import-style.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,9 @@ test.babel({
621621
});
622622

623623
test.snapshot({
624-
valid: [],
624+
valid: [
625+
'let a',
626+
],
625627
invalid: [
626628
'import util from \'util\'',
627629
'import * as util from \'util\'',

test/snapshots/import-style.mjs.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,40 @@ Generated by [AVA](https://avajs.dev).
7777
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use default import for module \`chalk\`.␊
7878
3 | }␊
7979
`
80+
81+
## Invalid #7
82+
1 | async () => {
83+
2 | const {red} = await import('chalk');
84+
3 | }
85+
86+
> Error 2/2
87+
88+
`␊
89+
1 | async () => {␊
90+
> 2 | const {red} = await import('chalk');␊
91+
| ^^^^^^^^^^^^^^^ Use default import for module \`chalk\`.␊
92+
3 | }␊
93+
`
94+
95+
## Invalid #1
96+
1 | async () => {
97+
2 | const {red} = await import('chalk');
98+
3 | }
99+
100+
> Error 1/2
101+
102+
`␊
103+
1 | async () => {␊
104+
> 2 | const {red} = await import('chalk');␊
105+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use default import for module \`chalk\`.␊
106+
3 | }␊
107+
`
108+
109+
> Error 2/2
110+
111+
`␊
112+
1 | async () => {␊
113+
> 2 | const {red} = await import('chalk');␊
114+
| ^^^^^^^^^^^^^^^ Use default import for module \`chalk\`.␊
115+
3 | }␊
116+
`

test/snapshots/import-style.mjs.snap

41 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)