Socket
Socket
Sign inDemoInstall

eslint-plugin-react

Package Overview
Dependencies
Maintainers
1
Versions
212
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-react - npm Package Compare versions

Comparing version 1.6.1 to 2.0.0

lib/rules/jsx-sort-props.js

20

History.md

@@ -0,1 +1,21 @@

2.0.0 / 2015-03-29
==================
* update dependencies
* add jsx-sort-props rule ([#16][])
* add no-unknown-property rule ([#28][])
* add ignore option to prop-types rule
* breaking in prop-types the children prop is no longer ignored
* fix components are now detected when using ES6 classes ([#24][])
* fix prop-types now return the right line/column ([#33][])
* fix props are now detected when destructuring ([#27][])
* fix only check for computed property names in prop-types ([#36][] @burnnat)
[#16]: https://github.com/yannickcr/eslint-plugin-react/issues/16
[#28]: https://github.com/yannickcr/eslint-plugin-react/issues/28
[#24]: https://github.com/yannickcr/eslint-plugin-react/issues/24
[#33]: https://github.com/yannickcr/eslint-plugin-react/issues/33
[#27]: https://github.com/yannickcr/eslint-plugin-react/issues/27
[#36]: https://github.com/yannickcr/eslint-plugin-react/pull/36
1.6.1 / 2015-03-25

@@ -2,0 +22,0 @@ ==================

8

index.js

@@ -16,3 +16,5 @@ 'use strict';

'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
'jsx-quotes': require('./lib/rules/jsx-quotes')
'jsx-quotes': require('./lib/rules/jsx-quotes'),
'no-unknown-property': require('./lib/rules/no-unknown-property'),
'jsx-sort-props': require('./lib/rules/jsx-sort-props')
},

@@ -31,4 +33,6 @@ rulesConfig: {

'jsx-no-undef': 0,
'jsx-quotes': 0
'jsx-quotes': 0,
'no-unknown-property': 0,
'jsx-sort-props': 0
}
};

@@ -7,2 +7,5 @@ /**

var componentUtil = require('../util/component');
var ComponentList = componentUtil.List;
// ------------------------------------------------------------------------------

@@ -14,15 +17,45 @@ // Rule Definition

var hasDisplayName = false;
var componentList = new ComponentList();
function isComponentDefinition(node) {
return (
var MISSING_MESSAGE = 'Component definition is missing display name';
var MISSING_MESSAGE_NAMED_COMP = '{{component}} component definition is missing display name';
/**
* Checks if we are declaring a display name
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if we are declaring a display name, false if not.
*/
function isDisplayNameDeclaration(node) {
return Boolean(
node &&
node.callee &&
node.callee.object &&
node.callee.property &&
node.callee.object.name === 'React' &&
node.callee.property.name === 'createClass'
node.name === 'displayName'
);
}
/**
* Mark a prop type as declared
* @param {ASTNode} node The AST node being checked.
*/
function markDisplayNameAsDeclared(node) {
componentList.set(context, node, {
hasDisplayName: true
});
}
/**
* Reports missing display name for a given component
* @param {Object} component The component to process
*/
function reportMissingDisplayName(component) {
if (!component || component.hasDisplayName === true) {
return;
}
context.report(
component.node,
component.name === componentUtil.DEFAULT_COMPONENT_NAME ? MISSING_MESSAGE : MISSING_MESSAGE_NAMED_COMP, {
component: component.name
}
);
}
// --------------------------------------------------------------------------

@@ -34,27 +67,39 @@ // Public

ObjectExpression: function(node) {
if (!isComponentDefinition(node.parent)) {
MemberExpression: function(node) {
if (!isDisplayNameDeclaration(node.property)) {
return;
}
var component = componentList.getByName(node.object.name);
if (!component) {
return;
}
markDisplayNameAsDeclared(component.node);
},
ObjectExpression: function(node) {
// Search for the displayName declaration
node.properties.forEach(function(property) {
var keyName = property.key.name || property.key.value;
if (keyName === 'displayName') {
hasDisplayName = true;
if (!isDisplayNameDeclaration(property.key)) {
return;
}
markDisplayNameAsDeclared(node);
});
},
'ObjectExpression:exit': function(node) {
'Program:exit': function() {
var list = componentList.getList();
// Report missing display name for all classes
for (var component in list) {
if (!list.hasOwnProperty(component)) {
continue;
}
reportMissingDisplayName(list[component]);
}
},
if (!isComponentDefinition(node.parent)) {
ReturnStatement: function(node) {
if (!componentUtil.isReactComponent(context, node)) {
return;
}
if (!hasDisplayName) {
context.report(node, 'Component definition is missing display name');
}
hasDisplayName = false;
componentList.set(context, node);
}

@@ -61,0 +106,0 @@ };

@@ -7,2 +7,5 @@ /**

var componentUtil = require('../util/component');
var ComponentList = componentUtil.List;
// ------------------------------------------------------------------------------

@@ -14,4 +17,6 @@ // Rule Definition

var componentCounter = 0;
var componentList = new ComponentList();
var MULTI_COMP_MESSAGE = 'Declare only one React component per file';
// --------------------------------------------------------------------------

@@ -22,8 +27,25 @@ // Public

return {
MemberExpression: function(node) {
if (node.object.name === 'React' && node.property.name === 'createClass' && ++componentCounter > 1) {
context.report(node, 'Declare only one React component per file');
'Program:exit': function() {
if (componentList.count() <= 1) {
return;
}
var list = componentList.getList();
var i = 0;
for (var component in list) {
if (!list.hasOwnProperty(component) || ++i === 1) {
continue;
}
context.report(list[component].node, MULTI_COMP_MESSAGE);
}
},
ReturnStatement: function(node) {
if (!componentUtil.isReactComponent(context, node)) {
return;
}
componentList.set(context, node);
}
};
};

@@ -7,2 +7,5 @@ /**

var componentUtil = require('../util/component');
var ComponentList = componentUtil.List;
// ------------------------------------------------------------------------------

@@ -14,17 +17,153 @@ // Rule Definition

var declaredPropTypes = [];
var usedPropTypes = [];
var ignorePropsValidation = false;
var configuration = context.options[0] || {};
var ignored = configuration.ignore || [];
function isComponentDefinition(node) {
return (
var componentList = new ComponentList();
var MISSING_MESSAGE = '\'{{name}}\' is missing in props validation';
var MISSING_MESSAGE_NAMED_COMP = '\'{{name}}\' is missing in props validation for {{component}}';
/**
* Checks if we are using a prop
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if we are using a prop, false if not.
*/
function isPropTypesUsage(node) {
return Boolean(
node.object.type === 'ThisExpression' &&
node.property.name === 'props'
);
}
/**
* Checks if we are declaring a prop
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if we are declaring a prop, false if not.
*/
function isPropTypesDeclaration(node) {
return Boolean(
node &&
node.callee &&
node.callee.object &&
node.callee.property &&
node.callee.object.name === 'React' &&
node.callee.property.name === 'createClass'
node.name === 'propTypes'
);
}
/**
* Checks if the prop is ignored
* @param {String} name Name of the prop to check.
* @returns {Boolean} True if the prop is ignored, false if not.
*/
function isIgnored(name) {
return ignored.indexOf(name) !== -1;
}
/**
* Checks if the prop is declared
* @param {String} name Name of the prop to check.
* @param {Object} component The component to process
* @returns {Boolean} True if the prop is declared, false if not.
*/
function isDeclaredInComponent(component, name) {
return (
component.declaredPropTypes &&
component.declaredPropTypes.indexOf(name) !== -1
);
}
/**
* Mark a prop type as used
* @param {ASTNode} node The AST node being marked.
*/
function markPropTypesAsUsed(node) {
var component = componentList.getByNode(context, node);
var usedPropTypes = component && component.usedPropTypes || [];
var type;
if (node.parent.property && node.parent.property.name && !node.parent.computed) {
type = 'direct';
} else if (
node.parent.parent.declarations &&
node.parent.parent.declarations[0].id.properties &&
node.parent.parent.declarations[0].id.properties[0].key.name
) {
type = 'destructuring';
}
switch (type) {
case 'direct':
usedPropTypes.push({
name: node.parent.property.name,
node: node
});
break;
case 'destructuring':
for (var i = 0, j = node.parent.parent.declarations[0].id.properties.length; i < j; i++) {
usedPropTypes.push({
name: node.parent.parent.declarations[0].id.properties[i].key.name,
node: node
});
}
break;
default:
break;
}
componentList.set(context, node, {
usedPropTypes: usedPropTypes
});
}
/**
* Mark a prop type as declared
* @param {ASTNode} node The AST node being checked.
* @param {propTypes} node The AST node containing the proptypes
*/
function markPropTypesAsDeclared(node, propTypes) {
var component = componentList.getByNode(context, node);
var declaredPropTypes = component && component.declaredPropTypes || [];
var ignorePropsValidation = false;
switch (propTypes.type) {
case 'ObjectExpression':
for (var i = 0, j = propTypes.properties.length; i < j; i++) {
declaredPropTypes.push(propTypes.properties[i].key.name);
}
break;
case 'MemberExpression':
declaredPropTypes.push(propTypes.property.name);
break;
default:
ignorePropsValidation = true;
break;
}
componentList.set(context, node, {
declaredPropTypes: declaredPropTypes,
ignorePropsValidation: ignorePropsValidation
});
}
/**
* Reports undeclared proptypes for a given component
* @param {Object} component The component to process
*/
function reportUndeclaredPropTypes(component) {
if (!component || !component.usedPropTypes || component.ignorePropsValidation === true) {
return;
}
var name;
for (var i = 0, j = component.usedPropTypes.length; i < j; i++) {
name = component.usedPropTypes[i].name;
if (isDeclaredInComponent(component, name) || isIgnored(name)) {
continue;
}
context.report(
component.usedPropTypes[i].node,
component.name === componentUtil.DEFAULT_COMPONENT_NAME ? MISSING_MESSAGE : MISSING_MESSAGE_NAMED_COMP, {
name: name,
component: component.name
}
);
}
}
// --------------------------------------------------------------------------

@@ -37,46 +176,51 @@ // Public

MemberExpression: function(node) {
if (node.object.type !== 'ThisExpression' || node.property.name !== 'props' || !node.parent.property) {
return;
var type;
if (isPropTypesUsage(node)) {
type = 'usage';
} else if (isPropTypesDeclaration(node.property)) {
type = 'declaration';
}
usedPropTypes.push(node.parent.property.name);
switch (type) {
case 'usage':
markPropTypesAsUsed(node);
break;
case 'declaration':
var component = componentList.getByName(node.object.name);
if (!component) {
return;
}
markPropTypesAsDeclared(component.node, node.parent.right || node.parent);
break;
default:
break;
}
},
ObjectExpression: function(node) {
if (!isComponentDefinition(node.parent)) {
return;
}
// Search for the displayName declaration
node.properties.forEach(function(property) {
var keyName = property.key.name || property.key.value;
if (keyName !== 'propTypes') {
if (!isPropTypesDeclaration(property.key)) {
return;
}
if (property.value.type !== 'ObjectExpression') {
ignorePropsValidation = true;
return;
}
for (var i = 0, j = property.value.properties.length; i < j; i++) {
declaredPropTypes.push(property.value.properties[i].key.name);
}
markPropTypesAsDeclared(node, property.value);
});
},
'ObjectExpression:exit': function(node) {
if (!isComponentDefinition(node.parent)) {
return;
}
for (var i = 0, j = usedPropTypes.length; !ignorePropsValidation && i < j; i++) {
if (declaredPropTypes.indexOf(usedPropTypes[i]) !== -1 || usedPropTypes[i] === 'children') {
'Program:exit': function() {
var list = componentList.getList();
// Report undeclared proptypes for all classes
for (var component in list) {
if (!list.hasOwnProperty(component)) {
continue;
}
context.report(node, '\'' + usedPropTypes[i] + '\' is missing in props validation');
reportUndeclaredPropTypes(list[component]);
}
},
declaredPropTypes.length = 0;
usedPropTypes.length = 0;
ignorePropsValidation = false;
ReturnStatement: function(node) {
if (!componentUtil.isReactComponent(context, node)) {
return;
}
componentList.set(context, node);
}

@@ -83,0 +227,0 @@ };

{
"name": "eslint-plugin-react",
"version": "1.6.1",
"version": "2.0.0",
"author": "Yannick Croissant <yannick.croissant+npm@gmail.com>",

@@ -27,5 +27,5 @@ "description": "React specific linting rules for ESLint",

"coveralls": "2.11.2",
"eslint": "0.17.1",
"eslint": "0.18",
"eslint-tester": "0.6.0",
"istanbul": "0.3.8",
"istanbul": "0.3.11",
"mocha": "2.2.1"

@@ -32,0 +32,0 @@ },

@@ -48,2 +48,3 @@ ESLint-plugin-React

"react/jsx-no-undef": 1,
"react/jsx-sort-props": 1,
"react/jsx-uses-react": 1,

@@ -54,2 +55,3 @@ "react/jsx-uses-vars": 1,

"react/no-multi-comp": 1,
"react/no-unknown-property": 1,
"react/prop-types": 1,

@@ -68,2 +70,3 @@ "react/react-in-jsx-scope": 1,

* [jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
* [jsx-sort-props](docs/rules/jsx-sort-props.md): Enforce props alphabetical sorting
* [jsx-uses-react](docs/rules/jsx-uses-react.md): Prevent React to be incorrectly marked as unused

@@ -74,2 +77,3 @@ * [jsx-uses-vars](docs/rules/jsx-uses-vars.md): Prevent variables used in JSX to be incorrectly marked as unused

* [no-multi-comp](docs/rules/no-multi-comp.md): Prevent multiple component definition per file
* [no-unknown-property](docs/rules/no-unknown-property.md): Prevent usage of unknown DOM property
* [prop-types](docs/rules/prop-types.md): Prevent missing props validation in a React component definition

@@ -76,0 +80,0 @@ * [react-in-jsx-scope](docs/rules/react-in-jsx-scope.md): Prevent missing React when using JSX

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc