Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/eslint-plugin-import
Path: blob/main/src/rules/group-exports.js
829 views
1
import docsUrl from '../docsUrl';
2
import values from 'object.values';
3
import flat from 'array.prototype.flat';
4
5
const meta = {
6
type: 'suggestion',
7
docs: {
8
url: docsUrl('group-exports'),
9
},
10
};
11
/* eslint-disable max-len */
12
const errors = {
13
ExportNamedDeclaration: 'Multiple named export declarations; consolidate all named exports into a single export declaration',
14
AssignmentExpression: 'Multiple CommonJS exports; consolidate all exports into a single assignment to `module.exports`',
15
};
16
/* eslint-enable max-len */
17
18
/**
19
* Returns an array with names of the properties in the accessor chain for MemberExpression nodes
20
*
21
* Example:
22
*
23
* `module.exports = {}` => ['module', 'exports']
24
* `module.exports.property = true` => ['module', 'exports', 'property']
25
*
26
* @param {Node} node AST Node (MemberExpression)
27
* @return {Array} Array with the property names in the chain
28
* @private
29
*/
30
function accessorChain(node) {
31
const chain = [];
32
33
do {
34
chain.unshift(node.property.name);
35
36
if (node.object.type === 'Identifier') {
37
chain.unshift(node.object.name);
38
break;
39
}
40
41
node = node.object;
42
} while (node.type === 'MemberExpression');
43
44
return chain;
45
}
46
47
function create(context) {
48
const nodes = {
49
modules: {
50
set: new Set(),
51
sources: {},
52
},
53
types: {
54
set: new Set(),
55
sources: {},
56
},
57
commonjs: {
58
set: new Set(),
59
},
60
};
61
62
return {
63
ExportNamedDeclaration(node) {
64
const target = node.exportKind === 'type' ? nodes.types : nodes.modules;
65
if (!node.source) {
66
target.set.add(node);
67
} else if (Array.isArray(target.sources[node.source.value])) {
68
target.sources[node.source.value].push(node);
69
} else {
70
target.sources[node.source.value] = [node];
71
}
72
},
73
74
AssignmentExpression(node) {
75
if (node.left.type !== 'MemberExpression') {
76
return;
77
}
78
79
const chain = accessorChain(node.left);
80
81
// Assignments to module.exports
82
// Deeper assignments are ignored since they just modify what's already being exported
83
// (ie. module.exports.exported.prop = true is ignored)
84
if (chain[0] === 'module' && chain[1] === 'exports' && chain.length <= 3) {
85
nodes.commonjs.set.add(node);
86
return;
87
}
88
89
// Assignments to exports (exports.* = *)
90
if (chain[0] === 'exports' && chain.length === 2) {
91
nodes.commonjs.set.add(node);
92
return;
93
}
94
},
95
96
'Program:exit': function onExit() {
97
// Report multiple `export` declarations (ES2015 modules)
98
if (nodes.modules.set.size > 1) {
99
nodes.modules.set.forEach(node => {
100
context.report({
101
node,
102
message: errors[node.type],
103
});
104
});
105
}
106
107
// Report multiple `aggregated exports` from the same module (ES2015 modules)
108
flat(values(nodes.modules.sources)
109
.filter(nodesWithSource => Array.isArray(nodesWithSource) && nodesWithSource.length > 1))
110
.forEach((node) => {
111
context.report({
112
node,
113
message: errors[node.type],
114
});
115
});
116
117
// Report multiple `export type` declarations (FLOW ES2015 modules)
118
if (nodes.types.set.size > 1) {
119
nodes.types.set.forEach(node => {
120
context.report({
121
node,
122
message: errors[node.type],
123
});
124
});
125
}
126
127
// Report multiple `aggregated type exports` from the same module (FLOW ES2015 modules)
128
flat(values(nodes.types.sources)
129
.filter(nodesWithSource => Array.isArray(nodesWithSource) && nodesWithSource.length > 1))
130
.forEach((node) => {
131
context.report({
132
node,
133
message: errors[node.type],
134
});
135
});
136
137
// Report multiple `module.exports` assignments (CommonJS)
138
if (nodes.commonjs.set.size > 1) {
139
nodes.commonjs.set.forEach(node => {
140
context.report({
141
node,
142
message: errors[node.type],
143
});
144
});
145
}
146
},
147
};
148
}
149
150
module.exports = {
151
meta,
152
create,
153
};
154
155