Repository: yannickcr/eslint-plugin-react
Pull request #679 (2016-07-13 00:06:38)

Handle spread props in jsx-no-target-blank rule
When running the `jsx-no-target-blank` rule against code like the following:

```
<a {...this.props} target='_blank' />
```

the following error would be raised:

```
TypeError: Cannot read property 'name' of undefined
```

The root cause is that the node representing the spread props does not have a `name` property.

Note that if the desired `rel` attribute appears before the spread props, the error does not occur.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d57a7e5..e3460595 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,44 @@ All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](http://semver.org/).
 This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
 
+## [6.0.0-alpha.2] - 2016-07-24
+### Added
+* Add an `all` sharable configuration with all rules enabled ([#674][] @pfhayes)
+* Add `no-find-dom-node` rule ([#678][])
+* Add `shorthandFirst` option to `jsx-sort-props` ([#391][] @mathieumg)
+* Add `allowDecorators` option to `require-optimization` ([#669][] @Tom910)
+
+### Fixed
+* Fix `jsx-no-bind` reporting errors on render functions that don't return JSX ([#663][] @petersendidit)
+* Fix `jsx-closing-bracket-location` autofix when `location` is set to `props-aligned` ([#684][] @pfhayes)
+* Fix `prop-types` for destructured arguments being assigned to the parent stateless component in some cases ([#698][])
+* Fix `prop-types` for JSX return being assigned to the parent function in some cases ([#504][])
+* Fix `jsx-curly-spacing` for reporting on JSX content by mistake ([#671][])
+
+### Breaking
+* Remove deprecated `jsx-sort-prop-types` rule, use `sort-prop-types` instead ([#549][] @lencioni)
+* Rename `jsx-no-comment-textnodes` to `no-comment-textnodes`. `jsx-no-comment-textnodes` still works but will trigger a warning ([#668][] @lencioni)
+* Rename `jsx-require-extension` to `require-extension`. `jsx-require-extension` still works but will trigger a warning ([#668][] @lencioni)
+* Rename `jsx-wrap-multilines` to `wrap-multilines`. `jsx-wrap-multilines` still works but will trigger a warning ([#668][] @lencioni)
+
+### Changed
+* Update dependencies
+* Only report `jsx-filename-extension` warning once per file ([#660][] @mathieumg)
+
+[6.0.0-alpha.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v6.0.0-alpha.1...v6.0.0-alpha.2
+[#674]: https://github.com/yannickcr/eslint-plugin-react/issues/674
+[#678]: https://github.com/yannickcr/eslint-plugin-react/issues/678
+[#391]: https://github.com/yannickcr/eslint-plugin-react/issues/391
+[#669]: https://github.com/yannickcr/eslint-plugin-react/pull/669
+[#663]: https://github.com/yannickcr/eslint-plugin-react/issues/663
+[#684]: https://github.com/yannickcr/eslint-plugin-react/pull/684
+[#698]: https://github.com/yannickcr/eslint-plugin-react/issues/698
+[#504]: https://github.com/yannickcr/eslint-plugin-react/issues/504
+[#671]: https://github.com/yannickcr/eslint-plugin-react/issues/671
+[#549]: https://github.com/yannickcr/eslint-plugin-react/issues/549
+[#668]: https://github.com/yannickcr/eslint-plugin-react/issues/668
+[#660]: https://github.com/yannickcr/eslint-plugin-react/pull/660
+
 ## [6.0.0-alpha.1] - 2016-07-08
 ### Fixed
 * Fix `prop-types` crash when accessing constructor on props ([#654][])
diff --git a/README.md b/README.md
index a5497302..9bf65866 100644
--- a/README.md
+++ b/README.md
@@ -81,12 +81,12 @@ Finally, enable all of the rules that you would like to use.  Use [our preset](#
 
 * [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
 * [react/forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
-* [react/no-comment-textnodes](docs/rules/no-comment-textnodes.md): Prevent comments from being inserted as text nodes
 * [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
 * [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods
 * [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`
 * [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate`
 * [react/no-direct-mutation-state](docs/rules/no-direct-mutation-state.md): Prevent direct mutation of `this.state`
+* [react/no-find-dom-node](docs/rules/no-find-dom-node.md): Prevent usage of `findDOMNode`
 * [react/no-is-mounted](docs/rules/no-is-mounted.md): Prevent usage of `isMounted`
 * [react/no-multi-comp](docs/rules/no-multi-comp.md): Prevent multiple component definition per file
 * [react/no-render-return-value](docs/rules/no-render-return-value.md): Prevent usage of the return value of `React.render`
@@ -97,13 +97,11 @@ Finally, enable all of the rules that you would like to use.  Use [our preset](#
 * [react/prefer-stateless-function](docs/rules/prefer-stateless-function.md): Enforce stateless React Components to be written as a pure function
 * [react/prop-types](docs/rules/prop-types.md): Prevent missing props validation in a React component definition
 * [react/react-in-jsx-scope](docs/rules/react-in-jsx-scope.md): Prevent missing `React` when using JSX
-* [react/require-extension](docs/rules/require-extension.md): Restrict file extensions that may be required
 * [react/require-optimization](docs/rules/require-optimization.md): Enforce React components to have a shouldComponentUpdate method
 * [react/require-render-return](docs/rules/require-render-return.md): Enforce ES5 or ES6 class for returning value in render function
 * [react/self-closing-comp](docs/rules/self-closing-comp.md): Prevent extra closing tags for components without children
 * [react/sort-comp](docs/rules/sort-comp.md): Enforce component methods order
 * [react/sort-prop-types](docs/rules/sort-prop-types.md): Enforce propTypes declarations alphabetical sorting
-* [react/wrap-multilines](docs/rules/wrap-multilines.md): Prevent missing parentheses around multilines JSX (fixable)
 
 ## JSX-specific rules
 
@@ -119,15 +117,18 @@ Finally, enable all of the rules that you would like to use.  Use [our preset](#
 * [react/jsx-key](docs/rules/jsx-key.md): Validate JSX has key prop when in array or iterator
 * [react/jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md): Limit maximum of props on a single line in JSX
 * [react/jsx-no-bind](docs/rules/jsx-no-bind.md): Prevent usage of `.bind()` and arrow functions in JSX props
+* [react/jsx-no-comment-textnodes](docs/rules/jsx-no-comment-textnodes.md): Prevent comments from being inserted as text nodes
 * [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md): Prevent duplicate props in JSX
 * [react/jsx-no-literals](docs/rules/jsx-no-literals.md): Prevent usage of unwrapped JSX strings
 * [react/jsx-no-target-blank](docs/rules/jsx-no-target-blank.md): Prevent usage of unsafe `target='_blank'`
 * [react/jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
 * [react/jsx-pascal-case](docs/rules/jsx-pascal-case.md): Enforce PascalCase for user-defined JSX components
+* [react/jsx-require-extension](docs/rules/jsx-require-extension.md): Restrict file extensions that may be required
 * [react/jsx-sort-props](docs/rules/jsx-sort-props.md): Enforce props alphabetical sorting
 * [react/jsx-space-before-closing](docs/rules/jsx-space-before-closing.md): Validate spacing before closing bracket in JSX (fixable)
 * [react/jsx-uses-react](docs/rules/jsx-uses-react.md): Prevent React to be incorrectly marked as unused
 * [react/jsx-uses-vars](docs/rules/jsx-uses-vars.md): Prevent variables used in JSX to be incorrectly marked as unused
+* [react/jsx-wrap-multilines](docs/rules/jsx-wrap-multilines.md): Prevent missing parentheses around multilines JSX (fixable)
 
 ## React Native rules
 
@@ -162,6 +163,7 @@ The rules enabled in this configuration are:
 * [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md) with `allow-in-func` option
 * [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md) with `allow-in-func` option
 * [react/no-direct-mutation-state](docs/rules/no-direct-mutation-state.md)
+* [react/no-find-dom-node](docs/rules/no-find-dom-node.md)
 * [react/no-is-mounted](docs/rules/no-is-mounted.md)
 * [react/no-unknown-property](docs/rules/no-unknown-property.md)
 * [react/prop-types](docs/rules/prop-types.md)
@@ -169,6 +171,18 @@ The rules enabled in this configuration are:
 
 **Note**: This configuration will also enable JSX in [parser options](http://eslint.org/docs/user-guide/configuring#specifying-parser-options).
 
+This plugin also exports an `all` configuration that includes every available rule.
+This pairs well with the `eslint:all` rule.
+
+```js
+{
+  "plugins": [
+    "react"
+  ],
+  "extends": ["eslint:all", "plugin:react/all"]
+}
+```
+
 # License
 
 ESLint-plugin-React is licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
diff --git a/docs/rules/no-comment-textnodes.md b/docs/rules/jsx-no-comment-textnodes.md
similarity index 94%
rename from docs/rules/no-comment-textnodes.md
rename to docs/rules/jsx-no-comment-textnodes.md
index 22e73b16..9dfd339c 100644
--- a/docs/rules/no-comment-textnodes.md
+++ b/docs/rules/jsx-no-comment-textnodes.md
@@ -1,4 +1,4 @@
-# Prevent comments from being inserted as text nodes (no-comment-textnodes)
+# Prevent comments from being inserted as text nodes (jsx-no-comment-textnodes)
 
 This rule prevents comment strings (e.g. beginning with `//` or `/*`) from being accidentally
 injected as a text node in JSX statements.
diff --git a/docs/rules/require-extension.md b/docs/rules/jsx-require-extension.md
similarity index 89%
rename from docs/rules/require-extension.md
rename to docs/rules/jsx-require-extension.md
index 1e91d6d2..00761375 100644
--- a/docs/rules/require-extension.md
+++ b/docs/rules/jsx-require-extension.md
@@ -1,4 +1,4 @@
-# Restrict file extensions that may be required (require-extension)
+# Restrict file extensions that may be required (jsx-require-extension)
 
 `require()` statements should generally not include a file extension as there is a well defined mechanism for resolving a module ID to a specific file. This rule inspects the module ID being required and creates a warning if the ID contains a '.jsx' file extension.
 
@@ -29,7 +29,7 @@ The set of forbidden extensions is configurable. By default '.jsx' is blocked. I
 
 ```js
 "rules": {
-  "react/require-extension": [1, { "extensions": [".js", ".jsx"] }],
+  "react/jsx-require-extension": [1, { "extensions": [".js", ".jsx"] }],
 }
 ```
 
diff --git a/docs/rules/jsx-sort-props.md b/docs/rules/jsx-sort-props.md
index e2cc8660..2fb8ca56 100644
--- a/docs/rules/jsx-sort-props.md
+++ b/docs/rules/jsx-sort-props.md
@@ -26,6 +26,7 @@ The following patterns are considered okay and do not cause warnings:
 "jsx-sort-props": [<enabled>, {
   "callbacksLast": <boolean>,
   "shorthandFirst": <boolean>,
+  "shorthandLast": <boolean>,
   "ignoreCase": <boolean>
 }]
 ...
@@ -43,7 +44,7 @@ The following patterns are considered okay and do not cause warnings:
 
 ### `callbacksLast`
 
-When `true`, callbacks must be listed after all other props:
+When `true`, callbacks must be listed after all other props, even if `shorthandLast` is set :
 
 ```js
 <Hello tel={5555555} onClick={this._handleClick} />
@@ -57,6 +58,14 @@ When `true`, short hand props must be listed before all other props, but still r
 <Hello active validate name="John" tel={5555555} />
 ```
 
+### `shorthandLast`
+
+When `true`, short hand props must be listed after all other props (unless `callbacksLast` is set), but still respecting the alphabetical order:
+
+```js
+<Hello name="John" tel={5555555} active validate />
+```
+
 ## When not to use
 
 This rule is a formatting preference and not following it won't negatively affect the quality of your code. If alphabetizing props isn't a part of your coding standards, then you can leave this rule off.
diff --git a/docs/rules/jsx-uses-react.md b/docs/rules/jsx-uses-react.md
index ecf9f788..8cbb022a 100644
--- a/docs/rules/jsx-uses-react.md
+++ b/docs/rules/jsx-uses-react.md
@@ -7,6 +7,7 @@ If you are using the @jsx pragma this rule will mark the designated variable and
 
 This rule has no effect if the `no-unused-vars` rule is not enabled.
 
+You can use the [shared settings](/README.md#configuration) to specify a custom pragma.
 
 ## Rule Details
 
@@ -40,28 +41,6 @@ var Foo = require('foo');
 var Hello = <div>Hello {this.props.name}</div>;
 ```
 
-## Rule Options
-
-```js
-...
-"jsx-uses-react": [<enabled>, { "pragma": <string> }]
-...
-```
-
-### `pragma`
-
-**Deprecation notice**: This option is deprecated, please use the [shared settings](/README.md#configuration) to specify a custom pragma.
-
-As an alternative to specifying the above pragma in each source file, you can specify
-this configuration option:
-
-```js
-var Foo = require('Foo');
-
-var Hello = <div>Hello {this.props.name}</div>;
-```
-
-
 ## When Not To Use It
 
 If you are not using JSX, if React is declared as global variable or if you do not use the `no-unused-vars` rule then you can disable this rule.
diff --git a/docs/rules/wrap-multilines.md b/docs/rules/jsx-wrap-multilines.md
similarity index 93%
rename from docs/rules/wrap-multilines.md
rename to docs/rules/jsx-wrap-multilines.md
index b24e892b..3179f8cc 100644
--- a/docs/rules/wrap-multilines.md
+++ b/docs/rules/jsx-wrap-multilines.md
@@ -1,4 +1,4 @@
-# Prevent missing parentheses around multiline JSX (wrap-multilines)
+# Prevent missing parentheses around multiline JSX (jsx-wrap-multilines)
 
 Wrapping multiline JSX in parentheses can improve readability and/or convenience. It optionally takes a second parameter in the form of an object, containing places to apply the rule. By default, `"declaration"`, `"assignment"`, and `"return"` syntax is checked, but these can be explicitly disabled. Any syntax type missing in the object will follow the default behavior (become enabled).
 
diff --git a/docs/rules/no-deprecated.md b/docs/rules/no-deprecated.md
index ffbef989..333664a5 100644
--- a/docs/rules/no-deprecated.md
+++ b/docs/rules/no-deprecated.md
@@ -1,6 +1,6 @@
 # Prevent usage of deprecated methods (no-deprecated)
 
-Several methods are deprecated between React versions. This rule will warn you if you try to use a deprecated method.
+Several methods are deprecated between React versions. This rule will warn you if you try to use a deprecated method. Use the [shared settings](/README.md#configuration) to specify the React version.
 
 ## Rule Details
 
@@ -26,15 +26,3 @@ ReactDOM.render(<MyComponent />, root);
 // When [1, {"react": "0.13.0"}]
 ReactDOM.findDOMNode(this.refs.foo);
 ```
-
-## Rule Options
-
-**Deprecation notice**: This option is deprecated, please use the [shared settings](/README.md#configuration) to specify the React version.
-
-By default this rule will warn to every methods marked as deprecated. You can limit it to an older React version if you are not using the latest one:
-
-```js
-"rules": {
-  "react/no-deprecated": [1, {"react": "0.12.0"}] // Will warn for every deprecated methods in React 0.12.0 and below
-}
-```
diff --git a/docs/rules/no-find-dom-node.md b/docs/rules/no-find-dom-node.md
new file mode 100644
index 00000000..f981c003
--- /dev/null
+++ b/docs/rules/no-find-dom-node.md
@@ -0,0 +1,33 @@
+# Prevent usage of findDOMNode (no-find-dom-node)
+
+Facebook will eventually deprecate `findDOMNode` as it blocks certain improvements in React in the future.
+
+It is recommended to use callback refs instead. See [Dan Abramov comments and examples](https://github.com/yannickcr/eslint-plugin-react/issues/678#issue-165177220).
+
+## Rule Details
+
+The following patterns are considered warnings:
+
+```js
+class MyComponent extends Component {
+  componentDidMount() {
+    findDOMNode(this).scrollIntoView();
+  }
+  render() {
+    return <div />
+  }
+}
+```
+
+The following patterns are not considered warnings:
+
+```js
+class MyComponent extends Component {
+  componentDidMount() {
+    this.node.scrollIntoView();
+  }
+  render() {
+    return <div ref={node => this.node = node} />
+  }
+}
+```
diff --git a/docs/rules/require-optimization.md b/docs/rules/require-optimization.md
index 62a45a3b..4758e7a5 100644
--- a/docs/rules/require-optimization.md
+++ b/docs/rules/require-optimization.md
@@ -52,14 +52,30 @@ React.createClass({
 
 ```js
 ...
-"require-optimization": [<enabled>]
+"require-optimization": [<enabled>, { allowDecorators: [<allowDecorator>] }]
 ...
 ```
 
+* `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0.
+* `allowDecorators`: optional array of decorators names to allow validation.
+
+
+### `allowDecorators`
+
+Sets the allowed names of decorators. If the variable is present in the chain of decorators, it validates
+
+The following patterns are not warnings:
+
+```js
+// ['pureRender']
+@pureRender
+class Hello extends React.Component {}
+```
+
 ### Example
 
 ```js
 ...
-"require-optimization": 2
+"require-optimization": [2, {allowDecorators: ['customDecorators']}]
 ...
 ```
diff --git a/index.js b/index.js
index 25cbdb4c..a28494f7 100644
--- a/index.js
+++ b/index.js
@@ -1,55 +1,66 @@
 'use strict';
 
+var rules = {
+  'jsx-uses-react': require('./lib/rules/jsx-uses-react'),
+  'no-multi-comp': require('./lib/rules/no-multi-comp'),
+  'prop-types': require('./lib/rules/prop-types'),
+  'display-name': require('./lib/rules/display-name'),
+  'wrap-multilines': require('./lib/rules/wrap-multilines'),
+  'jsx-wrap-multilines': require('./lib/rules/jsx-wrap-multilines'),
+  'self-closing-comp': require('./lib/rules/self-closing-comp'),
+  'no-comment-textnodes': require('./lib/rules/no-comment-textnodes'),
+  'jsx-no-comment-textnodes': require('./lib/rules/jsx-no-comment-textnodes'),
+  'no-danger': require('./lib/rules/no-danger'),
+  'no-set-state': require('./lib/rules/no-set-state'),
+  'no-is-mounted': require('./lib/rules/no-is-mounted'),
+  'no-deprecated': require('./lib/rules/no-deprecated'),
+  'no-did-mount-set-state': require('./lib/rules/no-did-mount-set-state'),
+  'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'),
+  'no-render-return-value': require('./lib/rules/no-render-return-value'),
+  'react-in-jsx-scope': require('./lib/rules/react-in-jsx-scope'),
+  'jsx-uses-vars': require('./lib/rules/jsx-uses-vars'),
+  'jsx-handler-names': require('./lib/rules/jsx-handler-names'),
+  'jsx-pascal-case': require('./lib/rules/jsx-pascal-case'),
+  'jsx-no-bind': require('./lib/rules/jsx-no-bind'),
+  'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
+  'no-unknown-property': require('./lib/rules/no-unknown-property'),
+  '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-boolean-value': require('./lib/rules/jsx-boolean-value'),
+  'sort-comp': require('./lib/rules/sort-comp'),
+  'require-extension': require('./lib/rules/require-extension'),
+  'jsx-require-extension': require('./lib/rules/jsx-require-extension'),
+  'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props'),
+  'jsx-max-props-per-line': require('./lib/rules/jsx-max-props-per-line'),
+  'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
+  'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
+  'jsx-indent': require('./lib/rules/jsx-indent'),
+  'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
+  'jsx-space-before-closing': require('./lib/rules/jsx-space-before-closing'),
+  'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
+  'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
+  'prefer-es6-class': require('./lib/rules/prefer-es6-class'),
+  'jsx-key': require('./lib/rules/jsx-key'),
+  'no-string-refs': require('./lib/rules/no-string-refs'),
+  'prefer-stateless-function': require('./lib/rules/prefer-stateless-function'),
+  'require-render-return': require('./lib/rules/require-render-return'),
+  'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line'),
+  'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
+  'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
+  'require-optimization': require('./lib/rules/require-optimization'),
+  'no-find-dom-node': require('./lib/rules/no-find-dom-node')
+};
+
+var ruleNames = Object.keys(rules);
+var allRules = {};
+for (var i = 0; i < ruleNames.length; i++) {
+  allRules['react/' + ruleNames[i]] = 2;
+}
+
 module.exports = {
-  rules: {
-    'jsx-uses-react': require('./lib/rules/jsx-uses-react'),
-    'no-multi-comp': require('./lib/rules/no-multi-comp'),
-    'prop-types': require('./lib/rules/prop-types'),
-    'display-name': require('./lib/rules/display-name'),
-    'wrap-multilines': require('./lib/rules/wrap-multilines'),
-    'self-closing-comp': require('./lib/rules/self-closing-comp'),
-    'no-comment-textnodes': require('./lib/rules/no-comment-textnodes'),
-    'no-danger': require('./lib/rules/no-danger'),
-    'no-set-state': require('./lib/rules/no-set-state'),
-    'no-is-mounted': require('./lib/rules/no-is-mounted'),
-    'no-deprecated': require('./lib/rules/no-deprecated'),
-    'no-did-mount-set-state': require('./lib/rules/no-did-mount-set-state'),
-    'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'),
-    'no-render-return-value': require('./lib/rules/no-render-return-value'),
-    'react-in-jsx-scope': require('./lib/rules/react-in-jsx-scope'),
-    'jsx-uses-vars': require('./lib/rules/jsx-uses-vars'),
-    'jsx-handler-names': require('./lib/rules/jsx-handler-names'),
-    'jsx-pascal-case': require('./lib/rules/jsx-pascal-case'),
-    'jsx-no-bind': require('./lib/rules/jsx-no-bind'),
-    'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
-    'no-unknown-property': require('./lib/rules/no-unknown-property'),
-    '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'),
-    'require-extension': require('./lib/rules/require-extension'),
-    'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props'),
-    'jsx-max-props-per-line': require('./lib/rules/jsx-max-props-per-line'),
-    'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
-    'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
-    'jsx-indent': require('./lib/rules/jsx-indent'),
-    'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
-    'jsx-space-before-closing': require('./lib/rules/jsx-space-before-closing'),
-    'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
-    'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
-    'prefer-es6-class': require('./lib/rules/prefer-es6-class'),
-    'jsx-key': require('./lib/rules/jsx-key'),
-    'no-string-refs': require('./lib/rules/no-string-refs'),
-    'prefer-stateless-function': require('./lib/rules/prefer-stateless-function'),
-    'require-render-return': require('./lib/rules/require-render-return'),
-    'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line'),
-    'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
-    'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
-    'require-optimization': require('./lib/rules/require-optimization')
-  },
+  rules: rules,
   configs: {
     recommended: {
       parserOptions: {
@@ -68,12 +79,21 @@ module.exports = {
         'react/no-did-mount-set-state': [2, 'allow-in-func'],
         'react/no-did-update-set-state': [2, 'allow-in-func'],
         'react/no-direct-mutation-state': 2,
+        'react/no-find-dom-node': 2,
         'react/no-is-mounted': 2,
         'react/no-unknown-property': 2,
         'react/no-render-return-value': 2,
         'react/prop-types': 2,
         'react/react-in-jsx-scope': 2
       }
+    },
+    all: {
+      parserOptions: {
+        ecmaFeatures: {
+          jsx: true
+        }
+      },
+      rules: allRules
     }
   }
 };
diff --git a/lib/rules/jsx-closing-bracket-location.js b/lib/rules/jsx-closing-bracket-location.js
index 8a8f180d..47bd4bd2 100644
--- a/lib/rules/jsx-closing-bracket-location.js
+++ b/lib/rules/jsx-closing-bracket-location.js
@@ -167,7 +167,6 @@ module.exports = function(context) {
     'JSXOpeningElement:exit': function(node) {
       var attributeNode = lastAttributeNode[getOpeningElementId(node)];
       var cachedLastAttributeEndPos = attributeNode ? attributeNode.end : null;
-      var cachedLastAttributeStartPos = attributeNode ? attributeNode.start : null;
       var expectedNextLine;
       var tokens = getTokensLocations(node);
       var expectedLocation = getExpectedLocation(tokens);
@@ -205,17 +204,11 @@ module.exports = function(context) {
               return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
                 (expectedNextLine ? '\n' : '') + closingTag);
             case 'props-aligned':
-              var spaces = new Array(cachedLastAttributeEndPos - cachedLastAttributeStartPos);
-              return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
-                '\n' + spaces.join(' ') + closingTag);
             case 'tag-aligned':
-              var tagSpaces = new Array(+correctColumn + 1);
-              return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
-                '\n' + tagSpaces.join(' ') + closingTag);
             case 'line-aligned':
-              var lineSpaces = new Array(+correctColumn + 1);
+              var spaces = new Array(+correctColumn + 1);
               return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
-                '\n' + lineSpaces.join(' ') + closingTag);
+                '\n' + spaces.join(' ') + closingTag);
             default:
               return true;
           }
diff --git a/lib/rules/jsx-curly-spacing.js b/lib/rules/jsx-curly-spacing.js
index 0cdafb90..e42a04a6 100644
--- a/lib/rules/jsx-curly-spacing.js
+++ b/lib/rules/jsx-curly-spacing.js
@@ -155,6 +155,10 @@ module.exports = function(context) {
    * @returns {void}
    */
   function validateBraceSpacing(node) {
+    // Only validate attributes
+    if (node.parent.type === 'JSXElement') {
+      return;
+    }
     var first = context.getFirstToken(node);
     var last = sourceCode.getLastToken(node);
     var second = context.getTokenAfter(first);
diff --git a/lib/rules/jsx-filename-extension.js b/lib/rules/jsx-filename-extension.js
index 2fde88d4..727a3c16 100644
--- a/lib/rules/jsx-filename-extension.js
+++ b/lib/rules/jsx-filename-extension.js
@@ -25,18 +25,24 @@ module.exports = function(context) {
     return context.options[0] && context.options[0].extensions || DEFAULTS.extensions;
   }
 
+  var invalidExtension;
+  var invalidNode;
+
   // --------------------------------------------------------------------------
   // Public
   // --------------------------------------------------------------------------
 
   return {
-
     JSXElement: function(node) {
       var filename = context.getFilename();
       if (filename === '<text>') {
         return;
       }
 
+      if (invalidNode) {
+        return;
+      }
+
       var allowedExtensions = getExtensionsConfig();
       var isAllowedExtension = allowedExtensions.some(function (extension) {
         return filename.slice(-extension.length) === extension;
@@ -46,11 +52,18 @@ module.exports = function(context) {
         return;
       }
 
-      var extension = path.extname(filename);
+      invalidNode = node;
+      invalidExtension = path.extname(filename);
+    },
+
+    'Program:exit': function() {
+      if (!invalidNode) {
+        return;
+      }
 
       context.report({
-        node: node,
-        message: 'JSX not allowed in files with extension \'' + extension + '\''
+        node: invalidNode,
+        message: 'JSX not allowed in files with extension \'' + invalidExtension + '\''
       });
     }
   };
diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js
index 091c1a8f..2d63df31 100644
--- a/lib/rules/jsx-no-bind.js
+++ b/lib/rules/jsx-no-bind.js
@@ -5,13 +5,14 @@
  */
 'use strict';
 
+var Components = require('../util/Components');
 var propName = require('jsx-ast-utils/propName');
 
 // -----------------------------------------------------------------------------
 // Rule Definition
 // -----------------------------------------------------------------------------
 
-module.exports = function(context) {
+module.exports = Components.detect(function(context, components, utils) {
   var configuration = context.options[0] || {};
 
   return {
@@ -30,10 +31,12 @@ module.exports = function(context) {
           (ancestors[i].type === 'MethodDefinition' && ancestors[i].key.name === 'render') ||
           (ancestors[i].type === 'Property' && ancestors[i].key.name === 'render')
         ) {
-          context.report({
-            node: callee,
-            message: 'JSX props should not use .bind()'
-          });
+          if (utils.isReturningJSX(ancestors[i])) {
+            context.report({
+              node: callee,
+              message: 'JSX props should not use .bind()'
+            });
+          }
           break;
         }
       }
@@ -74,7 +77,7 @@ module.exports = function(context) {
       }
     }
   };
-};
+});
 
 module.exports.schema = [{
   type: 'object',
diff --git a/lib/rules/jsx-no-comment-textnodes.js b/lib/rules/jsx-no-comment-textnodes.js
new file mode 100644
index 00000000..7c3cf71d
--- /dev/null
+++ b/lib/rules/jsx-no-comment-textnodes.js
@@ -0,0 +1,38 @@
+/**
+ * @fileoverview Comments inside children section of tag should be placed inside braces.
+ * @author Ben Vinegar
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+  function reportLiteralNode(node) {
+    context.report(node, 'Comments inside children section of tag should be placed inside braces');
+  }
+
+  // --------------------------------------------------------------------------
+  // Public
+  // --------------------------------------------------------------------------
+
+  return {
+    Literal: function(node) {
+      if (/^\s*\/(\/|\*)/m.test(node.value)) {
+        // inside component, e.g. <div>literal</div>
+        if (node.parent.type !== 'JSXAttribute' &&
+            node.parent.type !== 'JSXExpressionContainer' &&
+            node.parent.type.indexOf('JSX') !== -1) {
+          reportLiteralNode(node);
+        }
+      }
+    }
+  };
+};
+
+module.exports.schema = [{
+  type: 'object',
+  properties: {},
+  additionalProperties: false
+}];
diff --git a/lib/rules/jsx-no-target-blank.js b/lib/rules/jsx-no-target-blank.js
index 8f1d7839..6aec5d32 100644
--- a/lib/rules/jsx-no-target-blank.js
+++ b/lib/rules/jsx-no-target-blank.js
@@ -15,7 +15,7 @@ module.exports = function(context) {
         var relFound = false;
         var attrs = node.parent.attributes;
         for (var idx in attrs) {
-          if (attrs[idx].name.name === 'rel') {
+          if (attrs[idx].name && attrs[idx].name.name === 'rel') {
             var tags = attrs[idx].value.value.split(' ');
             if (tags.indexOf('noopener') >= 0 && tags.indexOf('noreferrer') >= 0) {
               relFound = true;
diff --git a/lib/rules/jsx-require-extension.js b/lib/rules/jsx-require-extension.js
new file mode 100644
index 00000000..dde310db
--- /dev/null
+++ b/lib/rules/jsx-require-extension.js
@@ -0,0 +1,88 @@
+/**
+ * @fileoverview Restrict file extensions that may be required
+ * @author Scott Andrews
+ */
+'use strict';
+
+var path = require('path');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var DEFAULTS = {
+  extensions: ['.jsx']
+};
+
+var PKG_REGEX = /^[^\.]((?!\/).)*$/;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+  function isPackage(id) {
+    return PKG_REGEX.test(id);
+  }
+
+  function isRequire(expression) {
+    return expression.callee.name === 'require';
+  }
+
+  function getId(expression) {
+    return expression.arguments[0] && expression.arguments[0].value;
+  }
+
+  function getExtension(id) {
+    return path.extname(id || '');
+  }
+
+  function getExtensionsConfig() {
+    return context.options[0] && context.options[0].extensions || DEFAULTS.extensions;
+  }
+
+  var forbiddenExtensions = getExtensionsConfig().reduce(function (extensions, extension) {
+    extensions[extension] = true;
+    return extensions;
+  }, Object.create(null));
+
+  function isForbiddenExtension(ext) {
+    return ext in forbiddenExtensions;
+  }
+
+  // --------------------------------------------------------------------------
+  // Public
+  // --------------------------------------------------------------------------
+
+  return {
+
+    CallExpression: function(node) {
+      if (isRequire(node)) {
+        var id = getId(node);
+        var ext = getExtension(id);
+        if (!isPackage(id) && isForbiddenExtension(ext)) {
+          context.report({
+            node: node,
+            message: 'Unable to require module with extension \'' + ext + '\''
+          });
+        }
+      }
+    }
+
+  };
+
+};
+
+module.exports.schema = [{
+  type: 'object',
+  properties: {
+    extensions: {
+      type: 'array',
+      items: {
+        type: 'string'
+      }
+    }
+  },
+  additionalProperties: false
+}];
diff --git a/lib/rules/jsx-sort-prop-types.js b/lib/rules/jsx-sort-prop-types.js
deleted file mode 100644
index 549654ad..00000000
--- a/lib/rules/jsx-sort-prop-types.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * @fileoverview Enforce propTypes declarations alphabetical sorting
- * @deprecated
- */
-'use strict';
-
-// ------------------------------------------------------------------------------
-// Rule Definition
-// ------------------------------------------------------------------------------
-
-var util = require('util');
-var sortPropTypes = require('./sort-prop-types');
-var isWarnedForDeprecation = false;
-
-module.exports = function(context) {
-  return util._extend(sortPropTypes(context), {
-    Program: function() {
-      if (isWarnedForDeprecation || /\=-(f|-format)=/.test(process.argv.join('='))) {
-        return;
-      }
-
-      /* 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 = [{
-  type: 'object',
-  properties: {
-    requiredFirst: {
-      type: 'boolean'
-    },
-    callbacksLast: {
-      type: 'boolean'
-    },
-    ignoreCase: {
-      type: 'boolean'
-    }
-  },
-  additionalProperties: false
-}];
diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js
index 1a3e3640..a448e2eb 100644
--- a/lib/rules/jsx-sort-props.js
+++ b/lib/rules/jsx-sort-props.js
@@ -20,6 +20,7 @@ module.exports = function(context) {
   var ignoreCase = configuration.ignoreCase || false;
   var callbacksLast = configuration.callbacksLast || false;
   var shorthandFirst = configuration.shorthandFirst || false;
+  var shorthandLast = configuration.shorthandLast || false;
 
   return {
     JSXOpeningElement: function(node) {
@@ -68,6 +69,19 @@ module.exports = function(context) {
           }
         }
 
+        if (shorthandLast) {
+          if (!currentValue && previousValue) {
+            return decl;
+          }
+          if (currentValue && !previousValue) {
+            context.report({
+              node: memo,
+              message: 'Shorthand props must be listed after all other props'
+            });
+            return memo;
+          }
+        }
+
         if (currentPropName < previousPropName) {
           context.report({
             node: decl,
@@ -86,7 +100,7 @@ module.exports.schema = [{
   type: 'object',
   properties: {
     // Whether callbacks (prefixed with "on") should be listed at the very end,
-    // after all other props.
+    // after all other props. Supersedes shorthandLast.
     callbacksLast: {
       type: 'boolean'
     },
@@ -94,6 +108,10 @@ module.exports.schema = [{
     shorthandFirst: {
       type: 'boolean'
     },
+    // Whether shorthand properties (without a value) should be listed last
+    shorthandLast: {
+      type: 'boolean'
+    },
     ignoreCase: {
       type: 'boolean'
     }
diff --git a/lib/rules/jsx-uses-react.js b/lib/rules/jsx-uses-react.js
index c5fd7420..5cac4b40 100644
--- a/lib/rules/jsx-uses-react.js
+++ b/lib/rules/jsx-uses-react.js
@@ -33,12 +33,4 @@ module.exports = function(context) {
 
 };
 
-module.exports.schema = [{
-  type: 'object',
-  properties: {
-    pragma: {
-      type: 'string'
-    }
-  },
-  additionalProperties: false
-}];
+module.exports.schema = [];
diff --git a/lib/rules/jsx-wrap-multilines.js b/lib/rules/jsx-wrap-multilines.js
new file mode 100644
index 00000000..a28b9a73
--- /dev/null
+++ b/lib/rules/jsx-wrap-multilines.js
@@ -0,0 +1,103 @@
+/**
+ * @fileoverview Prevent missing parentheses around multilines JSX
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var DEFAULTS = {
+  declaration: true,
+  assignment: true,
+  return: true
+};
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+  var sourceCode = context.getSourceCode();
+
+  function isParenthesised(node) {
+    var previousToken = sourceCode.getTokenBefore(node);
+    var nextToken = sourceCode.getTokenAfter(node);
+
+    return previousToken && nextToken &&
+      previousToken.value === '(' && previousToken.range[1] <= node.range[0] &&
+      nextToken.value === ')' && nextToken.range[0] >= node.range[1];
+  }
+
+  function isMultilines(node) {
+    return node.loc.start.line !== node.loc.end.line;
+  }
+
+  function check(node) {
+    if (!node || node.type !== 'JSXElement') {
+      return;
+    }
+
+    if (!isParenthesised(node) && isMultilines(node)) {
+      context.report({
+        node: node,
+        message: 'Missing parentheses around multilines JSX',
+        fix: function(fixer) {
+          return fixer.replaceText(node, '(' + sourceCode.getText(node) + ')');
+        }
+      });
+    }
+  }
+
+  function isEnabled(type) {
+    var userOptions = context.options[0] || {};
+    if (({}).hasOwnProperty.call(userOptions, type)) {
+      return userOptions[type];
+    }
+    return DEFAULTS[type];
+  }
+
+  // --------------------------------------------------------------------------
+  // Public
+  // --------------------------------------------------------------------------
+
+  return {
+
+    VariableDeclarator: function(node) {
+      if (isEnabled('declaration')) {
+        check(node.init);
+      }
+    },
+
+    AssignmentExpression: function(node) {
+      if (isEnabled('assignment')) {
+        check(node.right);
+      }
+    },
+
+    ReturnStatement: function(node) {
+      if (isEnabled('return')) {
+        check(node.argument);
+      }
+    }
+  };
+
+};
+
+module.exports.schema = [{
+  type: 'object',
+  properties: {
+    declaration: {
+      type: 'boolean'
+    },
+    assignment: {
+      type: 'boolean'
+    },
+    return: {
+      type: 'boolean'
+    }
+  },
+  additionalProperties: false
+}];
diff --git a/lib/rules/no-comment-textnodes.js b/lib/rules/no-comment-textnodes.js
index 7c3cf71d..954999e0 100644
--- a/lib/rules/no-comment-textnodes.js
+++ b/lib/rules/no-comment-textnodes.js
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Comments inside children section of tag should be placed inside braces.
  * @author Ben Vinegar
+ * @deprecated
  */
 'use strict';
 
@@ -8,27 +9,24 @@
 // Rule Definition
 // ------------------------------------------------------------------------------
 
-module.exports = function(context) {
-  function reportLiteralNode(node) {
-    context.report(node, 'Comments inside children section of tag should be placed inside braces');
-  }
-
-  // --------------------------------------------------------------------------
-  // Public
-  // --------------------------------------------------------------------------
+var util = require('util');
+var jsxNoCommentTextnodes = require('./jsx-no-comment-textnodes');
+var isWarnedForDeprecation = false;
 
-  return {
-    Literal: function(node) {
-      if (/^\s*\/(\/|\*)/m.test(node.value)) {
-        // inside component, e.g. <div>literal</div>
-        if (node.parent.type !== 'JSXAttribute' &&
-            node.parent.type !== 'JSXExpressionContainer' &&
-            node.parent.type.indexOf('JSX') !== -1) {
-          reportLiteralNode(node);
-        }
+module.exports = function(context) {
+  return util._extend(jsxNoCommentTextnodes(context), {
+    Program: function() {
+      if (isWarnedForDeprecation || /\=-(f|-format)=/.test(process.argv.join('='))) {
+        return;
       }
+
+      /* eslint-disable no-console */
+      console.log('The react/no-comment-textnodes rule is deprecated. Please ' +
+                  'use the react/jsx-no-comment-textnodes rule instead.');
+      /* eslint-enable no-console */
+      isWarnedForDeprecation = true;
     }
-  };
+  });
 };
 
 module.exports.schema = [{
diff --git a/lib/rules/no-deprecated.js b/lib/rules/no-deprecated.js
index 1f4fb02b..d01bbec5 100644
--- a/lib/rules/no-deprecated.js
+++ b/lib/rules/no-deprecated.js
@@ -99,13 +99,4 @@ module.exports = function(context) {
 
 };
 
-module.exports.schema = [{
-  type: 'object',
-  properties: {
-    react: {
-      type: 'string',
-      pattern: '^[0-9]+\.[0-9]+(\.[0-9]+)?$'
-    }
-  },
-  additionalProperties: false
-}];
+module.exports.schema = [];
diff --git a/lib/rules/no-find-dom-node.js b/lib/rules/no-find-dom-node.js
new file mode 100644
index 00000000..f619f551
--- /dev/null
+++ b/lib/rules/no-find-dom-node.js
@@ -0,0 +1,40 @@
+/**
+ * @fileoverview Prevent usage of findDOMNode
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+  // --------------------------------------------------------------------------
+  // Public
+  // --------------------------------------------------------------------------
+
+  return {
+
+    CallExpression: function(node) {
+      var callee = node.callee;
+
+      var isfindDOMNode =
+        (callee.object && callee.object.callee && callee.object.callee.name === 'findDOMNode') ||
+        (callee.property && callee.property.name === 'findDOMNode')
+      ;
+
+      if (!isfindDOMNode) {
+        return;
+      }
+
+      context.report({
+        node: callee,
+        message: 'Do not use findDOMNode'
+      });
+    }
+  };
+
+};
+
+module.exports.schema = [];
diff --git a/lib/rules/require-extension.js b/lib/rules/require-extension.js
index dde310db..8e25c665 100644
--- a/lib/rules/require-extension.js
+++ b/lib/rules/require-extension.js
@@ -1,77 +1,32 @@
 /**
  * @fileoverview Restrict file extensions that may be required
  * @author Scott Andrews
+ * @deprecated
  */
 'use strict';
 
-var path = require('path');
-
-// ------------------------------------------------------------------------------
-// Constants
-// ------------------------------------------------------------------------------
-
-var DEFAULTS = {
-  extensions: ['.jsx']
-};
-
-var PKG_REGEX = /^[^\.]((?!\/).)*$/;
-
 // ------------------------------------------------------------------------------
 // Rule Definition
 // ------------------------------------------------------------------------------
 
-module.exports = function(context) {
-
-  function isPackage(id) {
-    return PKG_REGEX.test(id);
-  }
-
-  function isRequire(expression) {
-    return expression.callee.name === 'require';
-  }
-
-  function getId(expression) {
-    return expression.arguments[0] && expression.arguments[0].value;
-  }
+var util = require('util');
+var jsxRequireExtension = require('./jsx-require-extension');
+var isWarnedForDeprecation = false;
 
-  function getExtension(id) {
-    return path.extname(id || '');
-  }
-
-  function getExtensionsConfig() {
-    return context.options[0] && context.options[0].extensions || DEFAULTS.extensions;
-  }
-
-  var forbiddenExtensions = getExtensionsConfig().reduce(function (extensions, extension) {
-    extensions[extension] = true;
-    return extensions;
-  }, Object.create(null));
-
-  function isForbiddenExtension(ext) {
-    return ext in forbiddenExtensions;
-  }
-
-  // --------------------------------------------------------------------------
-  // Public
-  // --------------------------------------------------------------------------
-
-  return {
-
-    CallExpression: function(node) {
-      if (isRequire(node)) {
-        var id = getId(node);
-        var ext = getExtension(id);
-        if (!isPackage(id) && isForbiddenExtension(ext)) {
-          context.report({
-            node: node,
-            message: 'Unable to require module with extension \'' + ext + '\''
-          });
-        }
+module.exports = function(context) {
+  return util._extend(jsxRequireExtension(context), {
+    Program: function() {
+      if (isWarnedForDeprecation || /\=-(f|-format)=/.test(process.argv.join('='))) {
+        return;
       }
-    }
-
-  };
 
+      /* eslint-disable no-console */
+      console.log('The react/require-extension rule is deprecated. Please ' +
+                  'use the react/jsx-require-extension rule instead.');
+      /* eslint-enable no-console */
+      isWarnedForDeprecation = true;
+    }
+  });
 };
 
 module.exports.schema = [{
diff --git a/lib/rules/require-optimization.js b/lib/rules/require-optimization.js
index a64afef7..35699bbf 100644
--- a/lib/rules/require-optimization.js
+++ b/lib/rules/require-optimization.js
@@ -8,6 +8,8 @@ var Components = require('../util/Components');
 
 module.exports = Components.detect(function (context, components) {
   var MISSING_MESSAGE = 'Component is not optimized. Please add a shouldComponentUpdate method.';
+  var configuration = context.options[0] || {};
+  var allowDecorators = configuration.allowDecorators || [];
 
   /**
    * Checks to see if our component is decorated by PureRenderMixin via reactMixin
@@ -36,6 +38,30 @@ module.exports = Components.detect(function (context, components) {
     return false;
   };
 
+  /**
+   * Checks to see if our component is custom decorated
+   * @param {ASTNode} node The AST node being checked.
+   * @returns {Boolean} True if node is decorated name with a custom decorated, false if not.
+   */
+  var hasCustomDecorator = function (node) {
+    var allowLenght = allowDecorators.length;
+
+    if (allowLenght && node.decorators && node.decorators.length) {
+      for (var i = 0; i < allowLenght; i++) {
+        for (var j = 0, l = node.decorators.length; j < l; j++) {
+          if (
+            node.decorators[j].expression &&
+            node.decorators[j].expression.name === allowDecorators[i]
+          ) {
+            return true;
+          }
+        }
+      }
+    }
+
+    return false;
+  };
+
   /**
    * Checks if we are declaring a shouldComponentUpdate method
    * @param {ASTNode} node The AST node being checked.
@@ -102,7 +128,7 @@ module.exports = Components.detect(function (context, components) {
     },
 
     ClassDeclaration: function (node) {
-      if (!hasPureRenderDecorator(node)) {
+      if (!(hasPureRenderDecorator(node) || hasCustomDecorator(node))) {
         return;
       }
       markSCUAsDeclared(node);
@@ -148,3 +174,16 @@ module.exports = Components.detect(function (context, components) {
     }
   };
 });
+
+module.exports.schema = [{
+  type: 'object',
+  properties: {
+    allowDecorators: {
+      type: 'array',
+      items: {
+        type: 'string'
+      }
+    }
+  },
+  additionalProperties: false
+}];
diff --git a/lib/rules/wrap-multilines.js b/lib/rules/wrap-multilines.js
index a28b9a73..f1266e2e 100644
--- a/lib/rules/wrap-multilines.js
+++ b/lib/rules/wrap-multilines.js
@@ -1,89 +1,32 @@
 /**
  * @fileoverview Prevent missing parentheses around multilines JSX
  * @author Yannick Croissant
+ * @deprecated
  */
 'use strict';
 
-// ------------------------------------------------------------------------------
-// Constants
-// ------------------------------------------------------------------------------
-
-var DEFAULTS = {
-  declaration: true,
-  assignment: true,
-  return: true
-};
-
 // ------------------------------------------------------------------------------
 // Rule Definition
 // ------------------------------------------------------------------------------
 
-module.exports = function(context) {
-
-  var sourceCode = context.getSourceCode();
+var util = require('util');
+var jsxWrapMultilines = require('./jsx-wrap-multilines');
+var isWarnedForDeprecation = false;
 
-  function isParenthesised(node) {
-    var previousToken = sourceCode.getTokenBefore(node);
-    var nextToken = sourceCode.getTokenAfter(node);
-
-    return previousToken && nextToken &&
-      previousToken.value === '(' && previousToken.range[1] <= node.range[0] &&
-      nextToken.value === ')' && nextToken.range[0] >= node.range[1];
-  }
-
-  function isMultilines(node) {
-    return node.loc.start.line !== node.loc.end.line;
-  }
-
-  function check(node) {
-    if (!node || node.type !== 'JSXElement') {
-      return;
-    }
-
-    if (!isParenthesised(node) && isMultilines(node)) {
-      context.report({
-        node: node,
-        message: 'Missing parentheses around multilines JSX',
-        fix: function(fixer) {
-          return fixer.replaceText(node, '(' + sourceCode.getText(node) + ')');
-        }
-      });
-    }
-  }
-
-  function isEnabled(type) {
-    var userOptions = context.options[0] || {};
-    if (({}).hasOwnProperty.call(userOptions, type)) {
-      return userOptions[type];
-    }
-    return DEFAULTS[type];
-  }
-
-  // --------------------------------------------------------------------------
-  // Public
-  // --------------------------------------------------------------------------
-
-  return {
-
-    VariableDeclarator: function(node) {
-      if (isEnabled('declaration')) {
-        check(node.init);
-      }
-    },
-
-    AssignmentExpression: function(node) {
-      if (isEnabled('assignment')) {
-        check(node.right);
+module.exports = function(context) {
+  return util._extend(jsxWrapMultilines(context), {
+    Program: function() {
+      if (isWarnedForDeprecation || /\=-(f|-format)=/.test(process.argv.join('='))) {
+        return;
       }
-    },
 
-    ReturnStatement: function(node) {
-      if (isEnabled('return')) {
-        check(node.argument);
-      }
+      /* eslint-disable no-console */
+      console.log('The react/wrap-multilines rule is deprecated. Please ' +
+                  'use the react/jsx-wrap-multilines rule instead.');
+      /* eslint-enable no-console */
+      isWarnedForDeprecation = true;
     }
-  };
-
+  });
 };
 
 module.exports.schema = [{
diff --git a/lib/util/Components.js b/lib/util/Components.js
index 72d9e80d..1ae133d3 100644
--- a/lib/util/Components.js
+++ b/lib/util/Components.js
@@ -98,7 +98,9 @@ Components.prototype.list = function() {
       component = this.get(node);
     }
     if (component) {
-      usedPropTypes[this._getId(component.node)] = this._list[i].usedPropTypes;
+      usedPropTypes[this._getId(component.node)] = (this._list[i].usedPropTypes || []).filter(function(propType) {
+        return propType.node.kind !== 'init';
+      });
     }
   }
   // Assign used props in not confident components to the parent component
@@ -199,12 +201,13 @@ function componentRule(rule, context) {
     /**
      * Check if the node is returning JSX
      *
-     * @param {ASTNode} node The AST node being checked (can be a ReturnStatement or an ArrowFunctionExpression).
+     * @param {ASTNode} ASTnode The AST node being checked
      * @param {Boolean} strict If true, in a ternary condition the node must return JSX in both cases
      * @returns {Boolean} True if the node is returning JSX, false if not
      */
-    isReturningJSX: function(node, strict) {
+    isReturningJSX: function(ASTnode, strict) {
       var property;
+      var node = ASTnode;
       switch (node.type) {
         case 'ReturnStatement':
           property = 'argument';
@@ -213,7 +216,11 @@ function componentRule(rule, context) {
           property = 'body';
           break;
         default:
-          return false;
+          node = utils.findReturnStatement(node);
+          if (!node) {
+            return false;
+          }
+          property = 'argument';
       }
 
       var returnsConditionalJSXConsequent =
@@ -248,6 +255,24 @@ function componentRule(rule, context) {
       );
     },
 
+    /**
+     * Find a return statment in the current node
+     *
+     * @param {ASTNode} ASTnode The AST node being checked
+     */
+    findReturnStatement: function(node) {
+      if (!node.value || !node.value.body) {
+        return false;
+      }
+      var i = node.value.body.body.length - 1;
+      for (; i >= 0; i--) {
+        if (node.value.body.body[i].type === 'ReturnStatement') {
+          return node.value.body.body[i];
+        }
+      }
+      return false;
+    },
+
     /**
      * Get the parent component node from the current scope
      *
@@ -305,15 +330,15 @@ function componentRule(rule, context) {
       while (scope) {
         var node = scope.block;
         var isClass = node.type === 'ClassExpression';
-        var isFunction = /Function/.test(node.type); // Ignore non functions
-        var isNotMethod = !node.parent || node.parent.type !== 'MethodDefinition'; // Ignore classes methods
-        var isNotArgument = !node.parent || node.parent.type !== 'CallExpression'; // Ignore arguments (callback, etc.)
-        // Stop moving up if we reach a class
-        if (isClass) {
+        var isFunction = /Function/.test(node.type); // Functions
+        var isMethod = node.parent && node.parent.type === 'MethodDefinition'; // Classes methods
+        var isArgument = node.parent && node.parent.type === 'CallExpression'; // Arguments (callback, etc.)
+        // Stop moving up if we reach a class or an argument (like a callback)
+        if (isClass || isArgument) {
           return null;
         }
-        // Return the node if it is a function that is not a class method or an argument (like a callback)
-        if (isFunction && isNotMethod && isNotArgument) {
+        // Return the node if it is a function that is not a class method
+        if (isFunction && !isMethod) {
           return node;
         }
         scope = scope.upper;
@@ -509,6 +534,8 @@ function componentRule(rule, context) {
       }
       node = utils.getParentComponent();
       if (!node) {
+        var scope = context.getScope();
+        components.add(scope.block, 1);
         return;
       }
       components.add(node, 2);
diff --git a/lib/util/pragma.js b/lib/util/pragma.js
index 479257f4..03f08c39 100644
--- a/lib/util/pragma.js
+++ b/lib/util/pragma.js
@@ -11,9 +11,6 @@ function getFromContext(context) {
   // .eslintrc shared settings (http://eslint.org/docs/user-guide/configuring#adding-shared-settings)
   if (context.settings.react && context.settings.react.pragma) {
     pragma = context.settings.react.pragma;
-  // Deprecated pragma option, here for backward compatibility
-  } else if (context.options[0] && context.options[0].pragma) {
-    pragma = context.options[0].pragma;
   }
   return pragma.split('.')[0];
 }
diff --git a/lib/util/version.js b/lib/util/version.js
index fe2001df..04737cf3 100644
--- a/lib/util/version.js
+++ b/lib/util/version.js
@@ -9,9 +9,6 @@ function getFromContext(context) {
   // .eslintrc shared settings (http://eslint.org/docs/user-guide/configuring#adding-shared-settings)
   if (context.settings.react && context.settings.react.version) {
     confVer = context.settings.react.version;
-  // Deprecated react option, here for backward compatibility
-  } else if (context.options[0] && context.options[0].react) {
-    confVer = context.options[0].react;
   }
   confVer = /^[0-9]+\.[0-9]+$/.test(confVer) ? confVer + '.0' : confVer;
   return confVer.split('.').map(function(part) {
diff --git a/package.json b/package.json
index ac107d16..925c2b1b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-react",
-  "version": "6.0.0-alpha.1",
+  "version": "6.0.0-alpha.2",
   "author": "Yannick Croissant <yannick.croissant+npm@gmail.com>",
   "description": "React specific linting rules for ESLint",
   "main": "index.js",
@@ -24,12 +24,12 @@
   "bugs": "https://github.com/yannickcr/eslint-plugin-react/issues",
   "dependencies": {
     "doctrine": "^1.2.2",
-    "jsx-ast-utils": "^1.2.1"
+    "jsx-ast-utils": "^1.3.1"
   },
   "devDependencies": {
-    "babel-eslint": "6.1.0",
-    "coveralls": "2.11.9",
-    "eslint": "3.0.1",
+    "babel-eslint": "6.1.2",
+    "coveralls": "2.11.11",
+    "eslint": "3.1.1",
     "istanbul": "0.4.4",
     "mocha": "2.5.3"
   },


Test patch:
diff --git a/tests/index.js b/tests/index.js
index 100c0ee6..13c2e6b5 100644
--- a/tests/index.js
+++ b/tests/index.js
@@ -26,5 +26,20 @@ describe('all rule files should be exported by the plugin', function() {
 describe('configurations', function() {
   it('should export a \'recommended\' configuration', function() {
     assert(plugin.configs.recommended);
+    Object.keys(plugin.configs.recommended.rules).forEach(function (configName) {
+      assert.equal(configName.indexOf('react/'), 0);
+      var ruleName = configName.substring('react/'.length);
+      assert(plugin.rules[ruleName]);
+    });
+  });
+  it('should export a \'all\' configuration', function() {
+    assert(plugin.configs.all);
+    Object.keys(plugin.configs.all.rules).forEach(function(configName) {
+      assert.equal(configName.indexOf('react/'), 0);
+      assert.equal(plugin.configs.all.rules[configName], 2);
+    });
+    rules.forEach(function(ruleName) {
+      assert(plugin.configs.all.rules['react/' + ruleName]);
+    });
   });
 });
diff --git a/tests/lib/rules/jsx-closing-bracket-location.js b/tests/lib/rules/jsx-closing-bracket-location.js
index 1f126947..588e89c5 100644
--- a/tests/lib/rules/jsx-closing-bracket-location.js
+++ b/tests/lib/rules/jsx-closing-bracket-location.js
@@ -651,6 +651,102 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
       line: 2,
       column: 8
     }]
+  }, {
+    code: [
+      'const Button = function(props) {',
+      '  return (',
+      '    <Button',
+      '      size={size}',
+      '      onClick={onClick}',
+      '                                    >',
+      '      Button Text',
+      '    </Button>',
+      '  );',
+      '};'
+    ].join('\n'),
+    output: [
+      'const Button = function(props) {',
+      '  return (',
+      '    <Button',
+      '      size={size}',
+      '      onClick={onClick}',
+      '      >',
+      '      Button Text',
+      '    </Button>',
+      '  );',
+      '};'
+    ].join('\n'),
+    options: ['props-aligned'],
+    parserOptions: parserOptions,
+    errors: [{
+      message: messageWithDetails(MESSAGE_PROPS_ALIGNED, 7, false),
+      line: 6,
+      column: 37
+    }]
+  }, {
+    code: [
+      'const Button = function(props) {',
+      '  return (',
+      '    <Button',
+      '      size={size}',
+      '      onClick={onClick}',
+      '                                    >',
+      '      Button Text',
+      '    </Button>',
+      '  );',
+      '};'
+    ].join('\n'),
+    output: [
+      'const Button = function(props) {',
+      '  return (',
+      '    <Button',
+      '      size={size}',
+      '      onClick={onClick}',
+      '    >',
+      '      Button Text',
+      '    </Button>',
+      '  );',
+      '};'
+    ].join('\n'),
+    options: ['tag-aligned'],
+    parserOptions: parserOptions,
+    errors: [{
+      message: messageWithDetails(MESSAGE_TAG_ALIGNED, 5, false),
+      line: 6,
+      column: 37
+    }]
+  }, {
+    code: [
+      'const Button = function(props) {',
+      '  return (',
+      '    <Button',
+      '      size={size}',
+      '      onClick={onClick}',
+      '                                    >',
+      '      Button Text',
+      '    </Button>',
+      '  );',
+      '};'
+    ].join('\n'),
+    output: [
+      'const Button = function(props) {',
+      '  return (',
+      '    <Button',
+      '      size={size}',
+      '      onClick={onClick}',
+      '    >',
+      '      Button Text',
+      '    </Button>',
+      '  );',
+      '};'
+    ].join('\n'),
+    options: ['line-aligned'],
+    parserOptions: parserOptions,
+    errors: [{
+      message: messageWithDetails(MESSAGE_LINE_ALIGNED, 5, false),
+      line: 6,
+      column: 37
+    }]
   }, {
     code: [
       '<Provider',
diff --git a/tests/lib/rules/jsx-curly-spacing.js b/tests/lib/rules/jsx-curly-spacing.js
index fc975a92..456f6b02 100644
--- a/tests/lib/rules/jsx-curly-spacing.js
+++ b/tests/lib/rules/jsx-curly-spacing.js
@@ -182,6 +182,10 @@ ruleTester.run('jsx-curly-spacing', rule, {
     code: '<App foo={3} bar={ {a: 2} } />',
     options: ['never', {spacing: {objectLiterals: 'always'}}],
     parserOptions: parserOptions
+  }, {
+    code: '<div foo={ bar }>{bar}</div>',
+    options: ['always'],
+    parserOptions: parserOptions
   }],
 
   invalid: [{
diff --git a/tests/lib/rules/jsx-filename-extension.js b/tests/lib/rules/jsx-filename-extension.js
index 578682dc..0efa99c0 100644
--- a/tests/lib/rules/jsx-filename-extension.js
+++ b/tests/lib/rules/jsx-filename-extension.js
@@ -22,7 +22,7 @@ var parserOptions = {
 // Code Snippets
 // ------------------------------------------------------------------------------
 
-var withJSX = 'module.exports = function MyComponent() { return <div />; }';
+var withJSX = 'module.exports = function MyComponent() { return <div>\n<div />\n</div>; }';
 var withoutJSX = 'module.exports = {}';
 
 // ------------------------------------------------------------------------------
diff --git a/tests/lib/rules/jsx-handler-names.js b/tests/lib/rules/jsx-handler-names.js
index be8deaf9..111aaa00 100644
--- a/tests/lib/rules/jsx-handler-names.js
+++ b/tests/lib/rules/jsx-handler-names.js
@@ -83,6 +83,16 @@ ruleTester.run('jsx-handler-names', rule, {
       '<TestComponent onChange={props::handleChange} />'
     ].join('\n'),
     parser: 'babel-eslint'
+  }, {
+    code: [
+      '<TestComponent onChange={::props.onChange} />'
+    ].join('\n'),
+    parser: 'babel-eslint'
+  }, {
+    code: [
+      '<TestComponent onChange={props.foo::handleChange} />'
+    ].join('\n'),
+    parser: 'babel-eslint'
   }],
 
   invalid: [{
@@ -109,5 +119,11 @@ ruleTester.run('jsx-handler-names', rule, {
     ].join('\n'),
     parser: 'babel-eslint',
     errors: [{message: 'Handler function for onChange prop key must begin with \'handle\''}]
+  }, {
+    code: [
+      '<TestComponent onChange={props.foo::onChange} />'
+    ].join('\n'),
+    parser: 'babel-eslint',
+    errors: [{message: 'Handler function for onChange prop key must begin with \'handle\''}]
   }]
 });
diff --git a/tests/lib/rules/jsx-no-bind.js b/tests/lib/rules/jsx-no-bind.js
index 5e81d168..02362089 100644
--- a/tests/lib/rules/jsx-no-bind.js
+++ b/tests/lib/rules/jsx-no-bind.js
@@ -72,6 +72,40 @@ ruleTester.run('jsx-no-bind', rule, {
       ].join('\n'),
       options: [{allowBind: true}],
       parser: 'babel-eslint'
+    },
+    // Backbone view with a bind
+    {
+      code: [
+        'var DocumentRow = Backbone.View.extend({',
+        '  tagName: "li",',
+        '  render: function() {',
+        '    this.onTap.bind(this);',
+        '  }',
+        '});'
+      ].join('\n'),
+      parser: 'babel-eslint'
+    },
+    {
+      code: [
+        'const foo = {',
+        '  render: function() {',
+        '    this.onTap.bind(this);',
+        '    return true;',
+        '  }',
+        '};'
+      ].join('\n'),
+      parser: 'babel-eslint'
+    },
+    {
+      code: [
+        'const foo = {',
+        '  render() {',
+        '    this.onTap.bind(this);',
+        '    return true;',
+        '  }',
+        '};'
+      ].join('\n'),
+      parser: 'babel-eslint'
     }
   ],
 
@@ -121,6 +155,30 @@ ruleTester.run('jsx-no-bind', rule, {
       errors: [{message: 'JSX props should not use .bind()'}],
       parser: 'babel-eslint'
     },
+    {
+      code: [
+        'const foo = {',
+        '  render: function() {',
+        '    const click = this.onTap.bind(this);',
+        '    return <div onClick={onClick}>Hello</div>;',
+        '  }',
+        '};'
+      ].join('\n'),
+      errors: [{message: 'JSX props should not use .bind()'}],
+      parser: 'babel-eslint'
+    },
+    {
+      code: [
+        'const foo = {',
+        '  render() {',
+        '    const click = this.onTap.bind(this);',
+        '    return <div onClick={onClick}>Hello</div>;',
+        '  }',
+        '};'
+      ].join('\n'),
+      errors: [{message: 'JSX props should not use .bind()'}],
+      parser: 'babel-eslint'
+    },
 
     // Arrow functions
     {
diff --git a/tests/lib/rules/no-comment-textnodes.js b/tests/lib/rules/jsx-no-comment-textnodes.js
similarity index 97%
rename from tests/lib/rules/no-comment-textnodes.js
rename to tests/lib/rules/jsx-no-comment-textnodes.js
index 2cb700fb..aa688c3b 100644
--- a/tests/lib/rules/no-comment-textnodes.js
+++ b/tests/lib/rules/jsx-no-comment-textnodes.js
@@ -8,7 +8,7 @@
 // Requirements
 // ------------------------------------------------------------------------------
 
-var rule = require('../../../lib/rules/no-comment-textnodes');
+var rule = require('../../../lib/rules/jsx-no-comment-textnodes');
 var RuleTester = require('eslint').RuleTester;
 
 // ------------------------------------------------------------------------------
@@ -16,7 +16,7 @@ var RuleTester = require('eslint').RuleTester;
 // ------------------------------------------------------------------------------
 
 var ruleTester = new RuleTester();
-ruleTester.run('jsx-needs-i18n', rule, {
+ruleTester.run('jsx-no-comment-textnodes', rule, {
 
   valid: [
     {
diff --git a/tests/lib/rules/jsx-no-target-blank.js b/tests/lib/rules/jsx-no-target-blank.js
index 2b624e29..48297eb2 100644
--- a/tests/lib/rules/jsx-no-target-blank.js
+++ b/tests/lib/rules/jsx-no-target-blank.js
@@ -27,7 +27,9 @@ ruleTester.run('jsx-no-target-blank', rule, {
   valid: [
     {code: '<a href="foobar"></a>', parserOptions: parserOptions},
     {code: '<a randomTag></a>', parserOptions: parserOptions},
-    {code: '<a href="foobar" target="_blank" rel="noopener noreferrer"></a>', parserOptions: parserOptions}
+    {code: '<a href="foobar" target="_blank" rel="noopener noreferrer"></a>', parserOptions: parserOptions},
+    {code: '<a target="_blank" {...spreadProps} rel="noopener noreferrer"></a>', parserOptions: parserOptions},
+    {code: '<a target="_blank" rel="noopener noreferrer" {...spreadProps}></a>', parserOptions: parserOptions}
   ],
   invalid: [
     {code: '<a target="_blank"></a>', parserOptions: parserOptions,
diff --git a/tests/lib/rules/require-extension.js b/tests/lib/rules/jsx-require-extension.js
similarity index 95%
rename from tests/lib/rules/require-extension.js
rename to tests/lib/rules/jsx-require-extension.js
index 8fb037c5..952a83a0 100644
--- a/tests/lib/rules/require-extension.js
+++ b/tests/lib/rules/jsx-require-extension.js
@@ -8,7 +8,7 @@
 // Requirements
 // ------------------------------------------------------------------------------
 
-var rule = require('../../../lib/rules/require-extension');
+var rule = require('../../../lib/rules/jsx-require-extension');
 var RuleTester = require('eslint').RuleTester;
 
 // ------------------------------------------------------------------------------
@@ -34,7 +34,7 @@ var REQUIRE_OBJECT = 'require({})';
 // ------------------------------------------------------------------------------
 
 var ruleTester = new RuleTester();
-ruleTester.run('require-extension', rule, {
+ruleTester.run('jsx-require-extension', rule, {
 
   valid: [
     {
diff --git a/tests/lib/rules/jsx-sort-props.js b/tests/lib/rules/jsx-sort-props.js
index 76a62264..53503863 100644
--- a/tests/lib/rules/jsx-sort-props.js
+++ b/tests/lib/rules/jsx-sort-props.js
@@ -33,16 +33,27 @@ var expectedCallbackError = {
   message: 'Callbacks must be listed after all other props',
   type: 'JSXAttribute'
 };
-var expectedShorthandError = {
+var expectedShorthandFirstError = {
   message: 'Shorthand props must be listed before all other props',
   type: 'JSXAttribute'
 };
+var expectedShorthandLastError = {
+  message: 'Shorthand props must be listed after all other props',
+  type: 'JSXAttribute'
+};
 var callbacksLastArgs = [{
   callbacksLast: true
 }];
 var shorthandFirstArgs = [{
   shorthandFirst: true
 }];
+var shorthandLastArgs = [{
+  shorthandLast: true
+}];
+var shorthandAndCallbackLastArgs = [{
+  callbacksLast: true,
+  shorthandLast: true
+}];
 var ignoreCaseArgs = [{
   ignoreCase: true
 }];
@@ -67,7 +78,13 @@ ruleTester.run('jsx-sort-props', rule, {
     // Sorting shorthand props before others
     {code: '<App a b="b" />;', options: shorthandFirstArgs, parserOptions: parserOptions},
     {code: '<App z a="a" />;', options: shorthandFirstArgs, parserOptions: parserOptions},
-    {code: '<App x y z a="a" b="b" />;', options: shorthandFirstArgs, parserOptions: parserOptions}
+    {code: '<App x y z a="a" b="b" />;', options: shorthandFirstArgs, parserOptions: parserOptions},
+    {code: '<App a="a" b="b" x y z />;', options: shorthandLastArgs, parserOptions: parserOptions},
+    {
+      code: '<App a="a" b="b" x y z onBar onFoo />;',
+      options: shorthandAndCallbackLastArgs,
+      parserOptions: parserOptions
+    }
   ],
   invalid: [
     {code: '<App b a />;', errors: [expectedError], parserOptions: parserOptions},
@@ -91,10 +108,20 @@ ruleTester.run('jsx-sort-props', rule, {
       parserOptions: parserOptions
     }, {
       code: '<App a="a" b />;',
-      errors: [expectedShorthandError],
+      errors: [expectedShorthandFirstError],
       options: shorthandFirstArgs,
       parserOptions: parserOptions
     },
-    {code: '<App z x a="a" />;', errors: [expectedError], options: shorthandFirstArgs, parserOptions: parserOptions}
+    {code: '<App z x a="a" />;', errors: [expectedError], options: shorthandFirstArgs, parserOptions: parserOptions}, {
+      code: '<App b a="a" />;',
+      errors: [expectedShorthandLastError],
+      options: shorthandLastArgs,
+      parserOptions: parserOptions
+    }, {
+      code: '<App a="a" onBar onFoo z x />;',
+      errors: [shorthandAndCallbackLastArgs],
+      options: shorthandLastArgs,
+      parserOptions: parserOptions
+    }
   ]
 });
diff --git a/tests/lib/rules/jsx-uses-react.js b/tests/lib/rules/jsx-uses-react.js
index a5777693..0974ef8a 100644
--- a/tests/lib/rules/jsx-uses-react.js
+++ b/tests/lib/rules/jsx-uses-react.js
@@ -37,7 +37,6 @@ ruleTester.run('no-unused-vars', rule, {
     {code: '/*eslint jsx-uses-react:1*/ var React; <div />;', parserOptions: parserOptions},
     {code: '/*eslint jsx-uses-react:1*/ var React; (function () { <div /> })();', parserOptions: parserOptions},
     {code: '/*eslint jsx-uses-react:1*/ /** @jsx Foo */ var Foo; <div />;', parserOptions: parserOptions},
-    {code: '/*eslint jsx-uses-react:[1,{"pragma":"Foo"}]*/ var Foo; <div />;', parserOptions: parserOptions},
     {code: '/*eslint jsx-uses-react:1*/ var Foo; <div />;', settings: settings, parserOptions: parserOptions}
   ],
   invalid: [
diff --git a/tests/lib/rules/wrap-multilines.js b/tests/lib/rules/jsx-wrap-multilines.js
similarity index 97%
rename from tests/lib/rules/wrap-multilines.js
rename to tests/lib/rules/jsx-wrap-multilines.js
index e169ff73..8fe41fd7 100644
--- a/tests/lib/rules/wrap-multilines.js
+++ b/tests/lib/rules/jsx-wrap-multilines.js
@@ -8,7 +8,7 @@
 // Requirements
 // ------------------------------------------------------------------------------
 
-var rule = require('../../../lib/rules/wrap-multilines');
+var rule = require('../../../lib/rules/jsx-wrap-multilines');
 var RuleTester = require('eslint').RuleTester;
 
 var parserOptions = {
@@ -78,7 +78,7 @@ var ASSIGNMENT_NO_PAREN = '\
 // ------------------------------------------------------------------------------
 
 var ruleTester = new RuleTester();
-ruleTester.run('wrap-multilines', rule, {
+ruleTester.run('jsx-wrap-multilines', rule, {
 
   valid: [
     {
diff --git a/tests/lib/rules/no-deprecated.js b/tests/lib/rules/no-deprecated.js
index c309acdb..6a775767 100644
--- a/tests/lib/rules/no-deprecated.js
+++ b/tests/lib/rules/no-deprecated.js
@@ -12,12 +12,6 @@
 var rule = require('../../../lib/rules/no-deprecated');
 var RuleTester = require('eslint').RuleTester;
 
-var settings = {
-  react: {
-    pragma: 'Foo'
-  }
-};
-
 require('babel-eslint');
 
 // ------------------------------------------------------------------------------
@@ -38,26 +32,24 @@ ruleTester.run('no-deprecated', rule, {
     'ReactDOMServer.renderToString(element);',
     'ReactDOMServer.renderToStaticMarkup(element);',
     // Deprecated in a later version
-    {code: 'React.renderComponent()', options: [{react: '0.11.0'}]},
     {code: 'React.renderComponent()', settings: {react: {version: '0.11.0'}}}
   ],
 
   invalid: [{
     code: 'React.renderComponent()',
-    options: [{react: '0.12.0'}],
+    settings: {react: {version: '0.12.0'}},
     errors: [{
       message: 'React.renderComponent is deprecated since React 0.12.0, use React.render instead'
     }]
   }, {
     code: 'Foo.renderComponent()',
-    options: [{react: '0.12.0'}],
-    settings: settings,
+    settings: {react: {pragma: 'Foo', version: '0.12.0'}},
     errors: [{
       message: 'Foo.renderComponent is deprecated since React 0.12.0, use Foo.render instead'
     }]
   }, {
     code: '/** @jsx Foo */ Foo.renderComponent()',
-    options: [{react: '0.12.0'}],
+    settings: {react: {version: '0.12.0'}},
     errors: [{
       message: 'Foo.renderComponent is deprecated since React 0.12.0, use Foo.render instead'
     }]
diff --git a/tests/lib/rules/no-find-dom-node.js b/tests/lib/rules/no-find-dom-node.js
new file mode 100644
index 00000000..3cecacf3
--- /dev/null
+++ b/tests/lib/rules/no-find-dom-node.js
@@ -0,0 +1,115 @@
+/**
+ * @fileoverview Prevent usage of findDOMNode
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require('../../../lib/rules/no-find-dom-node');
+var RuleTester = require('eslint').RuleTester;
+
+var parserOptions = {
+  ecmaVersion: 6,
+  ecmaFeatures: {
+    jsx: true
+  }
+};
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+var ruleTester = new RuleTester();
+ruleTester.run('no-find-dom-node', rule, {
+
+  valid: [{
+    code: [
+      'var Hello = function() {};'
+    ].join('\n'),
+    parserOptions: parserOptions
+  }, {
+    code: [
+      'var Hello = React.createClass({',
+      '  render: function() {',
+      '    return <div>Hello</div>;',
+      '  }',
+      '});'
+    ].join('\n'),
+    parserOptions: parserOptions
+  }, {
+    code: [
+      'var Hello = React.createClass({',
+      '  componentDidMount: function() {',
+      '    someNonMemberFunction(arg);',
+      '    this.someFunc = React.findDOMNode;',
+      '  },',
+      '  render: function() {',
+      '    return <div>Hello</div>;',
+      '  }',
+      '});'
+    ].join('\n'),
+    parserOptions: parserOptions
+  }, {
+    code: [
+      'var Hello = React.createClass({',
+      '  componentDidMount: function() {',
+      '    React.someFunc(this);',
+      '  },',
+      '  render: function() {',
+      '    return <div>Hello</div>;',
+      '  }',
+      '});'
+    ].join('\n'),
+    parserOptions: parserOptions
+  }],
+
+  invalid: [{
+    code: [
+      'var Hello = React.createClass({',
+      '  componentDidMount: function() {',
+      '    React.findDOMNode(this).scrollIntoView();',
+      '  },',
+      '  render: function() {',
+      '    return <div>Hello</div>;',
+      '  }',
+      '});'
+    ].join('\n'),
+    parserOptions: parserOptions,
+    errors: [{
+      message: 'Do not use findDOMNode'
+    }]
+  }, {
+    code: [
+      'var Hello = React.createClass({',
+      '  componentDidMount: function() {',
+      '    ReactDOM.findDOMNode(this).scrollIntoView();',
+      '  },',
+      '  render: function() {',
+      '    return <div>Hello</div>;',
+      '  }',
+      '});'
+    ].join('\n'),
+    parserOptions: parserOptions,
+    errors: [{
+      message: 'Do not use findDOMNode'
+    }]
+  }, {
+    code: [
+      'class Hello extends Component {',
+      '  componentDidMount() {',
+      '    findDOMNode(this).scrollIntoView();',
+      '  }',
+      '  render() {',
+      '    return <div>Hello</div>;',
+      '  }',
+      '};'
+    ].join('\n'),
+    parserOptions: parserOptions,
+    errors: [{
+      message: 'Do not use findDOMNode'
+    }]
+  }]
+});
diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js
index 3aa45fd0..7405a61e 100644
--- a/tests/lib/rules/prop-types.js
+++ b/tests/lib/rules/prop-types.js
@@ -1271,6 +1271,15 @@ ruleTester.run('prop-types', rule, {
         '}'
       ].join('\n'),
       parserOptions: parserOptions
+    }, {
+      code: [
+        'function Hello({names}) {',
+        '  return names.map((name) => {',
+        '    return <div>{name}</div>;',
+        '  });',
+        '}'
+      ].join('\n'),
+      parser: 'babel-eslint'
     }
   ],
 
@@ -2221,6 +2230,17 @@ ruleTester.run('prop-types', rule, {
       errors: [
         {message: '\'name.constructor.firstname\' is missing in props validation'}
       ]
+    }, {
+      code: [
+        'function SomeComponent({bar}) {',
+        '  function f({foo}) {}',
+        '  return <div className={f()}>{bar}</div>;',
+        '}'
+      ].join('\n'),
+      parserOptions: parserOptions,
+      errors: [
+        {message: '\'bar\' is missing in props validation'}
+      ]
     }
   ]
 });
diff --git a/tests/lib/rules/require-optimization.js b/tests/lib/rules/require-optimization.js
index a973d613..09f555e1 100644
--- a/tests/lib/rules/require-optimization.js
+++ b/tests/lib/rules/require-optimization.js
@@ -77,6 +77,17 @@ ruleTester.run('react-require-optimization', rule, {
     ].join('\n'),
     parser: 'babel-eslint',
     parserOptions: parserOptions
+  }, {
+    code: [
+      '@bar',
+      '@pureRender',
+      '@foo',
+      'class DecoratedComponent extends Component {' +
+      '}'
+    ].join('\n'),
+    parser: 'babel-eslint',
+    options: [{allowDecorators: ['renderPure', 'pureRender']}],
+    parserOptions: parserOptions
   }],
 
   invalid: [{
@@ -129,5 +140,19 @@ ruleTester.run('react-require-optimization', rule, {
     }],
     parser: 'babel-eslint',
     parserOptions: parserOptions
+  }, {
+    code: [
+      '@bar',
+      '@pure',
+      '@foo',
+      'class DecoratedComponent extends Component {' +
+      '}'
+    ].join('\n'),
+    errors: [{
+      message: MESSAGE
+    }],
+    parser: 'babel-eslint',
+    options: [{allowDecorators: ['renderPure', 'pureRender']}],
+    parserOptions: parserOptions
   }]
 });