-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat(eslint-plugin): add no-useless-empty-export
rule
#4380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
bradzacher
merged 12 commits into
typescript-eslint:main
from
JoshuaKGoldberg:no-useless-empty-export
Feb 23, 2022
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
a67eb47
feat(eslint-plugin): add `no-useless-empty-export` rule
JoshuaKGoldberg 7896f4f
chore: handle module declarations and fix table list
JoshuaKGoldberg 4181694
chore: add empty body case
JoshuaKGoldberg fa7c2c7
Merge branch 'main' into no-useless-empty-export
04ff072
Merge branch 'main' into no-useless-empty-export
0501bea
Merge branch 'main' into no-useless-empty-export
2412ab4
Merge branch 'main' into no-useless-empty-export
a6c635d
chore: update utils package name in no-useless-empty-export.ts
JoshuaKGoldberg b4e7ddf
Update packages/eslint-plugin/tests/rules/no-useless-empty-export.tes…
JoshuaKGoldberg 225e5aa
Merge branch 'main' into no-useless-empty-export
2509b70
Merge branch 'main' into no-useless-empty-export
JoshuaKGoldberg e909d2a
docs: corrected docs file
JoshuaKGoldberg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
45 changes: 45 additions & 0 deletions
45
packages/eslint-plugin/docs/rules/no-useless-empty-export.md
This file contains hidden or 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,45 @@ | ||
# `no-useless-empty-export` | ||
|
||
Disallow empty exports that don't change anything in a module file. | ||
|
||
## Rule Details | ||
|
||
An empty `export {}` statement is sometimes useful in TypeScript code to turn a file that would otherwise be a script file into a module file. | ||
Per the TypeScript Handbook [Modules](https://www.typescriptlang.org/docs/handbook/modules.html) page: | ||
|
||
> In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. | ||
> Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well). | ||
|
||
However, an `export {}` statement does nothing if there are any other top-level import or export statements in a file. | ||
|
||
Examples of code for this rule: | ||
|
||
<!--tabs--> | ||
|
||
### ❌ Incorrect | ||
|
||
```ts | ||
export const value = 'Hello, world!'; | ||
export {}; | ||
``` | ||
|
||
```ts | ||
import 'some-other-module'; | ||
export {}; | ||
``` | ||
|
||
### ✅ Correct | ||
|
||
```ts | ||
export const value = 'Hello, world!'; | ||
``` | ||
|
||
```ts | ||
import 'some-other-module'; | ||
``` | ||
|
||
## Attributes | ||
|
||
- [ ] ✅ Recommended | ||
- [x] 🔧 Fixable | ||
- [ ] 💭 Requires type information |
This file contains hidden or 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 hidden or 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
79 changes: 79 additions & 0 deletions
79
packages/eslint-plugin/src/rules/no-useless-empty-export.ts
This file contains hidden or 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,79 @@ | ||
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'; | ||
import * as util from '../util'; | ||
|
||
function isEmptyExport( | ||
node: TSESTree.Node, | ||
): node is TSESTree.ExportNamedDeclaration { | ||
return ( | ||
node.type === AST_NODE_TYPES.ExportNamedDeclaration && | ||
node.specifiers.length === 0 && | ||
!node.declaration | ||
); | ||
} | ||
|
||
const exportOrImportNodeTypes = new Set([ | ||
AST_NODE_TYPES.ExportAllDeclaration, | ||
AST_NODE_TYPES.ExportDefaultDeclaration, | ||
AST_NODE_TYPES.ExportNamedDeclaration, | ||
AST_NODE_TYPES.ExportSpecifier, | ||
AST_NODE_TYPES.ImportDeclaration, | ||
AST_NODE_TYPES.TSExportAssignment, | ||
AST_NODE_TYPES.TSImportEqualsDeclaration, | ||
]); | ||
|
||
export default util.createRule({ | ||
name: 'no-useless-empty-export', | ||
meta: { | ||
docs: { | ||
description: | ||
"Disallow empty exports that don't change anything in a module file", | ||
recommended: false, | ||
suggestion: true, | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages: { | ||
uselessExport: 'Empty export does nothing and can be removed.', | ||
}, | ||
schema: [], | ||
type: 'suggestion', | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
function checkNode( | ||
node: TSESTree.Program | TSESTree.TSModuleDeclaration, | ||
): void { | ||
if (!Array.isArray(node.body)) { | ||
return; | ||
} | ||
|
||
let emptyExport: TSESTree.ExportNamedDeclaration | undefined; | ||
let foundOtherExport = false; | ||
|
||
for (const statement of node.body) { | ||
if (isEmptyExport(statement)) { | ||
emptyExport = statement; | ||
|
||
if (foundOtherExport) { | ||
break; | ||
} | ||
} else if (exportOrImportNodeTypes.has(statement.type)) { | ||
foundOtherExport = true; | ||
} | ||
} | ||
|
||
if (emptyExport && foundOtherExport) { | ||
context.report({ | ||
fix: fixer => fixer.remove(emptyExport!), | ||
messageId: 'uselessExport', | ||
node: emptyExport, | ||
}); | ||
} | ||
} | ||
|
||
return { | ||
Program: checkNode, | ||
TSModuleDeclaration: checkNode, | ||
}; | ||
}, | ||
}); |
125 changes: 125 additions & 0 deletions
125
packages/eslint-plugin/tests/rules/no-useless-empty-export.test.ts
This file contains hidden or 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,125 @@ | ||
/* eslint-disable eslint-comments/no-use */ | ||
// this rule tests the spacing, which prettier will want to fix and break the tests | ||
/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ | ||
/* eslint-enable eslint-comments/no-use */ | ||
import rule from '../../src/rules/no-useless-empty-export'; | ||
import { RuleTester } from '../RuleTester'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parserOptions: { | ||
ecmaVersion: 2020, | ||
sourceType: 'module', | ||
}, | ||
parser: '@typescript-eslint/parser', | ||
}); | ||
|
||
const error = { | ||
messageId: 'uselessExport', | ||
} as const; | ||
|
||
ruleTester.run('no-useless-empty-export', rule, { | ||
valid: [ | ||
"declare module '_'", | ||
"import {} from '_';", | ||
"import * as _ from '_';", | ||
'export = {};', | ||
'export = 3;', | ||
'export const _ = {};', | ||
` | ||
const _ = {}; | ||
export default _; | ||
`, | ||
` | ||
export * from '_'; | ||
export = {}; | ||
`, | ||
` | ||
export {}; | ||
`, | ||
], | ||
invalid: [ | ||
{ | ||
code: ` | ||
export const _ = {}; | ||
export {}; | ||
`, | ||
errors: [error], | ||
output: ` | ||
export const _ = {}; | ||
|
||
`, | ||
}, | ||
{ | ||
code: ` | ||
export * from '_'; | ||
export {}; | ||
`, | ||
errors: [error], | ||
output: ` | ||
export * from '_'; | ||
|
||
`, | ||
}, | ||
{ | ||
code: ` | ||
export {}; | ||
export * from '_'; | ||
`, | ||
errors: [error], | ||
output: ` | ||
|
||
export * from '_'; | ||
`, | ||
}, | ||
{ | ||
code: ` | ||
const _ = {}; | ||
export default _; | ||
export {}; | ||
`, | ||
errors: [error], | ||
output: ` | ||
const _ = {}; | ||
export default _; | ||
|
||
`, | ||
}, | ||
{ | ||
code: ` | ||
export {}; | ||
const _ = {}; | ||
export default _; | ||
`, | ||
errors: [error], | ||
output: ` | ||
|
||
const _ = {}; | ||
export default _; | ||
`, | ||
}, | ||
{ | ||
code: ` | ||
const _ = {}; | ||
export { _ }; | ||
export {}; | ||
`, | ||
errors: [error], | ||
output: ` | ||
const _ = {}; | ||
export { _ }; | ||
|
||
`, | ||
}, | ||
{ | ||
code: ` | ||
import _ = require('_'); | ||
export {}; | ||
`, | ||
errors: [error], | ||
output: ` | ||
import _ = require('_'); | ||
|
||
`, | ||
}, | ||
], | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.