Skip to content

Commit 922819f

Browse files
azyzz228ljharb
authored andcommitted
[New] prefer-default-export: add "target" option
Fixes #2600.
1 parent f4f305b commit 922819f

File tree

5 files changed

+337
-13
lines changed

5 files changed

+337
-13
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1717
- [`order`]: new `alphabetize.orderImportKind` option to sort imports with same path based on their kind (`type`, `typeof`) ([#2544], thanks [@stropho])
1818
- [`consistent-type-specifier-style`]: add rule ([#2473], thanks [@bradzacher])
1919
- Add [`no-empty-named-blocks`] rule ([#2568], thanks [@guilhermelimak])
20+
- [`prefer-default-export`]: add "target" option ([#2602], thanks [@azyzz228])
2021

2122
### Fixed
2223
- [`order`]: move nested imports closer to main import entry ([#2396], thanks [@pri1311])
@@ -1025,6 +1026,7 @@ for info on changes for earlier releases.
10251026
[`memo-parser`]: ./memo-parser/README.md
10261027

10271028
[#2605]: https://github.com/import-js/eslint-plugin-import/pull/2605
1029+
[#2602]: https://github.com/import-js/eslint-plugin-import/pull/2602
10281030
[#2598]: https://github.com/import-js/eslint-plugin-import/pull/2598
10291031
[#2589]: https://github.com/import-js/eslint-plugin-import/pull/2589
10301032
[#2588]: https://github.com/import-js/eslint-plugin-import/pull/2588

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a
8989
| [no-namespace](docs/rules/no-namespace.md) | Forbid namespace (a.k.a. "wildcard" `*`) imports. | | | | 🔧 | | |
9090
| [no-unassigned-import](docs/rules/no-unassigned-import.md) | Forbid unassigned imports | | | | | | |
9191
| [order](docs/rules/order.md) | Enforce a convention in module import order. | | | | 🔧 | | |
92-
| [prefer-default-export](docs/rules/prefer-default-export.md) | Prefer a default export if module exports a single name. | | | | | | |
92+
| [prefer-default-export](docs/rules/prefer-default-export.md) | Prefer a default export if module exports a single name or multiple names. | | | | | | |
9393

9494
<!-- end auto-generated rules list -->
9595

docs/rules/prefer-default-export.md

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,44 @@
22

33
<!-- end auto-generated rule header -->
44

5-
When there is only a single export from a module, prefer using default export over named export.
5+
In exporting files, this rule checks if there is default export or not.
66

77
## Rule Details
88

9+
##### rule schema:
10+
11+
```javascript
12+
"import/prefer-default-export": [
13+
( "off" | "warn" | "error" ),
14+
{ "target": "single" | "any" } // default is "single"
15+
]
16+
```
17+
18+
### Config Options
19+
20+
There are two options available: `single` and `any`. By default, if you do not specify the option, rule will assume it is `single`.
21+
22+
#### single
23+
24+
**Definition**: When there is only a single export from a module, prefer using default export over named export.
25+
26+
How to setup config file for this rule:
27+
28+
```javascript
29+
// you can manually specify it
30+
"rules": {
31+
"import/prefer-default-export": [
32+
( "off" | "warn" | "error" ),
33+
{ "target": "single" }
34+
]
35+
}
36+
37+
// config setup below will also work
38+
"rules": {
39+
"import/prefer-default-export": "off" | "warn" | "error"
40+
}
41+
```
42+
943
The following patterns are considered warnings:
1044

1145
```javascript
@@ -58,3 +92,95 @@ export { foo as default }
5892
// Any batch export will disable this rule. The remote module is not inspected.
5993
export * from './other-module'
6094
```
95+
96+
#### any
97+
98+
**Definition**: any exporting file must contain a default export.
99+
100+
How to setup config file for this rule:
101+
102+
```javascript
103+
// you have to manually specify it
104+
"rules": {
105+
"import/prefer-default-export": [
106+
( "off" | "warn" | "error" ),
107+
{ "target": "any" }
108+
]
109+
}
110+
```
111+
112+
113+
The following patterns are *not* considered warnings:
114+
115+
```javascript
116+
// good1.js
117+
118+
//has default export
119+
export default function bar() {};
120+
```
121+
122+
```javascript
123+
// good2.js
124+
125+
// has default export
126+
let foo;
127+
export { foo as default }
128+
```
129+
130+
```javascript
131+
// good3.js
132+
133+
//contains multiple exports AND default export
134+
export const a = 5;
135+
export function bar(){};
136+
let foo;
137+
export { foo as default }
138+
```
139+
140+
```javascript
141+
// good4.js
142+
143+
// does not contain any exports => file is not checked by the rule
144+
import * as foo from './foo';
145+
```
146+
147+
```javascript
148+
// export-star.js
149+
150+
// Any batch export will disable this rule. The remote module is not inspected.
151+
export * from './other-module'
152+
```
153+
154+
The following patterns are considered warnings:
155+
156+
```javascript
157+
// bad1.js
158+
159+
//has 2 named exports, but no default export
160+
export const foo = 'foo';
161+
export const bar = 'bar';
162+
```
163+
164+
```javascript
165+
// bad2.js
166+
167+
// does not have default export
168+
let foo, bar;
169+
export { foo, bar }
170+
```
171+
172+
```javascript
173+
// bad3.js
174+
175+
// does not have default export
176+
export { a, b } from "foo.js"
177+
```
178+
179+
```javascript
180+
// bad4.js
181+
182+
// does not have default export
183+
let item;
184+
export const foo = item;
185+
export { item };
186+
```

src/rules/prefer-default-export.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,28 @@
22

33
import docsUrl from '../docsUrl';
44

5+
const SINGLE_EXPORT_ERROR_MESSAGE = 'Prefer default export on a file with single export.';
6+
const ANY_EXPORT_ERROR_MESSAGE = 'Prefer default export to be present on every file that has export.';
7+
58
module.exports = {
69
meta: {
710
type: 'suggestion',
811
docs: {
912
category: 'Style guide',
10-
description: 'Prefer a default export if module exports a single name.',
13+
description: 'Prefer a default export if module exports a single name or multiple names.',
1114
url: docsUrl('prefer-default-export'),
1215
},
13-
schema: [],
16+
schema: [{
17+
type: 'object',
18+
properties:{
19+
target: {
20+
type: 'string',
21+
enum: ['single', 'any'],
22+
default: 'single',
23+
},
24+
},
25+
additionalProperties: false,
26+
}],
1427
},
1528

1629
create(context) {
@@ -19,7 +32,8 @@ module.exports = {
1932
let hasStarExport = false;
2033
let hasTypeExport = false;
2134
let namedExportNode = null;
22-
35+
// get options. by default we look into files with single export
36+
const { target = 'single' } = context.options[0] || {};
2337
function captureDeclaration(identifierOrPattern) {
2438
if (identifierOrPattern && identifierOrPattern.type === 'ObjectPattern') {
2539
// recursively capture
@@ -88,8 +102,13 @@ module.exports = {
88102
},
89103

90104
'Program:exit': function () {
91-
if (specifierExportCount === 1 && !hasDefaultExport && !hasStarExport && !hasTypeExport) {
92-
context.report(namedExportNode, 'Prefer default export.');
105+
if (hasDefaultExport || hasStarExport || hasTypeExport) {
106+
return;
107+
}
108+
if (target === 'single' && specifierExportCount === 1) {
109+
context.report(namedExportNode, SINGLE_EXPORT_ERROR_MESSAGE);
110+
} else if (target === 'any' && specifierExportCount > 0) {
111+
context.report(namedExportNode, ANY_EXPORT_ERROR_MESSAGE);
93112
}
94113
},
95114
};

0 commit comments

Comments
 (0)