diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js index ea2e8c88d3..87519dbc32 100644 --- a/lib/rules/jsx-sort-props.js +++ b/lib/rules/jsx-sort-props.js @@ -6,6 +6,7 @@ 'use strict'; const propName = require('jsx-ast-utils/propName'); +//const astUtils = require(".node_modules\eslint\lib\rules\utils\ast-utils.js"); const includes = require('array-includes'); const docsUrl = require('../util/docsUrl'); const jsxUtil = require('../util/jsx'); @@ -28,7 +29,7 @@ const messages = { listIsEmpty: 'A customized reserved first list must not be empty', listReservedPropsFirst: 'Reserved props must be listed before all other props', listCallbacksLast: 'Callbacks must be listed after all other props', - listShorthandFirst: 'Shorthand props must be listed before all other props', + listShorthandFirst: 'S horthand props must be listed before all other props', listShorthandLast: 'Shorthand props must be listed after all other props', listMultilineFirst: 'Multiline props must be listed before all other props', listMultilineLast: 'Multiline props must be listed after all other props', @@ -46,6 +47,12 @@ function isReservedPropName(name, list) { return list.indexOf(name) >= 0; } +function sorttoend(node) { + if (attributemap.get(node) && attributemap.get(node)[1]){ + return true + } +} + function contextCompare(a, b, options) { let aProp = propName(a); let bProp = propName(b); @@ -98,6 +105,17 @@ function contextCompare(a, b, options) { return 0; } + if (options.commentbetween) { + const asorttoend = sorttoend(a); + const bsorttoend = sorttoend(b); + if (asorttoend && !bsorttoend) { + return 1; + } + if (!asorttoend && bsorttoend) { + return -1; + } + } + const actualLocale = options.locale === 'auto' ? undefined : options.locale; if (options.ignoreCase) { @@ -120,24 +138,67 @@ function contextCompare(a, b, options) { * @param {Array} attributes * @return {Array>} */ -function getGroupsOfSortableAttributes(attributes) { + +const attributemap = new WeakMap() +// attributemap = [endrange, true||false if comment inbetween nodes exists, it needs to be sorted to end ] +function getGroupsOfSortableAttributes(attributes, context) { const sortableAttributeGroups = []; + const sourceCode = context.getSourceCode(); + let groupCount = 0; for (let i = 0; i < attributes.length; i++) { + const attribute = attributes[i]; + const nextattribute = attributes[i+1]; + const attributeline = attribute.loc.start.line; + const comment = sourceCode.getCommentsAfter(attribute); const lastAttr = attributes[i - 1]; + function addtoSortableAttributeGroups() {sortableAttributeGroups[groupCount - 1].push(attribute)}; // If we have no groups or if the last attribute was JSXSpreadAttribute // then we start a new group. Append attributes to the group until we // come across another JSXSpreadAttribute or exhaust the array. if ( !lastAttr || (lastAttr.type === 'JSXSpreadAttribute' - && attributes[i].type !== 'JSXSpreadAttribute') + && attribute.type !== 'JSXSpreadAttribute') ) { groupCount += 1; sortableAttributeGroups[groupCount - 1] = []; } - if (attributes[i].type !== 'JSXSpreadAttribute') { - sortableAttributeGroups[groupCount - 1].push(attributes[i]); + if (attribute.type !== 'JSXSpreadAttribute') { + if (comment.length > 0) { + const commentline = comment[0].loc.start.line + if (attributeline + 1 == commentline && comment.length == 1 && nextattribute) { + attributemap.set(attribute, [nextattribute.range[1], true]) + addtoSortableAttributeGroups() + i++ + continue + } + if (attributeline == commentline && comment.length == 1) { + if (comment[0].type == 'Block') { + attributemap.set(attribute, [nextattribute.range[1], true]) + addtoSortableAttributeGroups() + i++ + continue + } + attributemap.set(attribute, [comment[0].range[1], false]) + addtoSortableAttributeGroups() + } + if (comment.length > 1) { + if (attributeline + 1 == comment[1].loc.start.line && nextattribute) { + const commentnextattribute = sourceCode.getCommentsAfter(nextattribute); + attributemap.set(attribute, [nextattribute.range[1], true]) + if (commentnextattribute.length == 1 && nextattribute.loc.start.line == commentnextattribute[0].loc.start.line) { + attributemap.set(attribute, [commentnextattribute[0].range[1], true]) + } + addtoSortableAttributeGroups() + i++ + continue + } + } + } else { + attributemap.set(attribute, [attribute.range[1], false]) + addtoSortableAttributeGroups() + } } } return sortableAttributeGroups; @@ -155,6 +216,7 @@ const generateFixerFunction = (node, context, reservedList) => { const noSortAlphabetically = configuration.noSortAlphabetically || false; const reservedFirst = configuration.reservedFirst || false; const locale = configuration.locale || 'auto'; + const commentbetween = configuration.commentbetween || true; // Sort props according to the context. Only supports ignoreCase. // Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides), @@ -169,23 +231,24 @@ const generateFixerFunction = (node, context, reservedList) => { reservedFirst, reservedList, locale, + commentbetween, }; - const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes); + const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes, context); const sortedAttributeGroups = sortableAttributeGroups .slice(0) - .map((group) => group.slice(0).sort((a, b) => contextCompare(a, b, options))); + .map((group) => group.slice(0).sort((a, b) => contextCompare(a, b, options) )); return function fixFunction(fixer) { const fixers = []; let source = sourceCode.getText(); - // Replace each unsorted attribute with the sorted one. sortableAttributeGroups.forEach((sortableGroup, ii) => { sortableGroup.forEach((attr, jj) => { const sortedAttr = sortedAttributeGroups[ii][jj]; - const sortedAttrText = sourceCode.getText(sortedAttr); + const sortedAttrText = source.substring(sortedAttr.range[0], attributemap.get(sortedAttr)[0]) + const attrrangeEnd = attributemap.get(attr)[0] fixers.push({ - range: [attr.range[0], attr.range[1]], + range: [attr.range[0], attrrangeEnd], text: sortedAttrText, }); }); @@ -194,7 +257,7 @@ const generateFixerFunction = (node, context, reservedList) => { fixers.sort((a, b) => b.range[0] - a.range[0]); const rangeStart = fixers[fixers.length - 1].range[0]; - const rangeEnd = fixers[0].range[1]; + const rangeEnd = fixers[0].range[1]; fixers.forEach((fix) => { 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 errors.push(errorType); reportedNodeAttributes.set(nodeAttribute, errors); - + report(context, messages[errorType], errorType, { node: nodeAttribute.name, fix: generateFixerFunction(node, context, reservedList), + }); } diff --git a/tests/lib/rules/jsx-sort-props.js b/tests/lib/rules/jsx-sort-props.js index 3abcbb8233..f042ca9917 100644 --- a/tests/lib/rules/jsx-sort-props.js +++ b/tests/lib/rules/jsx-sort-props.js @@ -119,6 +119,7 @@ const multilineAndShorthandAndCallbackLastArgs = [ callbacksLast: true, }, ]; +//const commentbetween = [{ commentbetween: true }]; ruleTester.run('jsx-sort-props', rule, { valid: parsers.all([].concat( @@ -821,5 +822,231 @@ ruleTester.run('jsx-sort-props', rule, { }, ], }, + { + code: ` + + `, + output: ` + + `, + errors: [ + { + messageId: 'sortPropsByAlpha', + line: 6, + }, + { + messageId: 'sortPropsByAlpha', + line: 8, + }, + { + messageId: 'sortPropsByAlpha', + line: 9, + }, + { + messageId: 'sortPropsByAlpha', + line: 10, + }, + { + messageId: 'sortPropsByAlpha', + line: 11, + }, + ], + }, + { + code: ` + + `, + output: ` + + `, + errors: [ + { + messageId: 'sortPropsByAlpha', + line: 6, + }, + { + messageId: 'sortPropsByAlpha', + line: 7, + }, + { + messageId: 'sortPropsByAlpha', + line: 8, + }, + { + messageId: 'sortPropsByAlpha', + line: 9, + }, + { + messageId: 'sortPropsByAlpha', + line: 10, + }, + { + messageId: 'sortPropsByAlpha', + line: 11, + }, + ], + }, + { + code: ` + + `, + output: ` + + `, + errors: [ + { + messageId: 'sortPropsByAlpha', + line: 5, + }, + { + messageId: 'sortPropsByAlpha', + line: 7, + }, + { + messageId: 'sortPropsByAlpha', + line: 8, + }, + { + messageId: 'sortPropsByAlpha', + line: 10, + }, + { + messageId: 'sortPropsByAlpha', + line: 11, + }, + { + messageId: 'sortPropsByAlpha', + line: 12, + }, + ], + }, + { + code: ` + + `, + output: ` + + `, + errors: [ + { + messageId: 'sortPropsByAlpha', + line: 8, + }, + { + messageId: 'sortPropsByAlpha', + line: 10, + }, + { + messageId: 'sortPropsByAlpha', + line: 11, + }, + { + messageId: 'sortPropsByAlpha', + line: 12, + }, + ], + }, + { + code: ` + + `, + output: ` + + `, + errors: [ + { + messageId: 'sortPropsByAlpha', + line: 2, + }, + { + messageId: 'sortPropsByAlpha', + line: 2, + }, + ], + }, ]), });