-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[New] Add
no-empty-named-blocks
rule
- Loading branch information
1 parent
4bfe644
commit 2e1edd6
Showing
7 changed files
with
237 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# import/no-empty-named-blocks | ||
|
||
Reports the use of empty named import blocks. | ||
|
||
## Rule Details | ||
|
||
### Valid | ||
```js | ||
import { mod } from 'mod' | ||
import Default, { mod } from 'mod' | ||
``` | ||
|
||
When using typescript | ||
```js | ||
import type { mod } from 'mod' | ||
``` | ||
|
||
When using flow | ||
```js | ||
import typeof { mod } from 'mod' | ||
``` | ||
|
||
### Invalid | ||
```js | ||
import {} from 'mod' | ||
import Default, {} from 'mod' | ||
``` | ||
|
||
When using typescript | ||
```js | ||
import type Default, {} from 'mod' | ||
import type {} from 'mod' | ||
``` | ||
|
||
When using flow | ||
```js | ||
import typeof {} from 'mod' | ||
import typeof Default, {} from 'mod' | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
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: { | ||
url: docsUrl('no-empty-named-blocks'), | ||
}, | ||
fixable: 'code', | ||
schema: [], | ||
hasSuggestions: true, | ||
}, | ||
|
||
create(context) { | ||
return { | ||
Program(node) { | ||
node.tokens.forEach((token, idx) => { | ||
const nextToken = node.tokens[idx + 1]; | ||
|
||
if (nextToken && token.value === '{' && nextToken.value === '}') { | ||
const hasOtherIdentifiers = node.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 = context.getSourceCode(); | ||
const fromToken = node.tokens.find(t => t.value === 'from'); | ||
const importToken = node.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(node.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(node.tokens, idx)); | ||
}, | ||
}); | ||
} | ||
} | ||
}); | ||
}, | ||
}; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import {} from './bar.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { parsers, test } from '../utils'; | ||
|
||
import { RuleTester } from 'eslint'; | ||
|
||
const ruleTester = new RuleTester(); | ||
const rule = require('rules/no-empty-named-blocks'); | ||
|
||
|
||
function generateSuggestionsTestCases(cases, parser) { | ||
return cases.map(code => test({ | ||
code, | ||
parser, | ||
errors: [{ | ||
suggestions: [ | ||
{ | ||
desc: 'Remove unused import', | ||
output: '', | ||
}, | ||
{ | ||
desc: 'Remove empty import block', | ||
output: `import 'mod';`, | ||
}, | ||
], | ||
}], | ||
})); | ||
} | ||
|
||
ruleTester.run('no-empty-named-blocks', rule, { | ||
valid: [].concat( | ||
test({ code: `import 'mod';` }), | ||
test({ code: `import Default from 'mod';` }), | ||
test({ code: `import { Named } from 'mod';` }), | ||
test({ code: `import Default, { Named } from 'mod';` }), | ||
test({ code: `import * as Namespace from 'mod';` }), | ||
|
||
// Typescript | ||
parsers.TS_NEW ? [ | ||
test({ code: `import type Default from 'mod';`, parser: parsers.TS_NEW }), | ||
test({ code: `import type { Named } from 'mod';`, parser: parsers.TS_NEW }), | ||
test({ code: `import type Default, { Named } from 'mod';`, parser: parsers.TS_NEW }), | ||
test({ code: `import type * as Namespace from 'mod';`, parser: parsers.TS_NEW }), | ||
] : [], | ||
|
||
// Flow | ||
test({ code: `import typeof Default from 'mod';`, parser: parsers.BABEL_OLD }), | ||
test({ code: `import typeof { Named } from 'mod';`, parser: parsers.BABEL_OLD }), | ||
test({ code: `import typeof Default, { Named } from 'mod';`, parser: parsers.BABEL_OLD }), | ||
), | ||
invalid: [].concat( | ||
test({ | ||
code: `import Default, {} from 'mod';`, | ||
output: `import Default from 'mod';`, | ||
errors: ['Unexpected empty named import block'], | ||
}), | ||
generateSuggestionsTestCases([ | ||
`import {} from 'mod';`, | ||
`import{}from'mod';`, | ||
`import {} from'mod';`, | ||
`import {}from 'mod';`, | ||
]), | ||
|
||
// Typescript | ||
parsers.TS_NEW ? [].concat( | ||
generateSuggestionsTestCases( | ||
[ | ||
`import type {} from 'mod';`, | ||
`import type {}from 'mod';`, | ||
`import type{}from 'mod';`, | ||
`import type {}from'mod';`, | ||
], | ||
parsers.TS_NEW, | ||
), | ||
test({ | ||
code: `import type Default, {} from 'mod';`, | ||
output: `import type Default from 'mod';`, | ||
parser: parsers.TS_NEW, | ||
errors: ['Unexpected empty named import block'], | ||
}), | ||
) : [], | ||
|
||
// Flow | ||
generateSuggestionsTestCases( | ||
[ | ||
`import typeof {} from 'mod';`, | ||
`import typeof {}from 'mod';`, | ||
`import typeof {} from'mod';`, | ||
`import typeof{}from'mod';`, | ||
], | ||
parsers.BABEL_OLD, | ||
), | ||
test({ | ||
code: `import typeof Default, {} from 'mod';`, | ||
output: `import typeof Default from 'mod';`, | ||
parser: parsers.BABEL_OLD, | ||
errors: ['Unexpected empty named import block'], | ||
}), | ||
), | ||
}); |