diff --git a/docs/rules/jsx-sort-prop-types.md b/docs/rules/sort-prop-types.md similarity index 81% rename from docs/rules/jsx-sort-prop-types.md rename to docs/rules/sort-prop-types.md index bf419001e5..0e863e419e 100644 --- a/docs/rules/jsx-sort-prop-types.md +++ b/docs/rules/sort-prop-types.md @@ -1,10 +1,10 @@ -# Enforce propTypes declarations alphabetical sorting (jsx-sort-prop-types) +# Enforce propTypes declarations alphabetical sorting (sort-prop-types) -Some developers prefer to sort propsTypes declarations alphabetically to be able to find necessary declaration easier at the later time. Others feel that it adds complexity and becomes burden to maintain. +Some developers prefer to sort propTypes declarations alphabetically to be able to find necessary declaration easier at the later time. Others feel that it adds complexity and becomes burden to maintain. ## Rule Details -This rule checks all JSX components and verifies that all propsTypes declarations are sorted alphabetically. +This rule checks all components and verifies that all propTypes declarations are sorted alphabetically. The default configuration of the rule is case-sensitive. This rule is off by default. @@ -78,7 +78,7 @@ class Component extends React.Component { ```js ... -"jsx-sort-prop-types": [, { +"sort-prop-types": [, { "callbacksLast": , "ignoreCase": , "requiredFirst": , @@ -92,7 +92,7 @@ When `true` the rule ignores the case-sensitivity of the declarations order. ### `callbacksLast` -When `true`, prop types for props beginning with "on" must be listed after all other props: +When `true`, propTypes for props beginning with "on" must be listed after all other props: ```js var Component = React.createClass({ diff --git a/index.js b/index.js index 5dfbf5e682..b777e7f030 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,7 @@ module.exports = { 'jsx-curly-spacing': require('./lib/rules/jsx-curly-spacing'), 'jsx-equals-spacing': require('./lib/rules/jsx-equals-spacing'), 'jsx-sort-props': require('./lib/rules/jsx-sort-props'), + 'sort-prop-types': require('./lib/rules/sort-prop-types'), 'jsx-sort-prop-types': require('./lib/rules/jsx-sort-prop-types'), 'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'), 'sort-comp': require('./lib/rules/sort-comp'), @@ -66,6 +67,7 @@ module.exports = { 'jsx-curly-spacing': 0, 'jsx-equals-spacing': 0, 'jsx-sort-props': 0, + 'sort-prop-types': 0, 'jsx-sort-prop-types': 0, 'jsx-boolean-value': 0, 'sort-comp': 0, diff --git a/lib/rules/jsx-sort-prop-types.js b/lib/rules/jsx-sort-prop-types.js index 8dd0758acb..636b9a10a7 100644 --- a/lib/rules/jsx-sort-prop-types.js +++ b/lib/rules/jsx-sort-prop-types.js @@ -1,5 +1,6 @@ /** * @fileoverview Enforce propTypes declarations alphabetical sorting + * @deprecated */ 'use strict'; @@ -7,134 +8,24 @@ // Rule Definition // ------------------------------------------------------------------------------ -module.exports = function(context) { - - var configuration = context.options[0] || {}; - var requiredFirst = configuration.requiredFirst || false; - var callbacksLast = configuration.callbacksLast || false; - var ignoreCase = configuration.ignoreCase || false; - - /** - * Checks if node is `propTypes` declaration - * @param {ASTNode} node The AST node being checked. - * @returns {Boolean} True if node is `propTypes` declaration, false if not. - */ - function isPropTypesDeclaration(node) { - - // Special case for class properties - // (babel-eslint does not expose property name so we have to rely on tokens) - if (node.type === 'ClassProperty') { - var tokens = context.getFirstTokens(node, 2); - return (tokens[0] && tokens[0].value === 'propTypes') || - (tokens[1] && tokens[1].value === 'propTypes'); - } - - return Boolean( - node && - node.name === 'propTypes' - ); - } - - function getKey(node) { - return node.key.type === 'Identifier' ? node.key.name : node.key.value; - } - - function getValueName(node) { - return node.value.property && node.value.property.name; - } - - function isCallbackPropName(propName) { - return /^on[A-Z]/.test(propName); - } - - function isRequiredProp(node) { - return getValueName(node) === 'isRequired'; - } - - /** - * Checks if propTypes declarations are sorted - * @param {Array} declarations The array of AST nodes being checked. - * @returns {void} - */ - function checkSorted(declarations) { - declarations.reduce(function(prev, curr) { - var prevPropName = getKey(prev); - var currentPropName = getKey(curr); - var previousIsRequired = isRequiredProp(prev); - var currentIsRequired = isRequiredProp(curr); - var previousIsCallback = isCallbackPropName(prevPropName); - var currentIsCallback = isCallbackPropName(currentPropName); - - if (ignoreCase) { - prevPropName = prevPropName.toLowerCase(); - currentPropName = currentPropName.toLowerCase(); - } +var sortPropTypes = require('./sort-prop-types'); +var isWarnedForDeprecation = false; - if (requiredFirst) { - if (previousIsRequired && !currentIsRequired) { - // Transition between required and non-required. Don't compare for alphabetical. - return curr; - } - if (!previousIsRequired && currentIsRequired) { - // Encountered a non-required prop after a required prop - context.report(curr, 'Required prop types must be listed before all other prop types'); - return curr; - } - } - - if (callbacksLast) { - if (!previousIsCallback && currentIsCallback) { - // Entering the callback prop section - return curr; - } - if (previousIsCallback && !currentIsCallback) { - // Encountered a non-callback prop after a callback prop - context.report(prev, 'Callback prop types must be listed after all other prop types'); - return prev; - } - } - - if (currentPropName < prevPropName) { - context.report(curr, 'Prop types declarations should be sorted alphabetically'); - return prev; - } - - return curr; - }, declarations[0]); - } - - return { - ClassProperty: function(node) { - if (isPropTypesDeclaration(node) && node.value && node.value.type === 'ObjectExpression') { - checkSorted(node.value.properties); - } - }, - - MemberExpression: function(node) { - if (isPropTypesDeclaration(node.property)) { - var right = node.parent.right; - if (right && right.type === 'ObjectExpression') { - checkSorted(right.properties); - } +module.exports = function(context) { + return Object.assign(sortPropTypes(context), { + Program: function() { + if (isWarnedForDeprecation || /\=-(f|-format)=/.test(process.argv.join('='))) { + return; } - }, - ObjectExpression: function(node) { - node.properties.forEach(function(property) { - if (!property.key) { - return; - } - if (!isPropTypesDeclaration(property.key)) { - return; - } - if (property.value.type === 'ObjectExpression') { - checkSorted(property.value.properties); - } - }); + /* eslint-disable no-console */ + console.log('The react/jsx-sort-prop-types rule is deprecated. Please ' + + 'use the react/sort-prop-types rule instead.'); + /* eslint-enable no-console */ + isWarnedForDeprecation = true; } - - }; + }); }; module.exports.schema = [{ diff --git a/lib/rules/sort-prop-types.js b/lib/rules/sort-prop-types.js new file mode 100644 index 0000000000..8dd0758acb --- /dev/null +++ b/lib/rules/sort-prop-types.js @@ -0,0 +1,154 @@ +/** + * @fileoverview Enforce propTypes declarations alphabetical sorting + */ +'use strict'; + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = function(context) { + + var configuration = context.options[0] || {}; + var requiredFirst = configuration.requiredFirst || false; + var callbacksLast = configuration.callbacksLast || false; + var ignoreCase = configuration.ignoreCase || false; + + /** + * Checks if node is `propTypes` declaration + * @param {ASTNode} node The AST node being checked. + * @returns {Boolean} True if node is `propTypes` declaration, false if not. + */ + function isPropTypesDeclaration(node) { + + // Special case for class properties + // (babel-eslint does not expose property name so we have to rely on tokens) + if (node.type === 'ClassProperty') { + var tokens = context.getFirstTokens(node, 2); + return (tokens[0] && tokens[0].value === 'propTypes') || + (tokens[1] && tokens[1].value === 'propTypes'); + } + + return Boolean( + node && + node.name === 'propTypes' + ); + } + + function getKey(node) { + return node.key.type === 'Identifier' ? node.key.name : node.key.value; + } + + function getValueName(node) { + return node.value.property && node.value.property.name; + } + + function isCallbackPropName(propName) { + return /^on[A-Z]/.test(propName); + } + + function isRequiredProp(node) { + return getValueName(node) === 'isRequired'; + } + + /** + * Checks if propTypes declarations are sorted + * @param {Array} declarations The array of AST nodes being checked. + * @returns {void} + */ + function checkSorted(declarations) { + declarations.reduce(function(prev, curr) { + var prevPropName = getKey(prev); + var currentPropName = getKey(curr); + var previousIsRequired = isRequiredProp(prev); + var currentIsRequired = isRequiredProp(curr); + var previousIsCallback = isCallbackPropName(prevPropName); + var currentIsCallback = isCallbackPropName(currentPropName); + + if (ignoreCase) { + prevPropName = prevPropName.toLowerCase(); + currentPropName = currentPropName.toLowerCase(); + } + + if (requiredFirst) { + if (previousIsRequired && !currentIsRequired) { + // Transition between required and non-required. Don't compare for alphabetical. + return curr; + } + if (!previousIsRequired && currentIsRequired) { + // Encountered a non-required prop after a required prop + context.report(curr, 'Required prop types must be listed before all other prop types'); + return curr; + } + } + + if (callbacksLast) { + if (!previousIsCallback && currentIsCallback) { + // Entering the callback prop section + return curr; + } + if (previousIsCallback && !currentIsCallback) { + // Encountered a non-callback prop after a callback prop + context.report(prev, 'Callback prop types must be listed after all other prop types'); + return prev; + } + } + + if (currentPropName < prevPropName) { + context.report(curr, 'Prop types declarations should be sorted alphabetically'); + return prev; + } + + return curr; + }, declarations[0]); + } + + return { + ClassProperty: function(node) { + if (isPropTypesDeclaration(node) && node.value && node.value.type === 'ObjectExpression') { + checkSorted(node.value.properties); + } + }, + + MemberExpression: function(node) { + if (isPropTypesDeclaration(node.property)) { + var right = node.parent.right; + if (right && right.type === 'ObjectExpression') { + checkSorted(right.properties); + } + } + }, + + ObjectExpression: function(node) { + node.properties.forEach(function(property) { + if (!property.key) { + return; + } + + if (!isPropTypesDeclaration(property.key)) { + return; + } + if (property.value.type === 'ObjectExpression') { + checkSorted(property.value.properties); + } + }); + } + + }; +}; + +module.exports.schema = [{ + type: 'object', + properties: { + requiredFirst: { + type: 'boolean' + }, + callbacksLast: { + type: 'boolean' + }, + ignoreCase: { + type: 'boolean' + } + }, + additionalProperties: false +}]; diff --git a/tests/lib/rules/jsx-sort-prop-types.js b/tests/lib/rules/jsx-sort-prop-types.js index b718dee589..5b87e39a81 100644 --- a/tests/lib/rules/jsx-sort-prop-types.js +++ b/tests/lib/rules/jsx-sort-prop-types.js @@ -1,5 +1,5 @@ /** - * @fileoverview Tests for jsx-sort-prop-types + * @fileoverview Tests for sort-prop-types */ 'use strict'; @@ -27,7 +27,7 @@ require('babel-eslint'); var ERROR_MESSAGE = 'Prop types declarations should be sorted alphabetically'; var ruleTester = new RuleTester(); -ruleTester.run('jsx-sort-prop-types', rule, { +ruleTester.run('sort-prop-types', rule, { valid: [{ code: [ diff --git a/tests/lib/rules/sort-prop-types.js b/tests/lib/rules/sort-prop-types.js new file mode 100644 index 0000000000..20450c7660 --- /dev/null +++ b/tests/lib/rules/sort-prop-types.js @@ -0,0 +1,599 @@ +/** + * @fileoverview Tests for sort-prop-types + */ +'use strict'; + +// ----------------------------------------------------------------------------- +// Requirements +// ----------------------------------------------------------------------------- + +var rule = require('../../../lib/rules/sort-prop-types'); +var RuleTester = require('eslint').RuleTester; + +var parserOptions = { + ecmaVersion: 6, + ecmaFeatures: { + experimentalObjectRestSpread: true, + jsx: true + } +}; + +require('babel-eslint'); + +// ----------------------------------------------------------------------------- +// Tests +// ----------------------------------------------------------------------------- + +var ERROR_MESSAGE = 'Prop types declarations should be sorted alphabetically'; + +var ruleTester = new RuleTester(); +ruleTester.run('sort-prop-types', rule, { + + valid: [{ + code: [ + 'var First = React.createClass({', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: externalPropTypes,', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' A: React.PropTypes.any,', + ' Z: React.PropTypes.string,', + ' a: React.PropTypes.any,', + ' z: React.PropTypes.string', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' a: React.PropTypes.any,', + ' A: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' Z: React.PropTypes.string', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + options: [{ + ignoreCase: true + }], + parserOptions: parserOptions + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' a: React.PropTypes.any,', + ' z: React.PropTypes.string', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});', + 'var Second = React.createClass({', + ' propTypes: {', + ' AA: React.PropTypes.any,', + ' ZZ: React.PropTypes.string', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions + }, { + code: [ + 'class First extends React.Component {', + ' render() {', + ' return
;', + ' }', + '}', + 'First.propTypes = {', + ' a: React.PropTypes.string,', + ' z: React.PropTypes.string', + '};', + 'First.propTypes.justforcheck = React.PropTypes.string;' + ].join('\n'), + parserOptions: parserOptions + }, { + code: [ + 'class First extends React.Component {', + ' render() {', + ' return
;', + ' }', + '}', + 'First.propTypes = {', + ' a: React.PropTypes.any,', + ' A: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' Z: React.PropTypes.string', + '};' + ].join('\n'), + options: [{ + ignoreCase: true + }], + parserOptions: parserOptions + }, { + code: [ + 'class Component extends React.Component {', + ' static propTypes = {', + ' a: React.PropTypes.any,', + ' b: React.PropTypes.any,', + ' c: React.PropTypes.any', + ' };', + ' render() {', + ' return
;', + ' }', + '}' + ].join('\n'), + parser: 'babel-eslint', + parserOptions: parserOptions + }, { + code: [ + 'class Hello extends React.Component {', + ' render() {', + ' return
Hello
;', + ' }', + '}', + 'Hello.propTypes = {', + ' "aria-controls": React.PropTypes.string', + '};' + ].join('\n'), + parser: 'babel-eslint', + options: [{ + ignoreCase: true + }] + }, { + // Invalid code, should not be validated + code: [ + 'class Component extends React.Component {', + ' propTypes: {', + ' a: React.PropTypes.any,', + ' c: React.PropTypes.any,', + ' b: React.PropTypes.any', + ' };', + ' render() {', + ' return
;', + ' }', + '}' + ].join('\n'), + parser: 'babel-eslint' + }, { + code: [ + 'var Hello = React.createClass({', + ' render: function() {', + ' let { a, ...b } = obj;', + ' let c = { ...d };', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' barRequired: React.PropTypes.func.isRequired,', + ' onBar: React.PropTypes.func,', + ' z: React.PropTypes.any', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' a: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' onBar: React.PropTypes.func,', + ' onFoo: React.PropTypes.func', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + options: [{ + callbacksLast: true + }], + parserOptions: parserOptions + }, { + code: [ + 'class Component extends React.Component {', + ' static propTypes = {', + ' a: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' onBar: React.PropTypes.func,', + ' onFoo: React.PropTypes.func', + ' };', + ' render() {', + ' return
;', + ' }', + '}' + ].join('\n'), + options: [{ + callbacksLast: true + }], + parser: 'babel-eslint', + parserOptions: parserOptions + }, { + code: [ + 'class First extends React.Component {', + ' render() {', + ' return
;', + ' }', + '}', + 'First.propTypes = {', + ' a: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' onBar: React.PropTypes.func,', + ' onFoo: React.PropTypes.func', + '};' + ].join('\n'), + options: [{ + callbacksLast: true + }], + parserOptions: parserOptions + }, { + code: [ + 'class First extends React.Component {', + ' render() {', + ' return
;', + ' }', + '}', + 'First.propTypes = {', + ' barRequired: React.PropTypes.string.isRequired,', + ' a: React.PropTypes.any', + '};' + ].join('\n'), + options: [{ + requiredFirst: true + }], + parserOptions: parserOptions + }, { + code: [ + 'class First extends React.Component {', + ' render() {', + ' return
;', + ' }', + '}', + 'First.propTypes = {', + ' fooRequired: MyPropType,', + '};' + ].join('\n'), + options: [{ + requiredFirst: true + }], + parserOptions: parserOptions + }, { + code: [ + 'class First extends React.Component {', + ' render() {', + ' return
;', + ' }', + '}', + 'First.propTypes = {', + ' barRequired: React.PropTypes.string.isRequired,', + ' fooRequired: React.PropTypes.any.isRequired,', + ' a: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' onBar: React.PropTypes.func,', + ' onFoo: React.PropTypes.func', + '};' + ].join('\n'), + options: [{ + requiredFirst: true, + callbacksLast: true + }], + parserOptions: parserOptions + }], + + invalid: [{ + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' z: React.PropTypes.string,', + ' a: React.PropTypes.any', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions, + errors: [{ + message: ERROR_MESSAGE, + line: 4, + column: 5, + type: 'Property' + }] + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' z: React.PropTypes.any,', + ' Z: React.PropTypes.any', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions, + errors: [{ + message: ERROR_MESSAGE, + line: 4, + column: 5, + type: 'Property' + }] + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' Z: React.PropTypes.any,', + ' a: React.PropTypes.any', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + options: [{ + ignoreCase: true + }], + parserOptions: parserOptions, + errors: [{ + message: ERROR_MESSAGE, + line: 4, + column: 5, + type: 'Property' + }] + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' a: React.PropTypes.any,', + ' A: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' Z: React.PropTypes.string', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions, + errors: 2 + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' a: React.PropTypes.any,', + ' Zz: React.PropTypes.string', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});', + 'var Second = React.createClass({', + ' propTypes: {', + ' aAA: React.PropTypes.any,', + ' ZZ: React.PropTypes.string', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + parserOptions: parserOptions, + errors: 2 + }, { + code: [ + 'class First extends React.Component {', + ' render() {', + ' return
;', + ' }', + '}', + 'First.propTypes = {', + ' yy: React.PropTypes.any,', + ' bb: React.PropTypes.string', + '};', + 'class Second extends React.Component {', + ' render() {', + ' return
;', + ' }', + '}', + 'Second.propTypes = {', + ' aAA: React.PropTypes.any,', + ' ZZ: React.PropTypes.string', + '};' + ].join('\n'), + parserOptions: parserOptions, + errors: 2 + }, { + code: [ + 'class Component extends React.Component {', + ' static propTypes = {', + ' z: React.PropTypes.any,', + ' y: React.PropTypes.any,', + ' a: React.PropTypes.any', + ' };', + ' render() {', + ' return
;', + ' }', + '}' + ].join('\n'), + parser: 'babel-eslint', + parserOptions: parserOptions, + errors: 2 + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' a: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' onFoo: React.PropTypes.func,', + ' onBar: React.PropTypes.func', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + options: [{ + callbacksLast: true + }], + parserOptions: parserOptions, + errors: [{ + message: ERROR_MESSAGE, + line: 6, + column: 5, + type: 'Property' + }] + }, { + code: [ + 'class Component extends React.Component {', + ' static propTypes = {', + ' a: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' onFoo: React.PropTypes.func,', + ' onBar: React.PropTypes.func', + ' };', + ' render() {', + ' return
;', + ' }', + '}' + ].join('\n'), + options: [{ + callbacksLast: true + }], + parser: 'babel-eslint', + parserOptions: parserOptions, + errors: [{ + message: ERROR_MESSAGE, + line: 6, + column: 5, + type: 'Property' + }] + }, { + code: [ + 'class First extends React.Component {', + ' render() {', + ' return
;', + ' }', + '}', + 'First.propTypes = {', + ' a: React.PropTypes.any,', + ' z: React.PropTypes.string,', + ' onFoo: React.PropTypes.func,', + ' onBar: React.PropTypes.func', + '};' + ].join('\n'), + options: [{ + callbacksLast: true + }], + parserOptions: parserOptions, + errors: [{ + message: ERROR_MESSAGE, + line: 10, + column: 5, + type: 'Property' + }] + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' a: React.PropTypes.any,', + ' onBar: React.PropTypes.func,', + ' onFoo: React.PropTypes.func,', + ' z: React.PropTypes.string', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + options: [{ + callbacksLast: true + }], + parserOptions: parserOptions, + errors: [{ + message: 'Callback prop types must be listed after all other prop types', + line: 5, + column: 5, + type: 'Property' + }] + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' fooRequired: React.PropTypes.string.isRequired,', + ' barRequired: React.PropTypes.string.isRequired,', + ' a: React.PropTypes.any', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + options: [{ + requiredFirst: true + }], + parserOptions: parserOptions, + errors: [{ + message: ERROR_MESSAGE, + line: 4, + column: 5, + type: 'Property' + }] + }, { + code: [ + 'var First = React.createClass({', + ' propTypes: {', + ' a: React.PropTypes.any,', + ' barRequired: React.PropTypes.string.isRequired,', + ' onFoo: React.PropTypes.func', + ' },', + ' render: function() {', + ' return
;', + ' }', + '});' + ].join('\n'), + options: [{ + requiredFirst: true + }], + parserOptions: parserOptions, + errors: [{ + message: 'Required prop types must be listed before all other prop types', + line: 4, + column: 5, + type: 'Property' + }] + }] +});