diff --git a/lib/rules/jsx-curly-spacing.js b/lib/rules/jsx-curly-spacing.js index 81f11c81b2..cd58f0080a 100644 --- a/lib/rules/jsx-curly-spacing.js +++ b/lib/rules/jsx-curly-spacing.js @@ -11,6 +11,7 @@ 'use strict'; const has = require('has'); +const commentsUtil = require('../util/comments'); const docsUrl = require('../util/docsUrl'); // ------------------------------------------------------------------------------ @@ -228,16 +229,7 @@ module.exports = { message: `There should be no space after '${token.value}'`, fix: function(fixer) { const nextToken = sourceCode.getTokenAfter(token); - let nextComment; - - // ESLint >=4.x - if (sourceCode.getCommentsAfter) { - nextComment = sourceCode.getCommentsAfter(token); - // ESLint 3.x - } else { - const potentialComment = sourceCode.getTokenAfter(token, {includeComments: true}); - nextComment = nextToken === potentialComment ? [] : [potentialComment]; - } + const nextComment = commentsUtil.getCommentsAfter(token, sourceCode); // Take comments into consideration to narrow the fix range to what is actually affected. (See #1414) if (nextComment.length > 0) { @@ -262,16 +254,7 @@ module.exports = { message: `There should be no space before '${token.value}'`, fix: function(fixer) { const previousToken = sourceCode.getTokenBefore(token); - let previousComment; - - // ESLint >=4.x - if (sourceCode.getCommentsBefore) { - previousComment = sourceCode.getCommentsBefore(token); - // ESLint 3.x - } else { - const potentialComment = sourceCode.getTokenBefore(token, {includeComments: true}); - previousComment = previousToken === potentialComment ? [] : [potentialComment]; - } + const previousComment = commentsUtil.getCommentsBefore(token, sourceCode); // Take comments into consideration to narrow the fix range to what is actually affected. (See #1414) if (previousComment.length > 0) { diff --git a/lib/rules/sort-prop-types.js b/lib/rules/sort-prop-types.js index da94a641db..cf01168aab 100644 --- a/lib/rules/sort-prop-types.js +++ b/lib/rules/sort-prop-types.js @@ -5,6 +5,7 @@ const variableUtil = require('../util/variable'); const propsUtil = require('../util/props'); +const commentsUtil = require('../util/comments'); const docsUrl = require('../util/docsUrl'); // ------------------------------------------------------------------------------ @@ -55,6 +56,7 @@ module.exports = { const noSortAlphabetically = configuration.noSortAlphabetically || false; const sortShapeProp = configuration.sortShapeProp || false; const propWrapperFunctions = new Set(context.settings.propWrapperFunctions || []); + const commentsAttachment = context.settings.comments || 'above'; function getKey(node) { return sourceCode.getText(node.key || node.argument); @@ -117,6 +119,90 @@ module.exports = { return 0; } + function getRelatedComments(node, nextNode) { + // check for an end of line comment + const nextNodeComments = nextNode + ? commentsUtil.getCommentsBefore(nextNode, sourceCode) + : commentsUtil.getCommentsAfter(node, sourceCode); + if (nextNodeComments.length === 1) { + const comment = nextNodeComments[0]; + if (comment.loc.start.line === comment.loc.end.line && comment.loc.end.line === node.loc.end.line) { + return [nextNodeComments, true]; + } + } + + if (commentsAttachment === 'above') { + return [commentsUtil.getCommentsBefore(node, sourceCode), false]; + } + + return [nextNodeComments, false]; + } + + function replaceNodeWithText(source, originalNode, sortedNodeText) { + return `${source.slice(0, originalNode.range[0])}${sortedNodeText}${source.slice(originalNode.range[1])}`; + } + + function sortNodeWithComments(source, originalAttr, originalComments, sortedAttrText, sortedComments) { + if (sortedComments.length && originalComments.length) { + const swapComments = () => { + const sortedCommentsText = sourceCode.getText().slice( + sortedComments[0].range[0], + sortedComments[sortedComments.length - 1].range[1] + ); + return `${source.slice(0, originalComments[0].range[0])}${sortedCommentsText}${source.slice(originalComments[originalComments.length - 1].range[1])}`; + }; + if (originalAttr.range[1] < originalComments[0].range[0]) { + source = swapComments(); + source = replaceNodeWithText(source, originalAttr, sortedAttrText); + } else { + source = replaceNodeWithText(source, originalAttr, sortedAttrText); + source = swapComments(); + } + return source; + } + + if (sortedComments.length) { + const sortedCommentsText = sourceCode.getText().slice( + sortedComments[0].range[0], + sortedComments[sortedComments.length - 1].range[1] + ); + + const indent = Array(sortedComments[0].loc.start.column + 1).join(' '); + if (commentsAttachment === 'above') { + source = replaceNodeWithText(source, originalAttr, sortedAttrText); + source = `${source.slice(0, originalAttr.range[0])}${sortedCommentsText}\n${indent}${source.slice(originalAttr.range[0])}`; + } else { + const nextToken = sourceCode.getTokenAfter(originalAttr); + const targetIndex = nextToken.value === ',' ? nextToken.range[1] : originalAttr.range[1]; + source = `${source.slice(0, targetIndex)}\n${indent}${sortedCommentsText}${source.slice(targetIndex)}`; + source = replaceNodeWithText(source, originalAttr, sortedAttrText); + } + return source; + } + + if (originalComments.length) { + const removeComments = () => { + const startLoc = sourceCode.getLocFromIndex(originalComments[0].range[0]); + const lineStart = sourceCode.getIndexFromLoc({line: startLoc.line, column: 0}); + const endLoc = sourceCode.getLocFromIndex(originalComments[originalComments.length - 1].range[1]); + const lineEnd = sourceCode.getIndexFromLoc({ + line: endLoc.line, + column: sourceCode.lines[endLoc.line - 1].length - 1 + }); + return `${source.slice(0, lineStart)}${source.slice(lineEnd + 2)}`; + }; + if (originalAttr.range[1] < originalComments[0].range[0]) { + source = removeComments(); + source = replaceNodeWithText(source, originalAttr, sortedAttrText); + } else { + source = replaceNodeWithText(source, originalAttr, sortedAttrText); + source = removeComments(); + } + return source; + } + + return null; + } /** * Checks if propTypes declarations are sorted @@ -148,7 +234,16 @@ module.exports = { for (let i = nodes.length - 1; i >= 0; i--) { const sortedAttr = sortedAttributes[i]; const attr = nodes[i]; + if (sortedAttr === attr) { + continue; + } + + const [sortedComments] = getRelatedComments(sortedAttr, + allNodes[allNodes.indexOf(sortedAttr) + 1]); + const [attrComments] = getRelatedComments(attr, nodes[i + 1]); + let sortedAttrText = sourceCode.getText(sortedAttr); + if (sortShapeProp && isShapeProp(sortedAttr.value)) { const shape = getShapeProperties(sortedAttr.value); if (shape) { @@ -159,7 +254,9 @@ module.exports = { sortedAttrText = attrSource.slice(sortedAttr.range[0], sortedAttr.range[1]); } } - source = `${source.slice(0, attr.range[0])}${sortedAttrText}${source.slice(attr.range[1])}`; + + const newSource = sortNodeWithComments(source, attr, attrComments, sortedAttrText, sortedComments); + source = newSource || replaceNodeWithText(source, attr, sortedAttrText); } }); return source; @@ -167,8 +264,14 @@ module.exports = { const source = sortInSource(declarations, context.getSourceCode().getText()); - const rangeStart = declarations[0].range[0]; - const rangeEnd = declarations[declarations.length - 1].range[1]; + const [startComments, isSameLineStart] = getRelatedComments(declarations[0], declarations[1]); + const [endComments, isSameLineEnd] = getRelatedComments(declarations[declarations.length - 1], null); + const rangeStart = (commentsAttachment === 'above' && startComments.length && !isSameLineStart) + ? startComments[0].range[0] + : declarations[0].range[0]; + const rangeEnd = (commentsAttachment === 'below' && endComments.length || isSameLineEnd) + ? endComments[endComments.length - 1].range[1] + : declarations[declarations.length - 1].range[1]; return fixer.replaceTextRange([rangeStart, rangeEnd], source.slice(rangeStart, rangeEnd)); } diff --git a/lib/util/comments.js b/lib/util/comments.js new file mode 100644 index 0000000000..d01513548f --- /dev/null +++ b/lib/util/comments.js @@ -0,0 +1,76 @@ +/** + * @fileoverview Utility functions for comments handling. + */ +'use strict'; + +/** + * Backports sourceCode.getCommentsBefore() for ESLint 3 + * + * @param {Object} nodeOrToken Node or token to get comments for. + * @param {Object} sourceCode The SourceCode object. + * @returns {Array} Array of comment tokens. + */ +function getCommentsBefore(nodeOrToken, sourceCode) { + const token = sourceCode.getFirstToken(nodeOrToken, {includeComments: true}); + let previousComments = []; + + // ESLint >=4.x + if (sourceCode.getCommentsBefore) { + previousComments = sourceCode.getCommentsBefore(token); + // ESLint 3.x + } else { + let currentToken = token; + do { + const previousToken = sourceCode.getTokenBefore(currentToken); + const potentialComment = sourceCode.getTokenBefore(currentToken, {includeComments: true}); + + if (previousToken !== potentialComment) { + previousComments.push(potentialComment); + currentToken = potentialComment; + } else { + currentToken = null; + } + } while (currentToken); + previousComments = previousComments.reverse(); + } + + return previousComments; +} + +/** + * Backports sourceCode.getCommentsAfter() for ESLint 3 + * + * @param {Object} nodeOrToken Node or token to get comments for. + * @param {Object} sourceCode The SourceCode object. + * @returns {Array} Array of comment tokens. + */ +function getCommentsAfter(nodeOrToken, sourceCode) { + const token = sourceCode.getLastToken(nodeOrToken, {includeComments: true}); + let nextComments = []; + + // ESLint >=4.x + if (sourceCode.getCommentsAfter) { + nextComments = sourceCode.getCommentsAfter(token); + // ESLint 3.x + } else { + let currentToken = token; + do { + const nextToken = sourceCode.getTokenAfter(currentToken); + const potentialComment = sourceCode.getTokenAfter(currentToken, {includeComments: true}); + + if (nextToken !== potentialComment) { + nextComments.push(potentialComment); + currentToken = potentialComment; + } else { + currentToken = null; + } + } while (currentToken); + } + + return nextComments; +} + +module.exports = { + getCommentsBefore: getCommentsBefore, + getCommentsAfter: getCommentsAfter +}; diff --git a/tests/lib/rules/sort-prop-types.js b/tests/lib/rules/sort-prop-types.js index e83ccacd34..0f35e33d40 100644 --- a/tests/lib/rules/sort-prop-types.js +++ b/tests/lib/rules/sort-prop-types.js @@ -1533,5 +1533,1330 @@ ruleTester.run('sort-prop-types', rule, { ' }', '});' ].join('\n') + }, { + code: ` + class Component extends React.Component { + static propTypes = { + /** z */ + z: PropTypes.any, + /** a */ + a: PropTypes.string + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + /** a */ + a: PropTypes.string, + /** z */ + z: PropTypes.any + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + /** z */ + z: PropTypes.any, + /** y */ + y: PropTypes.array, + /** b */ + b: PropTypes.func, + /** a */ + a: PropTypes.string + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 9, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 11, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + /** a */ + a: PropTypes.string, + /** b */ + b: PropTypes.func, + /** y */ + y: PropTypes.array, + /** z */ + z: PropTypes.any + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, + /** z */ + y: PropTypes.array, + /** y */ + b: PropTypes.func, + /** b */ + a: PropTypes.string + /** a */ + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + settings: {comments: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 6, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 8, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 10, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, + /** a */ + b: PropTypes.func, + /** b */ + y: PropTypes.array, + /** y */ + z: PropTypes.any + /** z */ + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, // z + a: PropTypes.string // a + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 5, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, // a + z: PropTypes.any // z + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, // z + y: PropTypes.array, // y + b: PropTypes.func, // b + a: PropTypes.string // a + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 5, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 6, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, // a + b: PropTypes.func, // b + y: PropTypes.array, // y + z: PropTypes.any // z + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, // z + y: PropTypes.array, // y + b: PropTypes.func, // b + a: PropTypes.string // a + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + settings: {commens: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 5, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 6, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, // a + b: PropTypes.func, // b + y: PropTypes.array, // y + z: PropTypes.any // z + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + /** z */ + z: PropTypes.any, + y: PropTypes.array, + /** b */ + b: PropTypes.func, + a: PropTypes.string + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 6, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 8, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 9, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, + /** b */ + b: PropTypes.func, + y: PropTypes.array, + /** z */ + z: PropTypes.any + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, + /** y */ + y: PropTypes.array, + b: PropTypes.func, + /** a */ + a: PropTypes.string + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 6, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 9, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + /** a */ + a: PropTypes.string, + b: PropTypes.func, + /** y */ + y: PropTypes.array, + z: PropTypes.any + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, + /** z */ + y: PropTypes.array, + b: PropTypes.func, + /** b */ + a: PropTypes.string + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + settings: {comments: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 6, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 9, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, + b: PropTypes.func, + /** b */ + y: PropTypes.array, + z: PropTypes.any + /** z */ + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, + y: PropTypes.array, + /** y */ + b: PropTypes.func, + a: PropTypes.string + /** a */ + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + settings: {comments: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 5, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 8, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, + /** a */ + b: PropTypes.func, + y: PropTypes.array, + /** y */ + z: PropTypes.any + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + /** + * z + */ + z: PropTypes.any, + /** + * y + */ + y: PropTypes.array, + /** + * b + */ + b: PropTypes.func, + /** + * a + */ + a: PropTypes.string + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 11, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 15, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 19, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + /** + * a + */ + a: PropTypes.string, + /** + * b + */ + b: PropTypes.func, + /** + * y + */ + y: PropTypes.array, + /** + * z + */ + z: PropTypes.any + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, + /** + * z + */ + y: PropTypes.array, + /** + * y + */ + b: PropTypes.func, + /** + * b + */ + a: PropTypes.string + /** + * a + */ + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + settings: {comments: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 8, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 12, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 16, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, + /** + * a + */ + b: PropTypes.func, + /** + * b + */ + y: PropTypes.array, + /** + * y + */ + z: PropTypes.any + /** + * z + */ + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + /* z1 */ + /* z2 */ + z: PropTypes.any, + /* y1 */ + /* y2 */ + y: PropTypes.array, + /* b1 */ + /* b2 */ + b: PropTypes.func, + /* a1 */ + /* a2 */ + a: PropTypes.string + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 9, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 12, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 15, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + /* a1 */ + /* a2 */ + a: PropTypes.string, + /* b1 */ + /* b2 */ + b: PropTypes.func, + /* y1 */ + /* y2 */ + y: PropTypes.array, + /* z1 */ + /* z2 */ + z: PropTypes.any + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, + /* z1 */ + /* z2 */ + y: PropTypes.array, + /* y1 */ + /* y2 */ + b: PropTypes.func, + /* b1 */ + /* b2 */ + a: PropTypes.string + /* a1 */ + /* a2 */ + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + settings: {comments: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 10, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 13, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, + /* a1 */ + /* a2 */ + b: PropTypes.func, + /* b1 */ + /* b2 */ + y: PropTypes.array, + /* y1 */ + /* y2 */ + z: PropTypes.any + /* z1 */ + /* z2 */ + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + // z1 + // z2 + z: PropTypes.any, + // y1 + // y2 + y: PropTypes.array, + // b1 + // b2 + b: PropTypes.func, + // a1 + // a2 + a: PropTypes.string + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 9, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 12, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 15, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + // a1 + // a2 + a: PropTypes.string, + // b1 + // b2 + b: PropTypes.func, + // y1 + // y2 + y: PropTypes.array, + // z1 + // z2 + z: PropTypes.any + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + z: PropTypes.any, + // z1 + // z2 + y: PropTypes.array, + // y1 + // y2 + b: PropTypes.func, + // b1 + // b2 + a: PropTypes.string + // a1 + // a2 + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + settings: {comments: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 10, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 13, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + a: PropTypes.string, + // a1 + // a2 + b: PropTypes.func, + // b1 + // b2 + y: PropTypes.array, + // y1 + // y2 + z: PropTypes.any + // z1 + // z2 + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + x: PropTypes.any, + y: PropTypes.any, + z: PropTypes.shape({ + /* c */ + c: PropTypes.any, + /* a */ + a: PropTypes.array, + /* b */ + b: PropTypes.bool + }), + }; + `, + options: [{ + sortShapeProp: true + }], + errors: [{ + message: ERROR_MESSAGE, + line: 14, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 16, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + x: PropTypes.any, + y: PropTypes.any, + z: PropTypes.shape({ + /* a */ + a: PropTypes.array, + /* b */ + b: PropTypes.bool, + /* c */ + c: PropTypes.any + }), + }; + ` + }, { + code: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + x: PropTypes.any, + y: PropTypes.any, + z: PropTypes.shape({ + c: PropTypes.any, + /* c */ + a: PropTypes.array, + /* a */ + b: PropTypes.bool + /* b */ + }), + }; + `, + options: [{ + sortShapeProp: true + }], + settings: {comments: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 13, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 15, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + x: PropTypes.any, + y: PropTypes.any, + z: PropTypes.shape({ + a: PropTypes.array, + /* a */ + b: PropTypes.bool, + /* b */ + c: PropTypes.any + /* c */ + }), + }; + ` + }, { + code: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + /* z */ + z: PropTypes.any, + /* y */ + y: PropTypes.any, + /* a */ + a: PropTypes.shape({ + /* c */ + c: PropTypes.any, + /* C */ + C: PropTypes.string, + /* a */ + a: PropTypes.any, + /* b */ + b: PropTypes.bool + }) + }; + `, + options: [{ + sortShapeProp: true + }], + errors: [{ + message: ERROR_MESSAGE, + line: 11, + column: 9, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 13, + column: 9, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 17, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 19, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 21, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + /* a */ + a: PropTypes.shape({ + /* C */ + C: PropTypes.string, + /* a */ + a: PropTypes.any, + /* b */ + b: PropTypes.bool, + /* c */ + c: PropTypes.any + }), + /* y */ + y: PropTypes.any, + /* z */ + z: PropTypes.any + }; + ` + }, { + code: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + z: PropTypes.any, + /* z */ + y: PropTypes.any, + /* y */ + a: PropTypes.shape({ + c: PropTypes.any, + /* c */ + C: PropTypes.string, + /* C */ + a: PropTypes.any, + /* a */ + b: PropTypes.bool + /* b */ + }) + /* a */ + }; + `, + options: [{ + sortShapeProp: true + }], + settings: {comments: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 10, + column: 9, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 12, + column: 9, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 15, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 17, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 19, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + a: PropTypes.shape({ + C: PropTypes.string, + /* C */ + a: PropTypes.any, + /* a */ + b: PropTypes.bool, + /* b */ + c: PropTypes.any + /* c */ + }), + /* a */ + y: PropTypes.any, + /* y */ + z: PropTypes.any + /* z */ + }; + ` + }, { + code: ` + export default class ClassWithSpreadInPropTypes extends BaseClass { + static propTypes = { + /* b */ + b: PropTypes.string, + /* a */ + ...a.propTypes, + /* d */ + d: PropTypes.string, + /* c */ + c: PropTypes.string + } + } + `, + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 11, + column: 11, + type: 'Property' + }], + output: ` + export default class ClassWithSpreadInPropTypes extends BaseClass { + static propTypes = { + /* b */ + b: PropTypes.string, + /* a */ + ...a.propTypes, + /* c */ + c: PropTypes.string, + /* d */ + d: PropTypes.string + } + } + ` + }, { + code: ` + export default class ClassWithSpreadInPropTypes extends BaseClass { + static propTypes = { + b: PropTypes.string, + /* b */ + ...a.propTypes, + /* a */ + d: PropTypes.string, + /* d */ + c: PropTypes.string + /* c */ + } + } + `, + parser: 'babel-eslint', + settings: {comments: 'below'}, + errors: [{ + message: ERROR_MESSAGE, + line: 10, + column: 11, + type: 'Property' + }], + output: ` + export default class ClassWithSpreadInPropTypes extends BaseClass { + static propTypes = { + b: PropTypes.string, + /* b */ + ...a.propTypes, + /* a */ + c: PropTypes.string, + /* c */ + d: PropTypes.string + /* d */ + } + } + ` + }, { + code: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + /* x */ + x: PropTypes.any, + /* y */ + y: PropTypes.any, + /* z */ + z: PropTypes.shape({ + /* a */ + a: PropTypes.string, + /* c */ + c: PropTypes.number.isRequired, + /* b */ + b: PropTypes.any, + /* other */ + ...otherPropTypes, + /* f */ + f: PropTypes.bool, + /* d */ + d: PropTypes.string + }) + }; + `, + options: [{ + sortShapeProp: true + }], + errors: [{ + message: ERROR_MESSAGE, + line: 19, + column: 11, + type: 'Property' + }, { + message: ERROR_MESSAGE, + line: 25, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + render() { + return
; + } + } + Component.propTypes = { + /* x */ + x: PropTypes.any, + /* y */ + y: PropTypes.any, + /* z */ + z: PropTypes.shape({ + /* a */ + a: PropTypes.string, + /* b */ + b: PropTypes.any, + /* c */ + c: PropTypes.number.isRequired, + /* other */ + ...otherPropTypes, + /* d */ + d: PropTypes.string, + /* f */ + f: PropTypes.bool + }) + }; + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + /* a */ + a: PropTypes.any, + /* z */ + z: PropTypes.string, + /* onFoo */ + onFoo: PropTypes.func, + /* onBar */ + onBar: PropTypes.func + }; + render() { + return
; + } + } + `, + options: [{ + callbacksLast: true + }], + parser: 'babel-eslint', + errors: [{ + message: ERROR_MESSAGE, + line: 11, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + /* a */ + a: PropTypes.any, + /* z */ + z: PropTypes.string, + /* onBar */ + onBar: PropTypes.func, + /* onFoo */ + onFoo: PropTypes.func + }; + render() { + return
; + } + } + ` + }, { + code: ` + class Component extends React.Component { + static propTypes = { + /* a */ + a: PropTypes.any, + /* bar */ + barRequired: PropTypes.string.isRequired, + /* onFoo */ + onFoo: PropTypes.func + }; + render() { + return
; + } + } + `, + parser: 'babel-eslint', + options: [{ + requiredFirst: true + }], + errors: [{ + message: REQUIRED_ERROR_MESSAGE, + line: 7, + column: 11, + type: 'Property' + }], + output: ` + class Component extends React.Component { + static propTypes = { + /* bar */ + barRequired: PropTypes.string.isRequired, + /* a */ + a: PropTypes.any, + /* onFoo */ + onFoo: PropTypes.func + }; + render() { + return
; + } + } + ` }] });