Skip to content

Commit

Permalink
Merge branch 'master' into curly-spacing-for-children
Browse files Browse the repository at this point in the history
  • Loading branch information
fatfisz committed Jun 11, 2017
2 parents 0c7a226 + 8e547ea commit 912ec6b
Show file tree
Hide file tree
Showing 27 changed files with 1,306 additions and 63 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"env": {
"node": true
"es6": true,
"node": true
},
parserOptions: {
ecmaVersion: 6,
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# gitignore

lib-cov
*.seed
*.log
Expand All @@ -15,3 +17,7 @@ build
node_modules
npm-debug.log
sftp-config.json

# Only apps should have lockfiles
yarn.lock
package-lock.json
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#

# List of supported rules

* [react/default-props-match-prop-types](docs/rules/default-props-match-prop-types): Prevent extraneous defaultProps on components
* [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
* [react/forbid-component-props](docs/rules/forbid-component-props.md): Forbid certain props on Components
* [react/forbid-elements](docs/rules/forbid-elements.md): Forbid certain elements
Expand All @@ -96,6 +97,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
* [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-redundant-should-component-update](docs/rules/no-redundant-should-component-update.md): Prevent usage of `shouldComponentUpdate` when extending React.PureComponent
* [react/no-render-return-value](docs/rules/no-render-return-value.md): Prevent usage of the return value of `React.render`
* [react/no-set-state](docs/rules/no-set-state.md): Prevent usage of `setState`
* [react/no-string-refs](docs/rules/no-string-refs.md): Prevent using string references in `ref` attribute.
Expand All @@ -108,7 +110,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
* [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-default-props](docs/rules/require-default-props.md): Enforce a defaultProps definition for every prop that is not a required prop
* [react/require-optimization](docs/rules/require-optimization.md): Enforce React components to have a shouldComponentUpdate method
* [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 (fixable)
* [react/sort-comp](docs/rules/sort-comp.md): Enforce component methods order
Expand All @@ -120,6 +122,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#

* [react/jsx-boolean-value](docs/rules/jsx-boolean-value.md): Enforce boolean attributes notation in JSX (fixable)
* [react/jsx-closing-bracket-location](docs/rules/jsx-closing-bracket-location.md): Validate closing bracket location in JSX (fixable)
* [react/jsx-closing-tag-location](docs/rules/jsx-closing-tag-location.md): Validate closing tag location in JSX (fixable)
* [react/jsx-curly-spacing](docs/rules/jsx-curly-spacing.md): Enforce or disallow spaces inside of curly braces in JSX attributes and expressions (fixable)
* [react/jsx-equals-spacing](docs/rules/jsx-equals-spacing.md): Enforce or disallow spaces around equal signs in JSX attributes (fixable)
* [react/jsx-filename-extension](docs/rules/jsx-filename-extension.md): Restrict file extensions that may contain JSX
Expand Down Expand Up @@ -152,7 +155,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#

## Recommended

This plugin exports a `recommended` configuration that enforce React good practices.
This plugin exports a `recommended` configuration that enforces React good practices.

To enable this configuration use the `extends` property in your `.eslintrc` config file:

Expand Down
38 changes: 38 additions & 0 deletions docs/rules/jsx-closing-tag-location.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Validate closing tag location in JSX (react/jsx-closing-tag-location)

Enforce the closing tag location for multiline JSX elements.

**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.

## Rule Details

This rule checks all JSX multiline elements with children (non-self-closing) and verifies the location of the closing tag. The expectation is that the closing tag is aligned with the opening tag on its own line.

The following patterns are considered warnings:

```jsx
<Hello>
marklar
</Hello>
```

```jsx
<Hello>
marklar</Hello>
```

The following are not considered warnings:

```jsx
<Hello>
marklar
</Hello>
```

```jsx
<Hello>marklar</Hello>
```

## When not to use

If you do not care about closing tag JSX alignment then you can disable this rule.
2 changes: 1 addition & 1 deletion docs/rules/jsx-no-bind.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# No `.bind()` or Arrow Functions in JSX Props (react/jsx-no-bind)

A `bind` call or [arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) in a JSX prop will create a brand new function on every single render. This is bad for performance, as it will result in the garbage collector being invoked way more than is necessary.
A `bind` call or [arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) in a JSX prop will create a brand new function on every single render. This is bad for performance, as it will result in the garbage collector being invoked way more than is necessary. It may also cause unnecessary re-renders if a brand new function is passed as a prop to a component that uses reference equality check on the prop to determine if it should update.

## Rule Details

Expand Down
8 changes: 5 additions & 3 deletions docs/rules/jsx-no-target-blank.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Prevent usage of unsafe `target='_blank'` (react/jsx-no-target-blank)

When creating a JSX element that has an a tag, it is often desired to have
When creating a JSX element that has an `a` tag, it is often desired to have
the link open in a new tab using the `target='_blank'` attribute. Using this
attribute unaccompanied by `rel='noreferrer noopener'`, however, is a severe
security vulnerability ([see here for more details](https://mathiasbynens.github.io/rel-noopener))
Expand All @@ -11,14 +11,16 @@ This rules requires that you accompany all `target='_blank'` attributes with `re
The following patterns are considered errors:

```jsx
var Hello = <a target='_blank'></a>
var Hello = <a target='_blank' href="http://example.com/"></a>
```

The following patterns are not considered errors:

```jsx
var Hello = <p target='_blank'></p>
var Hello = <a target='_blank' rel='noopener noreferrer'></a>
var Hello = <a target='_blank' rel='noopener noreferrer' href="http://example.com"></a>
var Hello = <a target='_blank' href="relative/path/in/the/host"></a>
var Hello = <a target='_blank' href="/absolute/path/in/the/host"></a>
var Hello = <a></a>
```

Expand Down
64 changes: 64 additions & 0 deletions docs/rules/no-redundant-should-component-update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Prevent usage of shouldComponentUpdate when extending React.PureComponent (react/no-redundant-should-component-update)

Warns if you have `shouldComponentUpdate` defined when defining a component that extends React.PureComponent.
While having `shouldComponentUpdate` will still work, it becomes pointless to extend PureComponent.

## Rule Details

The following patterns are considered warnings:

```jsx
class Foo extends React.PureComponent {
shouldComponentUpdate() {
// do check
}

render() {
return <div>Radical!</div>
}
}

function Bar() {
return class Baz extends React.PureComponent {
shouldComponentUpdate() {
// do check
}

render() {
return <div>Groovy!</div>
}
}
}
```

The following patterns are not considered warnings:

```jsx
class Foo extends React.Component {
shouldComponentUpdate() {
// do check
}

render() {
return <div>Radical!</div>
}
}

function Bar() {
return class Baz extends React.Component {
shouldComponentUpdate() {
// do check
}

render() {
return <div>Groovy!</div>
}
}
}

class Qux extends React.PureComponent {
render() {
return <div>Tubular!</div>
}
}
```
4 changes: 2 additions & 2 deletions docs/rules/no-will-update-set-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ The following patterns are considered warnings:

```jsx
var Hello = createReactClass({
componentDidUpdate: function() {
componentWillUpdate: function() {
this.setState({
name: this.props.name.toUpperCase()
});
Expand All @@ -76,7 +76,7 @@ var Hello = createReactClass({

```jsx
var Hello = createReactClass({
componentDidUpdate: function() {
componentWillUpdate: function() {
this.prepareHandler(function callback(newName) {
this.setState({
name: newName
Expand Down
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var allRules = {
'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-closing-tag-location': require('./lib/rules/jsx-closing-tag-location'),
'jsx-space-before-closing': require('./lib/rules/jsx-space-before-closing'),
'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
'forbid-component-props': require('./lib/rules/forbid-component-props'),
Expand All @@ -62,7 +63,8 @@ var allRules = {
'no-unused-prop-types': require('./lib/rules/no-unused-prop-types'),
'no-children-prop': require('./lib/rules/no-children-prop'),
'void-dom-elements-no-children': require('./lib/rules/void-dom-elements-no-children'),
'jsx-tag-spacing': require('./lib/rules/jsx-tag-spacing')
'jsx-tag-spacing': require('./lib/rules/jsx-tag-spacing'),
'no-redundant-should-component-update': require('./lib/rules/no-redundant-should-component-update')
};

function filterRules(rules, predicate) {
Expand Down
87 changes: 87 additions & 0 deletions lib/rules/jsx-closing-tag-location.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* @fileoverview Validate closing tag location in JSX
* @author Ross Solomon
*/
'use strict';

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: 'Validate closing tag location for multiline JSX',
category: 'Stylistic Issues',
recommended: false
},
fixable: 'whitespace'
},

create: function(context) {
var sourceCode = context.getSourceCode();

/**
* Checks if the node is the first in its line, excluding whitespace.
* @param {ASTNode} node The node to check
* @return {Boolean} true if its the first node in its line
*/
function isNodeFirstInLine(node) {
let token = node;
let lines;
do {
token = sourceCode.getTokenBefore(token);
lines = token.type === 'JSXText'
? token.value.split('\n')
: null;
} while (
token.type === 'JSXText' &&
/^\s*$/.test(lines[lines.length - 1])
);

var startLine = node.loc.start.line;
var endLine = token ? token.loc.end.line : -1;
return startLine !== endLine;
}

return {
JSXClosingElement: function(node) {
if (!node.parent) {
return;
}

const opening = node.parent.openingElement;
if (opening.loc.start.line === node.loc.start.line) {
return;
}

if (opening.loc.start.column === node.loc.start.column) {
return;
}

let message;
if (!isNodeFirstInLine(node)) {
message = 'Closing tag of a multiline JSX expression must be on its own line.';
} else {
message = 'Expected closing tag to match indentation of opening.';
}

context.report({
node: node,
loc: node.loc,
message,
fix: function(fixer) {
const indent = Array(opening.loc.start.column + 1).join(' ');
if (isNodeFirstInLine(node)) {
return fixer.replaceTextRange(
[node.start - node.loc.start.column, node.start],
indent
);
}

return fixer.insertTextBefore(node, `\n${indent}`);
}
});
}
};
}
};
48 changes: 30 additions & 18 deletions lib/rules/jsx-no-target-blank.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,31 @@
// Rule Definition
// ------------------------------------------------------------------------------

function isTargetBlank(attr) {
return attr.name.name === 'target' &&
attr.value.type === 'Literal' &&
attr.value.value.toLowerCase() === '_blank';
}

function hasExternalLink(element) {
return element.attributes.find(function (attr) {
return attr.name &&
attr.name.name === 'href' &&
attr.value.type === 'Literal' &&
/^(?:\w+:|\/\/)/.test(attr.value.value);
});
}

function hasSecureRel(element) {
return element.attributes.find(function (attr) {
if (attr.name.name === 'rel') {
var tags = attr.value.type === 'Literal' && attr.value.value.toLowerCase().split(' ');
return !tags || (tags.indexOf('noopener') >= 0 && tags.indexOf('noreferrer') >= 0);
}
return false;
});
}

module.exports = {
meta: {
docs: {
Expand All @@ -26,25 +51,12 @@ module.exports = {
}

if (
node.name.name === 'target' &&
node.value.type === 'Literal' &&
node.value.value.toLowerCase() === '_blank'
isTargetBlank(node) &&
hasExternalLink(node.parent) &&
!hasSecureRel(node.parent)
) {
var relFound = false;
var attrs = node.parent.attributes;
for (var idx in attrs) {
if (attrs[idx].name && attrs[idx].name.name === 'rel') {
var tags = attrs[idx].value.type === 'Literal' && attrs[idx].value.value.toLowerCase().split(' ');
if (!tags || (tags.indexOf('noopener') >= 0 && tags.indexOf('noreferrer') >= 0)) {
relFound = true;
break;
}
}
}
if (!relFound) {
context.report(node, 'Using target="_blank" without rel="noopener noreferrer" ' +
'is a security risk: see https://mathiasbynens.github.io/rel-noopener');
}
context.report(node, 'Using target="_blank" without rel="noopener noreferrer" ' +
'is a security risk: see https://mathiasbynens.github.io/rel-noopener');
}
}
};
Expand Down

0 comments on commit 912ec6b

Please sign in to comment.