Skip to content

Commit

Permalink
Merge pull request #1914 from alexzherdev/is-function-helper
Browse files Browse the repository at this point in the history
Add a helper function for determining function-like expressions
  • Loading branch information
ljharb committed Aug 5, 2018
2 parents efe0c0c + 7edc982 commit 9dbb834
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 127 deletions.
3 changes: 2 additions & 1 deletion lib/rules/display-name.js
Expand Up @@ -6,6 +6,7 @@

const has = require('has');
const Components = require('../util/Components');
const astUtil = require('../util/ast');
const docsUrl = require('../util/docsUrl');

// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -116,7 +117,7 @@ module.exports = {
);

const namedFunctionExpression = (
(node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') &&
astUtil.isFunctionLikeExpression(node) &&
node.parent &&
(node.parent.type === 'VariableDeclarator' || node.parent.method === true) &&
(!node.parent.parent || !utils.isES5Component(node.parent.parent))
Expand Down
7 changes: 2 additions & 5 deletions lib/rules/no-array-index-key.js
Expand Up @@ -5,6 +5,7 @@
'use strict';

const has = require('has');
const astUtil = require('../util/ast');
const docsUrl = require('../util/docsUrl');

// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -63,11 +64,7 @@ module.exports = {
return null;
}

const isFunction = [
'ArrowFunctionExpression',
'FunctionExpression'
].indexOf(firstArg.type) !== -1;
if (!isFunction) {
if (!astUtil.isFunctionLikeExpression(firstArg)) {
return null;
}

Expand Down
6 changes: 2 additions & 4 deletions lib/rules/no-unused-prop-types.js
Expand Up @@ -11,6 +11,7 @@ const has = require('has');
const Components = require('../util/Components');
const variable = require('../util/variable');
const annotations = require('../util/annotations');
const astUtil = require('../util/ast');
const versionUtil = require('../util/version');
const propsUtil = require('../util/props');
const docsUrl = require('../util/docsUrl');
Expand Down Expand Up @@ -835,10 +836,7 @@ module.exports = {
types.node = value;
declaredPropTypes.push(types);
// Handle custom prop validators using props inside
if (
value.type === 'ArrowFunctionExpression'
|| value.type === 'FunctionExpression'
) {
if (astUtil.isFunctionLikeExpression(value)) {
markPropTypesAsUsed(value);
}
});
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/require-render-return.js
Expand Up @@ -46,7 +46,7 @@ module.exports = {
if (astUtil.getPropertyName(properties[i]) !== 'render' || !properties[i].value) {
continue;
}
return /FunctionExpression$/.test(properties[i].value.type);
return astUtil.isFunctionLikeExpression(properties[i].value);
}
return false;
}
Expand Down
6 changes: 2 additions & 4 deletions lib/rules/sort-comp.js
Expand Up @@ -393,13 +393,11 @@ module.exports = {
instanceVariable: !node.static &&
node.type === 'ClassProperty' &&
node.value &&
node.value.type !== 'ArrowFunctionExpression' &&
node.value.type !== 'FunctionExpression',
!astUtil.isFunctionLikeExpression(node.value),
instanceMethod: !node.static &&
node.type === 'ClassProperty' &&
node.value &&
(node.value.type === 'ArrowFunctionExpression' ||
node.value.type === 'FunctionExpression'),
(astUtil.isFunctionLikeExpression(node.value)),
typeAnnotation: !!node.typeAnnotation && node.value === null
}));

Expand Down
22 changes: 15 additions & 7 deletions lib/util/ast.js
Expand Up @@ -62,19 +62,18 @@ function getComponentProperties(node) {
case 'ClassExpression':
return node.body.body;
case 'ObjectExpression':
// return node.properties;
return node.properties;
default:
return [];
}
}

