Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(eslint-plugin): addno-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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
12 commits
Select commitHold shift + click to select a range
a67eb47
feat(eslint-plugin): add `no-useless-empty-export` rule
JoshuaKGoldbergDec 31, 2021
7896f4f
chore: handle module declarations and fix table list
JoshuaKGoldbergJan 1, 2022
4181694
chore: add empty body case
JoshuaKGoldbergJan 2, 2022
fa7c2c7
Merge branch 'main' into no-useless-empty-export
Jan 2, 2022
04ff072
Merge branch 'main' into no-useless-empty-export
Jan 2, 2022
0501bea
Merge branch 'main' into no-useless-empty-export
Jan 13, 2022
2412ab4
Merge branch 'main' into no-useless-empty-export
Jan 23, 2022
a6c635d
chore: update utils package name in no-useless-empty-export.ts
JoshuaKGoldbergJan 23, 2022
b4e7ddf
Update packages/eslint-plugin/tests/rules/no-useless-empty-export.tes…
JoshuaKGoldbergFeb 2, 2022
225e5aa
Merge branch 'main' into no-useless-empty-export
Feb 2, 2022
2509b70
Merge branch 'main' into no-useless-empty-export
JoshuaKGoldbergFeb 23, 2022
e909d2a
docs: corrected docs file
JoshuaKGoldbergFeb 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletionspackages/eslint-plugin/README.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,6 +150,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int
| [`@typescript-eslint/no-unsafe-call`](./docs/rules/no-unsafe-call.md) | Disallows calling an any type value | :white_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/no-unsafe-member-access`](./docs/rules/no-unsafe-member-access.md) | Disallows member access on any typed variables | :white_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/no-unsafe-return`](./docs/rules/no-unsafe-return.md) | Disallows returning any from a function | :white_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/no-useless-empty-export`](./docs/rules/no-useless-empty-export.md) | Disallow empty exports that don't change anything in a module file | | :wrench: | |
| [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements | :white_check_mark: | | |
| [`@typescript-eslint/non-nullable-type-assertion-style`](./docs/rules/non-nullable-type-assertion-style.md) | Prefers a non-null assertion over explicit type cast when possible | | :wrench: | :thought_balloon: |
| [`@typescript-eslint/prefer-as-const`](./docs/rules/prefer-as-const.md) | Prefer usage of `as const` over literal type | :white_check_mark: | :wrench: | |
Expand Down
45 changes: 45 additions & 0 deletionspackages/eslint-plugin/docs/rules/no-useless-empty-export.md
View file
Open in desktop
Original file line numberDiff line numberDiff 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
1 change: 1 addition & 0 deletionspackages/eslint-plugin/src/configs/all.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,6 +114,7 @@ export = {
'@typescript-eslint/no-unused-vars': 'error',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'error',
'@typescript-eslint/no-useless-empty-export': 'error',
'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'error',
'@typescript-eslint/no-var-requires': 'error',
Expand Down
2 changes: 2 additions & 0 deletionspackages/eslint-plugin/src/rules/index.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -81,6 +81,7 @@ import noUnusedExpressions from './no-unused-expressions';
import noUnusedVars from './no-unused-vars';
import noUseBeforeDefine from './no-use-before-define';
import noUselessConstructor from './no-useless-constructor';
import noUselessEmptyExport from './no-useless-empty-export';
import noVarRequires from './no-var-requires';
import nonNullableTypeAssertionStyle from './non-nullable-type-assertion-style';
import objectCurlySpacing from './object-curly-spacing';
Expand DownExpand Up@@ -204,6 +205,7 @@ export default {
'no-unused-vars': noUnusedVars,
'no-use-before-define': noUseBeforeDefine,
'no-useless-constructor': noUselessConstructor,
'no-useless-empty-export': noUselessEmptyExport,
'no-var-requires': noVarRequires,
'non-nullable-type-assertion-style': nonNullableTypeAssertionStyle,
'object-curly-spacing': objectCurlySpacing,
Expand Down
79 changes: 79 additions & 0 deletionspackages/eslint-plugin/src/rules/no-useless-empty-export.ts
View file
Open in desktop
Original file line numberDiff line numberDiff 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 deletionspackages/eslint-plugin/tests/rules/no-useless-empty-export.test.ts
View file
Open in desktop
Original file line numberDiff line numberDiff 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('_');

`,
},
],
});

[8]ページ先頭

©2009-2025 Movatter.jp