Skip to content

Commit 51ed373

Browse files
committed
Issue#2366 sorted attributes respects comments
1 parent b7f388b commit 51ed373

File tree

2 files changed

+303
-12
lines changed

2 files changed

+303
-12
lines changed

lib/rules/jsx-sort-props.js

+76-12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
'use strict';
77

88
const propName = require('jsx-ast-utils/propName');
9+
//const astUtils = require(".node_modules\eslint\lib\rules\utils\ast-utils.js");
910
const includes = require('array-includes');
1011
const docsUrl = require('../util/docsUrl');
1112
const jsxUtil = require('../util/jsx');
@@ -28,7 +29,7 @@ const messages = {
2829
listIsEmpty: 'A customized reserved first list must not be empty',
2930
listReservedPropsFirst: 'Reserved props must be listed before all other props',
3031
listCallbacksLast: 'Callbacks must be listed after all other props',
31-
listShorthandFirst: 'Shorthand props must be listed before all other props',
32+
listShorthandFirst: 'S horthand props must be listed before all other props',
3233
listShorthandLast: 'Shorthand props must be listed after all other props',
3334
listMultilineFirst: 'Multiline props must be listed before all other props',
3435
listMultilineLast: 'Multiline props must be listed after all other props',
@@ -46,6 +47,12 @@ function isReservedPropName(name, list) {
4647
return list.indexOf(name) >= 0;
4748
}
4849

50+
function sorttoend(node) {
51+
if (attributemap.get(node) && attributemap.get(node)[1]){
52+
return true
53+
}
54+
}
55+
4956
function contextCompare(a, b, options) {
5057
let aProp = propName(a);
5158
let bProp = propName(b);
@@ -98,6 +105,17 @@ function contextCompare(a, b, options) {
98105
return 0;
99106
}
100107

108+
if (options.commentbetween) {
109+
const asorttoend = sorttoend(a);
110+
const bsorttoend = sorttoend(b);
111+
if (asorttoend && !bsorttoend) {
112+
return 1;
113+
}
114+
if (!asorttoend && bsorttoend) {
115+
return -1;
116+
}
117+
}
118+
101119
const actualLocale = options.locale === 'auto' ? undefined : options.locale;
102120

103121
if (options.ignoreCase) {
@@ -120,24 +138,67 @@ function contextCompare(a, b, options) {
120138
* @param {Array<JSXSpreadAttribute|JSXAttribute>} attributes
121139
* @return {Array<Array<JSXAttribute>>}
122140
*/
123-
function getGroupsOfSortableAttributes(attributes) {
141+
142+
const attributemap = new WeakMap()
143+
// attributemap = [endrange, true||false if comment inbetween nodes exists, it needs to be sorted to end ]
144+
function getGroupsOfSortableAttributes(attributes, context) {
124145
const sortableAttributeGroups = [];
146+
const sourceCode = context.getSourceCode();
147+
125148
let groupCount = 0;
126149
for (let i = 0; i < attributes.length; i++) {
150+
const attribute = attributes[i];
151+
const nextattribute = attributes[i+1];
152+
const attributeline = attribute.loc.start.line;
153+
const comment = sourceCode.getCommentsAfter(attribute);
127154
const lastAttr = attributes[i - 1];
155+
function addtoSortableAttributeGroups() {sortableAttributeGroups[groupCount - 1].push(attribute)};
128156
// If we have no groups or if the last attribute was JSXSpreadAttribute
129157
// then we start a new group. Append attributes to the group until we
130158
// come across another JSXSpreadAttribute or exhaust the array.
131159
if (
132160
!lastAttr
133161
|| (lastAttr.type === 'JSXSpreadAttribute'
134-
&& attributes[i].type !== 'JSXSpreadAttribute')
162+
&& attribute.type !== 'JSXSpreadAttribute')
135163
) {
136164
groupCount += 1;
137165
sortableAttributeGroups[groupCount - 1] = [];
138166
}
139-
if (attributes[i].type !== 'JSXSpreadAttribute') {
140-
sortableAttributeGroups[groupCount - 1].push(attributes[i]);
167+
if (attribute.type !== 'JSXSpreadAttribute') {
168+
if (comment.length > 0) {
169+
const commentline = comment[0].loc.start.line
170+
if (attributeline + 1 == commentline && comment.length == 1 && nextattribute) {
171+
attributemap.set(attribute, [nextattribute.range[1], true])
172+
addtoSortableAttributeGroups()
173+
i++
174+
continue
175+
}
176+
if (attributeline == commentline && comment.length == 1) {
177+
if (comment[0].type == 'Block') {
178+
attributemap.set(attribute, [nextattribute.range[1], true])
179+
addtoSortableAttributeGroups()
180+
i++
181+
continue
182+
}
183+
attributemap.set(attribute, [comment[0].range[1], false])
184+
addtoSortableAttributeGroups()
185+
}
186+
if (comment.length > 1) {
187+
if (attributeline + 1 == comment[1].loc.start.line && nextattribute) {
188+
const commentnextattribute = sourceCode.getCommentsAfter(nextattribute);
189+
attributemap.set(attribute, [nextattribute.range[1], true])
190+
if (commentnextattribute.length == 1 && nextattribute.loc.start.line == commentnextattribute[0].loc.start.line) {
191+
attributemap.set(attribute, [commentnextattribute[0].range[1], true])
192+
}
193+
addtoSortableAttributeGroups()
194+
i++
195+
continue
196+
}
197+
}
198+
} else {
199+
attributemap.set(attribute, [attribute.range[1], false])
200+
addtoSortableAttributeGroups()
201+
}
141202
}
142203
}
143204
return sortableAttributeGroups;
@@ -155,6 +216,7 @@ const generateFixerFunction = (node, context, reservedList) => {
155216
const noSortAlphabetically = configuration.noSortAlphabetically || false;
156217
const reservedFirst = configuration.reservedFirst || false;
157218
const locale = configuration.locale || 'auto';
219+
const commentbetween = configuration.commentbetween || true;
158220

159221
// Sort props according to the context. Only supports ignoreCase.
160222
// Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides),
@@ -169,23 +231,24 @@ const generateFixerFunction = (node, context, reservedList) => {
169231
reservedFirst,
170232
reservedList,
171233
locale,
234+
commentbetween,
172235
};
173-
const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes);
236+
const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes, context);
174237
const sortedAttributeGroups = sortableAttributeGroups
175238
.slice(0)
176-
.map((group) => group.slice(0).sort((a, b) => contextCompare(a, b, options)));
239+
.map((group) => group.slice(0).sort((a, b) => contextCompare(a, b, options) ));
177240

178241
return function fixFunction(fixer) {
179242
const fixers = [];
180243
let source = sourceCode.getText();
181244

182-
// Replace each unsorted attribute with the sorted one.
183245
sortableAttributeGroups.forEach((sortableGroup, ii) => {
184246
sortableGroup.forEach((attr, jj) => {
185247
const sortedAttr = sortedAttributeGroups[ii][jj];
186-
const sortedAttrText = sourceCode.getText(sortedAttr);
248+
const sortedAttrText = source.substring(sortedAttr.range[0], attributemap.get(sortedAttr)[0])
249+
const attrrangeEnd = attributemap.get(attr)[0]
187250
fixers.push({
188-
range: [attr.range[0], attr.range[1]],
251+
range: [attr.range[0], attrrangeEnd],
189252
text: sortedAttrText,
190253
});
191254
});
@@ -194,7 +257,7 @@ const generateFixerFunction = (node, context, reservedList) => {
194257
fixers.sort((a, b) => b.range[0] - a.range[0]);
195258

196259
const rangeStart = fixers[fixers.length - 1].range[0];
197-
const rangeEnd = fixers[0].range[1];
260+
const rangeEnd = fixers[0].range[1];
198261

199262
fixers.forEach((fix) => {
200263
source = `${source.substr(0, fix.range[0])}${fix.text}${source.substr(fix.range[1])}`;
@@ -262,10 +325,11 @@ function reportNodeAttribute(nodeAttribute, errorType, node, context, reservedLi
262325
errors.push(errorType);
263326

264327
reportedNodeAttributes.set(nodeAttribute, errors);
265-
328+
266329
report(context, messages[errorType], errorType, {
267330
node: nodeAttribute.name,
268331
fix: generateFixerFunction(node, context, reservedList),
332+
269333
});
270334
}
271335

0 commit comments

Comments
 (0)