Skip to content

Add no-deprecated-methods rule to prevents usage of deprecated component lifecycle methods #1748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 39 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
12d1376
Added `no-component-will-receive-props` rule
sergei-startsev Mar 28, 2018
daa076f
`no-component-will-receive-props` => `no-deprecated-methods`, added docs
sergei-startsev Mar 28, 2018
5c7ab5f
Fixed jsdoc comments for `no-deprecated-methods` rule
sergei-startsev Mar 28, 2018
323dba2
Adjusted no-deprecated rule for React 16.3.0
sergei-startsev Mar 29, 2018
c8008e0
Merge remote-tracking branch 'origin/master'
sergei-startsev Mar 29, 2018
69b1317
Fix default-props-match-prop-types type annotations detection with ES…
yannickcr May 21, 2018
31e4f33
Fix require-default-props type annotations detection with ESLint 3
yannickcr May 21, 2018
677e1bd
Fix jsx-sort-props auto fix with ESLint 3
yannickcr May 21, 2018
cb4d21e
Run tests against different ESLint versions on Travis
yannickcr May 21, 2018
589231a
Add SpreadElement support to boolean-prop-naming
yannickcr May 28, 2018
e6e8955
Add SpreadElement support to default-props-match-prop-types
yannickcr May 28, 2018
3762258
Add JSXText support to jsx-child-element-spacing
yannickcr May 28, 2018
c734901
Add JSXText support to jsx-curly-brace-presence
yannickcr May 28, 2018
0a6cb51
Add JSXText support to jsx-indent
yannickcr May 28, 2018
bd6caf0
Add JSXText support to jsx-no-literals
yannickcr May 28, 2018
0a028e4
Add JSXText support to jsx-one-expression-per-line
yannickcr May 28, 2018
ed3370b
Add SpreadElement and JSXText support to no-danger-with-children
yannickcr May 28, 2018
ce1fec7
Add JSXText support to no-unescaped-entities
yannickcr May 28, 2018
b3ed9d6
Add SpreadElement and RestElement support to no-unused-state
yannickcr May 28, 2018
632941c
Add SpreadElement support to require-default-props
yannickcr May 28, 2018
29c248d
Add JSXText support to self-closing-comp
yannickcr May 28, 2018
f2d8729
Add SpreadElement support to sort-prop-types
yannickcr May 28, 2018
9d4e27a
Ignore no-typos test that only works with ESLint 5
yannickcr May 28, 2018
ab6b41a
Remove deprecated experimentalObjectRestSpread option from tests conf…
yannickcr May 29, 2018
0d34198
Update CHANGELOG and bump version
yannickcr Jun 3, 2018
9e83b7c
[Deps] update `has`, `prop-types`, `doctrine`
ljharb Jun 4, 2018
df430ee
[Dev Deps] update `babel-eslint`, `coveralls`, `eslint`, `mocha`
ljharb Jun 4, 2018
ac10288
Update CHANGELOG and bump version
ljharb Jun 4, 2018
7c5b8c4
[Docs] Typo fixes in jsx-no-target-blank
ferhatelmas Jun 4, 2018
d779865
Merge pull request #1805 from ferhatelmas/patch-1
ljharb Jun 4, 2018
fee2d1a
Fix crash in no-unused-prop-types when encountering mixed union and i…
yannickcr Jun 4, 2018
c82746c
Fix crash in no-unused-prop-types when encountering impossible inters…
yannickcr Jun 4, 2018
fcff54e
Allow LHS in destructuring-assignment
alexzherdev Jun 14, 2018
b6e911b
Remove unnecessary babel-eslint
alexzherdev Jun 14, 2018
f924414
Fix static propTypes handling in no-typos
alexzherdev Jun 14, 2018
2a674b0
Merge pull request #1825 from alexzherdev/1728-destructuring-state-as…
ljharb Jun 14, 2018
d68b9e8
Remove unnecessary babel-eslint and fix typo
alexzherdev Jun 15, 2018
48e386d
Merge pull request #1827 from alexzherdev/1677-no-typos-static-proptypes
ljharb Jun 15, 2018
278e76a
Merge remote-tracking branch 'origin/master'
sergei-startsev Jun 17, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ node_js:
- '4'
before_install:
- 'nvm install-latest-npm'
before_script:
- 'if [ -n "${ESLINT-}" ]; then npm install --no-save "eslint@${ESLINT}" ; fi'
script:
- 'if [ -n "${PRETEST-}" ]; then npm run pretest ; fi'
- 'if [ -n "${TEST-}" ]; then npm run unit-test ; fi'
Expand All @@ -21,7 +23,28 @@ matrix:
include:
- node_js: 'lts/*'
env: PRETEST=true
- node_js: '9'
env: TEST=true ESLINT=next
- node_js: '8'
env: TEST=true ESLINT=next
- node_js: '7'
env: TEST=true ESLINT=next
- node_js: '6'
env: TEST=true ESLINT=next
- node_js: '9'
env: TEST=true ESLINT=3
- node_js: '8'
env: TEST=true ESLINT=3
- node_js: '7'
env: TEST=true ESLINT=3
- node_js: '6'
env: TEST=true ESLINT=3
- node_js: '5'
env: TEST=true ESLINT=3
- node_js: '4'
env: TEST=true ESLINT=3
allow_failures:
- node_js: '9'
- node_js: '7'
- node_js: '5'
- env: TEST=true ESLINT=next
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,35 @@ 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).

