-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
prefer-default-export.js
116 lines (102 loc) · 3.34 KB
/
prefer-default-export.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
108
109
110
111
112
113
114
115
116
'use strict';
import docsUrl from '../docsUrl';
const SINGLE_EXPORT_ERROR_MESSAGE = 'Prefer default export on a file with single export.';
const ANY_EXPORT_ERROR_MESSAGE = 'Prefer default export to be present on every file that has export.';
module.exports = {
meta: {
type: 'suggestion',
docs: {
category: 'Style guide',
description: 'Prefer a default export if module exports a single name or multiple names.',
url: docsUrl('prefer-default-export'),
},
schema: [{
type: 'object',
properties:{
target: {
type: 'string',
enum: ['single', 'any'],
default: 'single',
},
},
additionalProperties: false,
}],
},
create(context) {
let specifierExportCount = 0;
let hasDefaultExport = false;
let hasStarExport = false;
let hasTypeExport = false;
let namedExportNode = null;
// get options. by default we look into files with single export
const { target = 'single' } = context.options[0] || {};
function captureDeclaration(identifierOrPattern) {
if (identifierOrPattern && identifierOrPattern.type === 'ObjectPattern') {
// recursively capture
identifierOrPattern.properties
.forEach(function (property) {
captureDeclaration(property.value);
});
} else if (identifierOrPattern && identifierOrPattern.type === 'ArrayPattern') {
identifierOrPattern.elements
.forEach(captureDeclaration);
} else {
// assume it's a single standard identifier
specifierExportCount++;
}
}
return {
'ExportDefaultSpecifier': function () {
hasDefaultExport = true;
},
'ExportSpecifier': function (node) {
if ((node.exported.name || node.exported.value) === 'default') {
hasDefaultExport = true;
} else {
specifierExportCount++;
namedExportNode = node;
}
},
'ExportNamedDeclaration': function (node) {
// if there are specifiers, node.declaration should be null
if (!node.declaration) return;
const { type } = node.declaration;
if (
type === 'TSTypeAliasDeclaration' ||
type === 'TypeAlias' ||
type === 'TSInterfaceDeclaration' ||
type === 'InterfaceDeclaration'
) {
specifierExportCount++;
hasTypeExport = true;
return;
}
if (node.declaration.declarations) {
node.declaration.declarations.forEach(function (declaration) {
captureDeclaration(declaration.id);
});
} else {
// captures 'export function foo() {}' syntax
specifierExportCount++;
}
namedExportNode = node;
},
'ExportDefaultDeclaration': function () {
hasDefaultExport = true;
},
'ExportAllDeclaration': function () {
hasStarExport = true;
},
'Program:exit': function () {
if (hasDefaultExport || hasStarExport || hasTypeExport) {
return;
}
if (target === 'single' && specifierExportCount === 1) {
context.report(namedExportNode, SINGLE_EXPORT_ERROR_MESSAGE);
} else if (target === 'any' && specifierExportCount > 0) {
context.report(namedExportNode, ANY_EXPORT_ERROR_MESSAGE);
}
},
};
},
};