/**
* Checks if the node is the first in its line, excluding whitespace.
* @param {Object} context The node to check
* @param {ASTNode} node The node to check
* @return {Boolean} true if its the first node in its line
*/
* Checks if the node is the first in its line, excluding whitespace.
* @param {Object} context The node to check
* @param {ASTNode} node The node to check
* @return {Boolean} true if it's the first node in its line
*/
function isNodeFirstInLine(context, node) {
const sourceCode = context.getSourceCode();
let token = node;
Expand All @@ -94,11 +93,20 @@ function isNodeFirstInLine(context, node) {
return startLine !== endLine;
}

/**
* Checks if the node is a function or arrow function expression.
* @param {Object} context The node to check
* @return {Boolean} true if it's a function-like expression
*/
function isFunctionLikeExpression(node) {
return node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression';
}

module.exports = {
findReturnStatement: findReturnStatement,
getPropertyName: getPropertyName,
getPropertyNameNode: getPropertyNameNode,
getComponentProperties: getComponentProperties,
isNodeFirstInLine: isNodeFirstInLine
isNodeFirstInLine: isNodeFirstInLine,
isFunctionLikeExpression: isFunctionLikeExpression
};
210 changes: 105 additions & 105 deletions tests/lib/rules/no-typos.js
Expand Up @@ -1370,110 +1370,110 @@ ruleTester.run('no-typos', rule, {
message: 'Typo in declared prop type: objectof'
}]
}]
/*
// PropTypes declared on a component that is detected through JSDoc comments and is
// declared AFTER the PropTypes assignment
// Commented out since it only works with ESLint 5.
,{
code: `
MyComponent.PROPTYPES = {}
\/** @extends React.Component *\/
class MyComponent extends BaseComponent {}
`,
parserOptions: parserOptions
},
*/
/*
// createClass tests below fail, so they're commented out
// ---------
}, {
code: `
import React from 'react';
import PropTypes from 'prop-types';
const Component = React.createClass({
propTypes: {
a: PropTypes.string.isrequired,
b: PropTypes.shape({
c: PropTypes.number
}).isrequired
}
});
`,
parser: 'babel-eslint',
parserOptions: parserOptions,
errors: [{
message: 'Typo in prop type chain qualifier: isrequired'
}, {
message: 'Typo in prop type chain qualifier: isrequired'
}]
}, {
code: `
import React from 'react';
import PropTypes from 'prop-types';
const Component = React.createClass({
childContextTypes: {
a: PropTypes.bools,
b: PropTypes.Array,
c: PropTypes.function,
d: PropTypes.objectof,
}
});
`,
parser: 'babel-eslint',
parserOptions: parserOptions,
errors: [{
message: 'Typo in declared prop type: bools'
}, {
message: 'Typo in declared prop type: Array'
}, {
message: 'Typo in declared prop type: function'
}, {
message: 'Typo in declared prop type: objectof'
}]
}, {
code: `
import React from 'react';
import PropTypes from 'prop-types';
const Component = React.createClass({
propTypes: {
a: PropTypes.string.isrequired,
b: PropTypes.shape({
c: PropTypes.number
}).isrequired
}
});
`,
parserOptions: parserOptions,
errors: [{
message: 'Typo in prop type chain qualifier: isrequired'
}, {
message: 'Typo in prop type chain qualifier: isrequired'
}]
}, {
code: `
import React from 'react';
import PropTypes from 'prop-types';
const Component = React.createClass({
childContextTypes: {
a: PropTypes.bools,
b: PropTypes.Array,
c: PropTypes.function,
d: PropTypes.objectof,
}
});
`,
parserOptions: parserOptions,
errors: [{
message: 'Typo in declared prop type: bools'
}, {
message: 'Typo in declared prop type: Array'
}, {
message: 'Typo in declared prop type: function'
}, {
message: 'Typo in declared prop type: objectof'
/*
// PropTypes declared on a component that is detected through JSDoc comments and is
// declared AFTER the PropTypes assignment
// Commented out since it only works with ESLint 5.
,{
code: `
MyComponent.PROPTYPES = {}
\/** @extends React.Component *\/
class MyComponent extends BaseComponent {}
`,
parserOptions: parserOptions
},
*/
/*
// createClass tests below fail, so they're commented out
// ---------
}, {
code: `
import React from 'react';
import PropTypes from 'prop-types';
const Component = React.createClass({
propTypes: {
a: PropTypes.string.isrequired,
b: PropTypes.shape({
c: PropTypes.number
}).isrequired
}
});
`,
parser: 'babel-eslint',
parserOptions: parserOptions,
errors: [{
message: 'Typo in prop type chain qualifier: isrequired'
}, {
message: 'Typo in prop type chain qualifier: isrequired'
}]
}, {
code: `
import React from 'react';
import PropTypes from 'prop-types';
const Component = React.createClass({
childContextTypes: {
a: PropTypes.bools,
b: PropTypes.Array,
c: PropTypes.function,
d: PropTypes.objectof,
}
});
`,
parser: 'babel-eslint',
parserOptions: parserOptions,
errors: [{
message: 'Typo in declared prop type: bools'
}, {
message: 'Typo in declared prop type: Array'
}, {
message: 'Typo in declared prop type: function'
}, {
message: 'Typo in declared prop type: objectof'
}]
}, {
code: `
import React from 'react';
import PropTypes from 'prop-types';
const Component = React.createClass({
propTypes: {
a: PropTypes.string.isrequired,
b: PropTypes.shape({
c: PropTypes.number
}).isrequired
}
});
`,
parserOptions: parserOptions,
errors: [{
message: 'Typo in prop type chain qualifier: isrequired'
}, {
message: 'Typo in prop type chain qualifier: isrequired'
}]
}, {
code: `
import React from 'react';
import PropTypes from 'prop-types';
const Component = React.createClass({
childContextTypes: {
a: PropTypes.bools,
b: PropTypes.Array,
c: PropTypes.function,
d: PropTypes.objectof,
}
});
`,
parserOptions: parserOptions,
errors: [{
message: 'Typo in declared prop type: bools'
}, {
message: 'Typo in declared prop type: Array'
}, {
message: 'Typo in declared prop type: function'
}, {
message: 'Typo in declared prop type: objectof'
}]
}]
}]
// ---------
// createClass tests above fail, so they're commented out
*/
// ---------
// createClass tests above fail, so they're commented out
*/
});

0 comments on commit 9dbb834

Please sign in to comment.