## [7.9.1] - 2018-06-03
* Nothing was fixed; this is a republish with some updated deps. ([#1804][] @ljharb)

[#1804]: https://github.com/yannickcr/eslint-plugin-react/issues/1804

## [7.9.0] - 2018-06-03
### Added
* Add [`jsx-props-no-multi-spaces`][] rule ([#1755][] @ThiefMaster)
* Add `first` option to [`jsx-indent-props`][] ([#398][] @ThiefMaster)
* Add `enforceDynamicLinks` option to [`jsx-no-target-blank`][] ([#1737][] @kenearley)

### Fixed
* Fix static lifecycle methods validation in [`sort-comp`][] ([#1793][] @lynxtaa)
* Fix crash in [`no-typos`][] when encountering anonymous react imports ([#1796][] @jsg2021)
* Fix ESLint 3 support ([#1779][])

### Changed
* Documentation improvements ([#1794][] @lencioni)
* Update Travis CI configuration to test on multiple ESLint verions

[7.9.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v7.8.2...v7.9.0
[#1755]: https://github.com/yannickcr/eslint-plugin-react/pull/1755
[#398]: https://github.com/yannickcr/eslint-plugin-react/issues/398
[#1737]: https://github.com/yannickcr/eslint-plugin-react/issues/1737
[#1793]: https://github.com/yannickcr/eslint-plugin-react/issues/1793
[#1796]: https://github.com/yannickcr/eslint-plugin-react/pull/1796
[#1779]: https://github.com/yannickcr/eslint-plugin-react/issues/1779
[#1794]: https://github.com/yannickcr/eslint-plugin-react/pull/1794

## [7.8.2] - 2018-05-13
### Fixed
* Fix crash in [`boolean-prop-naming`][] when encountering a required shape prop type ([#1791][] @pcorpet)
Expand Down Expand Up @@ -2205,3 +2234,4 @@ If you're still not using React 15 you can keep the old behavior by setting the
[`no-this-in-sfc`]: docs/rules/no-this-in-sfc.md
[`jsx-sort-default-props`]: docs/rules/jsx-sort-default-props.md
[`jsx-max-depth`]: docs/rules/jsx-max-depth.md
[`jsx-props-no-multi-spaces`]: docs/rules/jsx-props-no-multi-spaces.md
4 changes: 2 additions & 2 deletions docs/rules/jsx-no-target-blank.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ This rule aims to prevent user generated links from creating security vulerabili

There are two main options for the rule:

* `{"enforceDynamicLinks": "always"}` enforces the rule if the href is a dyanamic link (default)
* `{"enforceDynamicLinks": "never"}` does not enforce the rule if the href is a dyamic link
* `{"enforceDynamicLinks": "always"}` enforces the rule if the href is a dynamic link (default)
* `{"enforceDynamicLinks": "never"}` does not enforce the rule if the href is a dynamic link


### always (default)
Expand Down
7 changes: 4 additions & 3 deletions lib/rules/boolean-prop-naming.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ module.exports = {
* @param {Object} node The node we're getting the name of
*/
function getPropKey(node) {
// Check for `ExperimentalSpreadProperty` so we can skip validation of those fields.
// Otherwise it will look for `node.value.property` which doesn't exist and breaks Eslint.
if (node.type === 'ExperimentalSpreadProperty') {
// Check for `ExperimentalSpreadProperty` (ESLint 3/4) and `SpreadElement` (ESLint 5)
// so we can skip validation of those fields.
// Otherwise it will look for `node.value.property` which doesn't exist and breaks ESLint.
if (node.type === 'ExperimentalSpreadProperty' || node.type === 'SpreadElement') {
return null;
}
if (node.value.property) {
Expand Down
44 changes: 40 additions & 4 deletions lib/rules/default-props-match-prop-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ module.exports = {
const configuration = context.options[0] || {};
const allowRequiredDefaults = configuration.allowRequiredDefaults || false;
const propWrapperFunctions = new Set(context.settings.propWrapperFunctions || []);
// Used to track the type annotations in scope.
// Necessary because babel's scopes do not track type annotations.
let stack = null;

/**
* Try to resolve the node passed in to a variable in the current scope. If the node passed in is not
Expand All @@ -62,6 +65,22 @@ module.exports = {
return node;
}

/**
* Helper for accessing the current scope in the stack.
* @param {string} key The name of the identifier to access. If omitted, returns the full scope.
* @param {ASTNode} value If provided sets the new value for the identifier.
* @returns {Object|ASTNode} Either the whole scope or the ASTNode associated with the given identifier.
*/
function typeScope(key, value) {
if (arguments.length === 0) {
return stack[stack.length - 1];
} else if (arguments.length === 1) {
return stack[stack.length - 1][key];
}
stack[stack.length - 1][key] = value;
return value;
}

/**
* Tries to find the definition of a GenericTypeAnnotation in the current scope.
* @param {ASTNode} node The node GenericTypeAnnotation node to resolve.
Expand All @@ -72,7 +91,7 @@ module.exports = {
return null;
}

return variableUtil.findVariableByName(context, node.id.name);
return variableUtil.findVariableByName(context, node.id.name) || typeScope(node.id.name);
}

function resolveUnionTypeAnnotation(node) {
Expand All @@ -92,7 +111,7 @@ module.exports = {
* @returns {Object[]} Array of PropType object representations, to be consumed by `addPropTypesToComponent`.
*/
function getPropTypesFromObjectExpression(objectExpression) {
const props = objectExpression.properties.filter(property => property.type !== 'ExperimentalSpreadProperty');
const props = objectExpression.properties.filter(property => property.type !== 'ExperimentalSpreadProperty' && property.type !== 'SpreadElement');

return props.map(property => ({
name: property.key.name,
Expand Down Expand Up @@ -190,7 +209,7 @@ module.exports = {
* from this ObjectExpression can't be resolved.
*/
function getDefaultPropsFromObjectExpression(objectExpression) {
const hasSpread = objectExpression.properties.find(property => property.type === 'ExperimentalSpreadProperty');
const hasSpread = objectExpression.properties.find(property => property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement');

if (hasSpread) {
return 'unresolved';
Expand Down Expand Up @@ -524,7 +543,7 @@ module.exports = {

// Search for the proptypes declaration
node.properties.forEach(property => {
if (property.type === 'ExperimentalSpreadProperty') {
if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') {
return;
}

Expand All @@ -551,12 +570,29 @@ module.exports = {
});
},

TypeAlias: function(node) {
typeScope(node.id.name, node.right);
},

Program: function() {
stack = [{}];
},

BlockStatement: function () {
stack.push(Object.create(typeScope()));
},

'BlockStatement:exit': function () {
stack.pop();
},

// Check for type annotations in stateless components
FunctionDeclaration: handleStatelessComponent,
ArrowFunctionExpression: handleStatelessComponent,
FunctionExpression: handleStatelessComponent,

'Program:exit': function() {
stack = null;
const list = components.list();

for (const component in list) {
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/destructuring-assignment.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ module.exports = {
// this.props.Aprop || this.context.aProp || this.state.aState
const isPropUsed = (
node.object.type === 'MemberExpression' && node.object.object.type === 'ThisExpression' &&
(node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state')
(node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state') &&
!isAssignmentToProp(node)
);

if (isPropUsed && configuration === 'always') {
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/jsx-child-element-spacing.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ module.exports = {
if (
(lastChild || nextChild) &&
(!lastChild || isInlineElement(lastChild)) &&
(child && child.type === 'Literal') &&
(child && (child.type === 'Literal' || child.type === 'JSXText')) &&
(!nextChild || isInlineElement(nextChild)) &&
true
) {
Expand Down
6 changes: 6 additions & 0 deletions lib/rules/jsx-curly-brace-presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,12 @@ module.exports = {
if (shouldCheckForMissingCurly(node.parent, userConfig)) {
reportMissingCurly(node);
}
},

JSXText: node => {
if (shouldCheckForMissingCurly(node.parent, userConfig)) {
reportMissingCurly(node);
}
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/jsx-indent.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ module.exports = {
// Use the parent in a list or an array
if (prevToken.type === 'JSXText' || prevToken.type === 'Punctuator' && prevToken.value === ',') {
prevToken = sourceCode.getNodeByRangeIndex(prevToken.range[0]);
prevToken = prevToken.type === 'Literal' ? prevToken.parent : prevToken;
prevToken = prevToken.type === 'Literal' || prevToken.type === 'JSXText' ? prevToken.parent : prevToken;
// Use the first non-punctuator token in a conditional expression
} else if (prevToken.type === 'Punctuator' && prevToken.value === ':') {
do {
Expand Down
6 changes: 6 additions & 0 deletions lib/rules/jsx-no-literals.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ module.exports = {
}
},

JSXText: function(node) {
if (getValidation(node)) {
reportLiteralNode(node);
}
},

TemplateLiteral: function(node) {
const parent = getParentIgnoringBinaryExpressions(node);
if (isNoStrings && parent.type === 'JSXExpressionContainer') {
Expand Down
10 changes: 5 additions & 5 deletions lib/rules/jsx-one-expression-per-line.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ module.exports = {
let countNewLinesBeforeContent = 0;
let countNewLinesAfterContent = 0;

if (child.type === 'Literal') {
if (child.type === 'Literal' || child.type === 'JSXText') {
if (/^\s*$/.test(child.raw)) {
return;
}
Expand Down Expand Up @@ -110,14 +110,14 @@ module.exports = {
}

function spaceBetweenPrev () {
return (prevChild.type === 'Literal' && / $/.test(prevChild.raw)) ||
(child.type === 'Literal' && /^ /.test(child.raw)) ||
return ((prevChild.type === 'Literal' || prevChild.type === 'JSXText') && / $/.test(prevChild.raw)) ||
((child.type === 'Literal' || child.type === 'JSXText') && /^ /.test(child.raw)) ||
sourceCode.isSpaceBetweenTokens(prevChild, child);
}

function spaceBetweenNext () {
return (nextChild.type === 'Literal' && /^ /.test(nextChild.raw)) ||
(child.type === 'Literal' && / $/.test(child.raw)) ||
return ((nextChild.type === 'Literal' || nextChild.type === 'JSXText') && /^ /.test(nextChild.raw)) ||
((child.type === 'Literal' || child.type === 'JSXText') && / $/.test(child.raw)) ||
sourceCode.isSpaceBetweenTokens(child, nextChild);
}

Expand Down
19 changes: 15 additions & 4 deletions lib/rules/jsx-sort-props.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,30 @@ const generateFixerFunction = (node, context, reservedList) => {

return function(fixer) {
const fixers = [];
let source = sourceCode.getText();

// Replace each unsorted attribute with the sorted one.
sortableAttributeGroups.forEach((sortableGroup, ii) => {
sortableGroup.forEach((attr, jj) => {
const sortedAttr = sortedAttributeGroups[ii][jj];
const sortedAttrText = sourceCode.getText(sortedAttr);
fixers.push(
fixer.replaceTextRange([attr.range[0], attr.range[1]], sortedAttrText)
);
fixers.push({
range: [attr.range[0], attr.range[1]],
text: sortedAttrText
});
});
});

return fixers;
fixers.sort((a, b) => a.range[0] < b.range[0]);

const rangeStart = fixers[fixers.length - 1].range[0];
const rangeEnd = fixers[0].range[1];

fixers.forEach(fix => {
source = `${source.substr(0, fix.range[0])}${fix.text}${source.substr(fix.range[1])}`;
});

return fixer.replaceTextRange([rangeStart, rangeEnd], source.substr(rangeStart, rangeEnd - rangeStart));
};
};

Expand Down
4 changes: 2 additions & 2 deletions lib/rules/no-danger-with-children.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module.exports = {
return node.properties.find(prop => {
if (prop.type === 'Property') {
return prop.key.name === propName;
} else if (prop.type === 'ExperimentalSpreadProperty') {
} else if (prop.type === 'ExperimentalSpreadProperty' || prop.type === 'SpreadElement') {
const variable = findSpreadVariable(prop.argument.name);
if (variable && variable.defs.length && variable.defs[0].node.init) {
if (seenProps.indexOf(prop.argument.name) > -1) {
Expand Down Expand Up @@ -74,7 +74,7 @@ module.exports = {
* @returns {Boolean} True if node is a line break, false if not
*/
function isLineBreak(node) {
const isLiteral = node.type === 'Literal';
const isLiteral = node.type === 'Literal' || node.type === 'JSXText';
const isMultiline = node.loc.start.line !== node.loc.end.line;
const isWhiteSpaces = /^\s*$/.test(node.value);

Expand Down
16 changes: 8 additions & 8 deletions lib/rules/no-typos.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ module.exports = {
let propTypesPackageName = null;
let reactPackageName = null;

function checkValidPropTypeQualfier(node) {
function checkValidPropTypeQualifier(node) {
if (node.name !== 'isRequired') {
context.report({
node: node,
Expand Down Expand Up @@ -101,14 +101,14 @@ module.exports = {
isPropTypesPackage(node.object.object)
) { // PropTypes.myProp.isRequired
checkValidPropType(node.object.property);
checkValidPropTypeQualfier(node.property);
checkValidPropTypeQualifier(node.property);
} else if (
isPropTypesPackage(node.object) &&
node.property.name !== 'isRequired'
) { // PropTypes.myProp
checkValidPropType(node.property);
} else if (node.object.type === 'CallExpression') {
checkValidPropTypeQualfier(node.property);
checkValidPropTypeQualifier(node.property);
checkValidCallExpression(node.object);
}
} else if (node.type === 'CallExpression') {
Expand All @@ -126,8 +126,7 @@ module.exports = {

function reportErrorIfClassPropertyCasingTypo(node, propertyName) {
if (propertyName === 'propTypes' || propertyName === 'contextTypes' || propertyName === 'childContextTypes') {
const propsNode = node && node.parent && node.parent.type === 'AssignmentExpression' && node.parent.right;
checkValidPropObject(propsNode);
checkValidPropObject(node);
}
STATIC_CLASS_PROPERTIES.forEach(CLASS_PROP => {
if (propertyName && CLASS_PROP.toLowerCase() === propertyName.toLowerCase() && CLASS_PROP !== propertyName) {
Expand Down Expand Up @@ -176,7 +175,7 @@ module.exports = {

const tokens = context.getFirstTokens(node, 2);
const propertyName = tokens[1].value;
reportErrorIfClassPropertyCasingTypo(node, propertyName);
reportErrorIfClassPropertyCasingTypo(node.value, propertyName);
},

MemberExpression: function(node) {
Expand All @@ -193,9 +192,10 @@ module.exports = {

if (
relatedComponent &&
(utils.isES6Component(relatedComponent.node) || utils.isReturningJSX(relatedComponent.node))
(utils.isES6Component(relatedComponent.node) || utils.isReturningJSX(relatedComponent.node)) &&
(node.parent && node.parent.type === 'AssignmentExpression' && node.parent.right)
) {
reportErrorIfClassPropertyCasingTypo(node, propertyName);
reportErrorIfClassPropertyCasingTypo(node.parent.right, propertyName);
}
},

Expand Down
Loading