Skip to content

Commit e464955

Browse files
committed
[New] add support for sorting exports.{…} assignments
1 parent 167b030 commit e464955

File tree

1 file changed

+98
-38
lines changed

1 file changed

+98
-38
lines changed

Diff for: src/rules/order.js

+98-38
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ import importType from '../core/importType';
88
import isStaticRequire from '../core/staticRequire';
99
import docsUrl from '../docsUrl';
1010

11+
const categories = {
12+
named: 'named',
13+
import: 'import',
14+
exports: 'exports',
15+
};
16+
1117
const defaultGroups = ['builtin', 'external', 'parent', 'sibling', 'index'];
1218

1319
// REPORTING AND FIXING
@@ -169,16 +175,38 @@ function isPlainImportEquals(node) {
169175
return node.type === 'TSImportEqualsDeclaration' && node.moduleReference.expression;
170176
}
171177

172-
function isModuleExports(node) {
173-
return node.parent.parent.type === 'Program'
174-
&& (
175-
node.left.type === 'Identifier'
176-
&& node.left.name === 'exports')
177-
|| node.left.type === 'MemberExpression'
178-
&& node.left.object.type === 'Identifier'
179-
&& node.left.object.name === 'module'
180-
&& node.left.property.type === 'Identifier'
181-
&& node.left.property.name === 'exports';
178+
function isCJSExports(context, node) {
179+
if (node.type === 'MemberExpression'
180+
&& node.object.type === 'Identifier'
181+
&& node.property.type === 'Identifier'
182+
&& node.object.name === 'module'
183+
&& node.property.name === 'exports') {
184+
return context.sourceCode.getScope(node).variables.findIndex((variable) => variable.name === 'module') === -1;
185+
} else if (node.type === 'Identifier'
186+
&& node.name === 'exports') {
187+
return context.sourceCode.getScope(node).variables.findIndex((variable) => variable.name === 'exports') === -1;
188+
}
189+
}
190+
191+
function getNamedCJSExports(context, node) {
192+
if (node.type !== 'MemberExpression') {
193+
return;
194+
}
195+
const result = [];
196+
let root = node;
197+
while (root.type === 'MemberExpression') {
198+
if (root.property.type !== 'Identifier') {
199+
return;
200+
}
201+
result.unshift(root.property.name);
202+
root = root.object;
203+
}
204+
if (isCJSExports(context, root)) {
205+
return result;
206+
}
207+
if (isCJSExports(context, root.parent)) {
208+
return result.slice(1);
209+
}
182210
}
183211

184212
function canCrossNodeWhileReorder(node) {
@@ -216,13 +244,15 @@ function makeImportDescription(node) {
216244
return 'import';
217245
}
218246

219-
function fixOutOfOrder(context, firstNode, secondNode, order, named) {
247+
function fixOutOfOrder(context, firstNode, secondNode, order, category) {
248+
const isNamed = category === categories.named;
249+
const isExports = category === categories.exports;
220250
const sourceCode = context.getSourceCode();
221251

222252
const {
223253
firstRoot,
224254
secondRoot,
225-
} = named ? {
255+
} = isNamed ? {
226256
firstRoot: firstNode.node,
227257
secondRoot: secondNode.node,
228258
} : {
@@ -257,7 +287,7 @@ function fixOutOfOrder(context, firstNode, secondNode, order, named) {
257287
return text.slice(0, i + 1);
258288
}
259289

260-
if (named) {
290+
if (isNamed) {
261291
const firstCode = sourceCode.text.substring(firstRootStart, firstRootEnd);
262292
const secondCode = sourceCode.text.substring(secondRootStart, secondRootEnd);
263293

@@ -285,7 +315,7 @@ function fixOutOfOrder(context, firstNode, secondNode, order, named) {
285315
});
286316
}
287317
} else {
288-
const canFix = canReorderItems(firstRoot, secondRoot);
318+
const canFix = isExports || canReorderItems(firstRoot, secondRoot);
289319
let newCode = sourceCode.text.substring(secondRootStart, secondRootEnd);
290320

291321
if (newCode[newCode.length - 1] !== '\n') {
@@ -314,16 +344,16 @@ function fixOutOfOrder(context, firstNode, secondNode, order, named) {
314344
}
315345
}
316346

317-
function reportOutOfOrder(context, imported, outOfOrder, order, named) {
347+
function reportOutOfOrder(context, imported, outOfOrder, order, category) {
318348
outOfOrder.forEach(function (imp) {
319349
const found = imported.find(function hasHigherRank(importedItem) {
320350
return importedItem.rank > imp.rank;
321351
});
322-
fixOutOfOrder(context, found, imp, order, named);
352+
fixOutOfOrder(context, found, imp, order, category);
323353
});
324354
}
325355

326-
function makeOutOfOrderReport(context, imported, named) {
356+
function makeOutOfOrderReport(context, imported, category) {
327357
const outOfOrder = findOutOfOrder(imported);
328358
if (!outOfOrder.length) {
329359
return;
@@ -333,10 +363,10 @@ function makeOutOfOrderReport(context, imported, named) {
333363
const reversedImported = reverse(imported);
334364
const reversedOrder = findOutOfOrder(reversedImported);
335365
if (reversedOrder.length < outOfOrder.length) {
336-
reportOutOfOrder(context, reversedImported, reversedOrder, 'after', named);
366+
reportOutOfOrder(context, reversedImported, reversedOrder, 'after', category);
337367
return;
338368
}
339-
reportOutOfOrder(context, imported, outOfOrder, 'before', named);
369+
reportOutOfOrder(context, imported, outOfOrder, 'before', category);
340370
}
341371

342372
const compareString = (a, b) => {
@@ -810,6 +840,7 @@ module.exports = {
810840
};
811841
}
812842
const importMap = new Map();
843+
const exportMap = new Map();
813844

814845
function getBlockImports(node) {
815846
if (!importMap.has(node)) {
@@ -818,6 +849,13 @@ module.exports = {
818849
return importMap.get(node);
819850
}
820851

852+
function getBlockExports(node) {
853+
if (!exportMap.has(node)) {
854+
exportMap.set(node, []);
855+
}
856+
return exportMap.get(node);
857+
}
858+
821859
function makeNamedOrderReport(context, namedImports) {
822860
if (namedImports.length > 1) {
823861
const imports = namedImports.map(
@@ -837,7 +875,7 @@ module.exports = {
837875
mutateRanksToAlphabetize(imports, alphabetize);
838876
}
839877

840-
makeOutOfOrderReport(context, imports, true);
878+
makeOutOfOrderReport(context, imports, categories.named);
841879
}
842880
}
843881

@@ -964,25 +1002,39 @@ module.exports = {
9641002
} : {},
9651003
...named.cjsExports && {
9661004
AssignmentExpression: function orderModuleExportNames(node) {
967-
if (isModuleExports(node)) {
968-
if (node.right.type === 'ObjectExpression') {
969-
for (let i = 0; i < node.right.properties.length; i++) {
970-
if (node.right.properties[i].key.type !== 'Identifier' || node.right.properties[i].value.type !== 'Identifier') {
971-
return;
1005+
if (node.parent.type === 'ExpressionStatement') {
1006+
if (isCJSExports(context, node.left)) {
1007+
if (node.right.type === 'ObjectExpression') {
1008+
for (let i = 0; i < node.right.properties.length; i++) {
1009+
if (node.right.properties[i].key.type !== 'Identifier' || node.right.properties[i].value.type !== 'Identifier') {
1010+
return;
1011+
}
9721012
}
973-
}
9741013

975-
makeNamedOrderReport(
976-
context,
977-
node.right.properties.map(
978-
(prop) => ({
979-
node: prop,
980-
value: prop.key.name,
981-
type: 'export',
982-
...prop.key.range[0] !== prop.value.range[0] && {
983-
alias: prop.value.name,
984-
},
985-
})));
1014+
makeNamedOrderReport(
1015+
context,
1016+
node.right.properties.map(
1017+
(prop) => ({
1018+
node: prop,
1019+
value: prop.key.name,
1020+
type: 'export',
1021+
...prop.key.range[0] !== prop.value.range[0] && {
1022+
alias: prop.value.name,
1023+
},
1024+
})));
1025+
}
1026+
} else {
1027+
const nameParts = getNamedCJSExports(context, node.left);
1028+
if (nameParts && nameParts.length > 0) {
1029+
const name = nameParts.join('.');
1030+
getBlockExports(node.parent.parent).push({
1031+
node,
1032+
value: name,
1033+
displayName: name,
1034+
type: 'export',
1035+
rank: 0,
1036+
});
1037+
}
9861038
}
9871039
}
9881040
},
@@ -997,10 +1049,18 @@ module.exports = {
9971049
mutateRanksToAlphabetize(imported, alphabetize);
9981050
}
9991051

1000-
makeOutOfOrderReport(context, imported, false);
1052+
makeOutOfOrderReport(context, imported, categories.import);
1053+
});
1054+
1055+
exportMap.forEach((exported) => {
1056+
if (alphabetize.order !== 'ignore') {
1057+
mutateRanksToAlphabetize(exported, alphabetize);
1058+
makeOutOfOrderReport(context, exported, categories.exports);
1059+
}
10011060
});
10021061

10031062
importMap.clear();
1063+
exportMap.clear();
10041064
},
10051065
};
10061066
},

0 commit comments

Comments
 (0)