-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathno-empty-named-blocks.js
107 lines (94 loc) · 3.89 KB
/
no-empty-named-blocks.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { getSourceCode } from 'eslint-module-utils/contextCompat';
import docsUrl from '../docsUrl';
function getEmptyBlockRange(tokens, index) {
const token = tokens[index];
const nextToken = tokens[index + 1];
const prevToken = tokens[index - 1];
let start = token.range[0];
const end = nextToken.range[1];
// Remove block tokens and the previous comma
if (prevToken.value === ',' || prevToken.value === 'type' || prevToken.value === 'typeof') {
start = prevToken.range[0];
}
return [start, end];
}
module.exports = {
meta: {
type: 'suggestion',
docs: {
category: 'Helpful warnings',
description: 'Forbid empty named import blocks.',
url: docsUrl('no-empty-named-blocks'),
},
fixable: 'code',
schema: [],
hasSuggestions: true,
},
create(context) {
const importsWithoutNameds = [];
return {
ImportDeclaration(node) {
if (!node.specifiers.some((x) => x.type === 'ImportSpecifier')) {
importsWithoutNameds.push(node);
}
},
'Program:exit'(program) {
const importsTokens = importsWithoutNameds.map((node) => [node, program.tokens.filter((x) => x.range[0] >= node.range[0] && x.range[1] <= node.range[1])]);
importsTokens.forEach(([node, tokens]) => {
tokens.forEach((token) => {
const idx = program.tokens.indexOf(token);
const nextToken = program.tokens[idx + 1];
if (nextToken && token.value === '{' && nextToken.value === '}') {
const hasOtherIdentifiers = tokens.some((token) => token.type === 'Identifier'
&& token.value !== 'from'
&& token.value !== 'type'
&& token.value !== 'typeof',
);
// If it has no other identifiers it's the only thing in the import, so we can either remove the import
// completely or transform it in a side-effects only import
if (!hasOtherIdentifiers) {
context.report({
node,
message: 'Unexpected empty named import block',
suggest: [
{
desc: 'Remove unused import',
fix(fixer) {
// Remove the whole import
return fixer.remove(node);
},
},
{
desc: 'Remove empty import block',
fix(fixer) {
// Remove the empty block and the 'from' token, leaving the import only for its side
// effects, e.g. `import 'mod'`
const sourceCode = getSourceCode(context);
const fromToken = program.tokens.find((t) => t.value === 'from');
const importToken = program.tokens.find((t) => t.value === 'import');
const hasSpaceAfterFrom = sourceCode.isSpaceBetween(fromToken, sourceCode.getTokenAfter(fromToken));
const hasSpaceAfterImport = sourceCode.isSpaceBetween(importToken, sourceCode.getTokenAfter(fromToken));
const [start] = getEmptyBlockRange(program.tokens, idx);
const [, end] = fromToken.range;
const range = [start, hasSpaceAfterFrom ? end + 1 : end];
return fixer.replaceTextRange(range, hasSpaceAfterImport ? '' : ' ');
},
},
],
});
} else {
context.report({
node,
message: 'Unexpected empty named import block',
fix(fixer) {
return fixer.removeRange(getEmptyBlockRange(program.tokens, idx));
},
});
}
}
});
});
},
};
},
};