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 2.5.2 to 2.6.0

lib/rules/require-extension.js

31

History.md

@@ -0,1 +1,32 @@

2.6.0 / 2015-06-28
==================
* update dependencies
* add support for nested prop types ([#62][] [#105][] @Cellule)
* add require-extension rule ([#117][] @scothis)
* add support for computed string format in prop-types ([#127][] @Cellule)
* add ES6 methods to sort-comp default configuration ([#97][] [#122][])
* add support for props destructuring directly on the this keyword
* add schema to validate rules options
* fix test command for Windows ([#114][] @Cellule)
* fix detection of missing displayName and propTypes when ecmaFeatures.jsx is false ([#119][] @rpl)
* fix `prop-types` destructuring with properties as string ([#118][] @Cellule)
* fix `jsx-sort-prop-types` support for keys as string ([#123][] @Cellule)
* fix crash if a ClassProperty has only one token ([#125][])
* fix invalid class property handling in jsx-sort-prop-types ([#129][])
[#62]: https://github.com/yannickcr/eslint-plugin-react/issues/62
[#105]: https://github.com/yannickcr/eslint-plugin-react/issues/105
[#114]: https://github.com/yannickcr/eslint-plugin-react/pull/114
[#117]: https://github.com/yannickcr/eslint-plugin-react/pull/117
[#119]: https://github.com/yannickcr/eslint-plugin-react/pull/119
[#118]: https://github.com/yannickcr/eslint-plugin-react/issues/118
[#123]: https://github.com/yannickcr/eslint-plugin-react/pull/123
[#125]: https://github.com/yannickcr/eslint-plugin-react/issues/125
[#127]: https://github.com/yannickcr/eslint-plugin-react/pull/127
[#97]: https://github.com/yannickcr/eslint-plugin-react/issues/97
[#122]: https://github.com/yannickcr/eslint-plugin-react/issues/122
[#129]: https://github.com/yannickcr/eslint-plugin-react/issues/129
2.5.2 / 2015-06-14

@@ -2,0 +33,0 @@ ==================

6

index.js

@@ -21,3 +21,4 @@ 'use strict';

'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
'sort-comp': require('./lib/rules/sort-comp')
'sort-comp': require('./lib/rules/sort-comp'),
'require-extension': require('./lib/rules/require-extension')
},

@@ -41,4 +42,5 @@ rulesConfig: {

'jsx-boolean-value': 0,
'sort-comp': 0
'sort-comp': 0,
'require-extension': 0
}
};

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

var config = context.options[0] || {};
var acceptTranspilerName = config.acceptTranspilerName || false;
var componentList = new ComponentList();

@@ -46,3 +49,6 @@

var tokens = context.getFirstTokens(node, 2);
if (tokens[0].value === 'displayName' || tokens[1].value === 'displayName') {
if (
tokens[0].value === 'displayName' ||
(tokens[1] && tokens[1].value === 'displayName')
) {
return true;

@@ -82,2 +88,35 @@ }

/**
* Checks if the component have a name set by the transpiler
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True ifcomponent have a name, false if not.
*/
function hasTranspilerName(node) {
var namedAssignment = (
node.type === 'ObjectExpression' &&
node.parent &&
node.parent.parent &&
node.parent.parent.type === 'AssignmentExpression' && (
!node.parent.parent.left.object ||
node.parent.parent.left.object.name !== 'module' ||
node.parent.parent.left.property.name !== 'exports'
)
);
var namedDeclaration = (
node.type === 'ObjectExpression' &&
node.parent &&
node.parent.parent &&
node.parent.parent.type === 'VariableDeclarator'
);
var namedClass = (
node.type === 'ClassDeclaration' &&
node.id && node.id.name
);
if (namedAssignment || namedDeclaration || namedClass) {
return true;
}
return false;
}
// --------------------------------------------------------------------------

@@ -115,2 +154,9 @@ // Public

ClassDeclaration: function(node) {
if (!acceptTranspilerName || !hasTranspilerName(node)) {
return;
}
markDisplayNameAsDeclared(node);
},
ObjectExpression: function(node) {

@@ -124,2 +170,12 @@ // Search for the displayName declaration

});
// Has transpiler name
if (acceptTranspilerName && hasTranspilerName(node)) {
markDisplayNameAsDeclared(node);
}
if (componentUtil.isComponentDefinition(node)) {
componentList.set(context, node, {
isReactComponent: true
});
}
},

@@ -149,1 +205,11 @@

};
module.exports.schema = [{
type: 'object',
properties: {
acceptTranspilerName: {
type: 'boolean'
}
},
additionalProperties: false
}];

@@ -37,1 +37,5 @@ /**

};
module.exports.schema = [{
enum: ['always', 'never']
}];

@@ -66,1 +66,3 @@ /**

};
module.exports.schema = [];

@@ -71,1 +71,7 @@ /**

};
module.exports.schema = [{
enum: ['single', 'double']
}, {
enum: ['avoid-escape']
}];

@@ -38,2 +38,6 @@ /**

function getKey(node) {
return node.key.type === 'Identifier' ? node.key.name : node.key.value;
}
/**

@@ -46,4 +50,4 @@ * Checks if propTypes declarations are sorted

declarations.reduce(function(prev, curr) {
var prevPropName = prev.key.name;
var currenPropName = curr.key.name;
var prevPropName = getKey(prev);
var currenPropName = getKey(curr);

@@ -66,3 +70,3 @@ if (ignoreCase) {

ClassProperty: function(node) {
if (isPropTypesDeclaration(node) && node.value.type === 'ObjectExpression') {
if (isPropTypesDeclaration(node) && node.value && node.value.type === 'ObjectExpression') {
checkSorted(node.value.properties);

@@ -94,1 +98,11 @@ }

};
module.exports.schema = [{
type: 'object',
properties: {
ignoreCase: {
type: 'boolean'
}
},
additionalProperties: false
}];

@@ -41,1 +41,11 @@ /**

};
module.exports.schema = [{
type: 'object',
properties: {
ignoreCase: {
type: 'boolean'
}
},
additionalProperties: false
}];

@@ -41,1 +41,11 @@ /**

};
module.exports.schema = [{
type: 'object',
properties: {
pragma: {
type: 'string'
}
},
additionalProperties: false
}];

@@ -33,1 +33,3 @@ /**

};
module.exports.schema = [];

@@ -50,1 +50,5 @@ /**

};
module.exports.schema = [{
enum: ['allow-in-func']
}];

@@ -38,1 +38,3 @@ /**

};
module.exports.schema = [];

@@ -49,1 +49,3 @@ /**

};
module.exports.schema = [];

@@ -81,1 +81,3 @@ /**

};
module.exports.schema = [];

@@ -50,3 +50,6 @@ /**

var tokens = context.getFirstTokens(node, 2);
if (tokens[0].value === 'propTypes' || tokens[1].value === 'propTypes') {
if (
tokens[0].value === 'propTypes' ||
(tokens[1] && tokens[1].value === 'propTypes')
) {
return true;

@@ -88,11 +91,69 @@ }

/**
* Internal: Checks if the prop is declared
* @param {Object} declaredPropTypes Description of propTypes declared in the current component
* @param {String[]} keyList Dot separated name of the prop to check.
* @returns {Boolean} True if the prop is declared, false if not.
*/
function _isDeclaredInComponent(declaredPropTypes, keyList) {
for (var i = 0, j = keyList.length; i < j; i++) {
var key = keyList[i];
var propType = (
// Check if this key is declared
declaredPropTypes[key] ||
// If not, check if this type accepts any key
declaredPropTypes.__ANY_KEY__
);
if (!propType) {
// If it's a computed property, we can't make any further analysis, but is valid
return key === '__COMPUTED_PROP__';
}
if (propType === true) {
return true;
}
// Consider every children as declared
if (propType.children === true) {
return true;
}
if (propType.acceptedProperties) {
return key in propType.acceptedProperties;
}
if (propType.type === 'union') {
// If we fall in this case, we know there is at least one complex type in the union
if (i + 1 >= j) {
// this is the last key, accept everything
return true;
}
// non trivial, check all of them
var unionTypes = propType.children;
var unionPropType = {};
for (var k = 0, z = unionTypes.length; k < z; k++) {
unionPropType[key] = unionTypes[k];
var isValid = _isDeclaredInComponent(
unionPropType,
keyList.slice(i)
);
if (isValid) {
return true;
}
}
// every possible union were invalid
return false;
}
declaredPropTypes = propType.children;
}
return true;
}
/**
* Checks if the prop is declared
* @param {String} name Name of the prop to check.
* @param {Object} component The component to process
* @param {String[]} names List of names of the prop to check.
* @returns {Boolean} True if the prop is declared, false if not.
*/
function isDeclaredInComponent(component, name) {
return (
component.declaredPropTypes &&
component.declaredPropTypes.indexOf(name) !== -1
function isDeclaredInComponent(component, names) {
return _isDeclaredInComponent(
component.declaredPropTypes || {},
names
);

@@ -112,27 +173,246 @@ }

/**
* Retrieve the name of a key node
* @param {ASTNode} node The AST node with the key.
* @return {string} the name of the key
*/
function getKeyValue(node) {
var key = node.key;
return key.type === 'Identifier' ? key.name : key.value;
}
/**
* Iterates through a properties node, like a customized forEach.
* @param {Object[]} properties Array of properties to iterate.
* @param {Function} fn Function to call on each property, receives property key
and property value. (key, value) => void
*/
function iterateProperties(properties, fn) {
if (properties.length && typeof fn === 'function') {
for (var i = 0, j = properties.length; i < j; i++) {
var node = properties[i];
var key = getKeyValue(node);
var value = node.value;
fn(key, value);
}
}
}
/**
* Creates the representation of the React propTypes for the component.
* The representation is used to verify nested used properties.
* @param {ASTNode} value Node of the React.PropTypes for the desired propery
* @return {Object|Boolean} The representation of the declaration, true means
* the property is declared without the need for further analysis.
*/
function buildReactDeclarationTypes(value) {
if (
value.type === 'MemberExpression' &&
value.property &&
value.property.name &&
value.property.name === 'isRequired'
) {
value = value.object;
}
// Verify React.PropTypes that are functions
if (
value.type === 'CallExpression' &&
value.callee &&
value.callee.property &&
value.callee.property.name &&
value.arguments &&
value.arguments.length > 0
) {
var callName = value.callee.property.name;
var argument = value.arguments[0];
switch (callName) {
case 'shape':
if (argument.type !== 'ObjectExpression') {
// Invalid proptype or cannot analyse statically
return true;
}
var shapeTypeDefinition = {
type: 'shape',
children: {}
};
iterateProperties(argument.properties, function(childKey, childValue) {
shapeTypeDefinition.children[childKey] = buildReactDeclarationTypes(childValue);
});
return shapeTypeDefinition;
case 'arrayOf':
return {
type: 'array',
children: {
// Accept only array prototype and computed properties
__ANY_KEY__: {
acceptedProperties: Array.prototype
},
__COMPUTED_PROP__: buildReactDeclarationTypes(argument)
}
};
case 'objectOf':
return {
type: 'object',
children: {
__ANY_KEY__: buildReactDeclarationTypes(argument)
}
};
case 'oneOfType':
if (
!argument.elements ||
!argument.elements.length
) {
// Invalid proptype or cannot analyse statically
return true;
}
var unionTypeDefinition = {
type: 'union',
children: []
};
for (var i = 0, j = argument.elements.length; i < j; i++) {
var type = buildReactDeclarationTypes(argument.elements[i]);
// keep only complex type
if (type !== true) {
if (type.children === true) {
// every child is accepted for one type, abort type analysis
unionTypeDefinition.children = true;
return unionTypeDefinition;
}
unionTypeDefinition.children.push(type);
}
}
if (unionTypeDefinition.length === 0) {
// no complex type found, simply accept everything
return true;
}
return unionTypeDefinition;
case 'instanceOf':
return {
type: 'instance',
// Accept all children because we can't know what type they are
children: true
};
case 'oneOf':
default:
return true;
}
}
if (
value.type === 'MemberExpression' &&
value.property &&
value.property.name
) {
var name = value.property.name;
// React propTypes with limited possible properties
var propertiesMap = {
array: Array.prototype,
bool: Boolean.prototype,
func: Function.prototype,
number: Number.prototype,
string: String.prototype
};
if (name in propertiesMap) {
return {
type: name,
children: {
__ANY_KEY__: {
acceptedProperties: propertiesMap[name]
}
}
};
}
}
// Unknown property or accepts everything (any, object, ...)
return true;
}
/**
* Retrieve the name of a property node
* @param {ASTNode} node The AST node with the property.
* @return {string} the name of the property or undefined if not found
*/
function getPropertyName(node) {
var property = node.property;
if (property) {
switch (property.type) {
case 'Identifier':
if (node.computed) {
return '__COMPUTED_PROP__';
}
return property.name;
case 'Literal':
// Accept computed properties that are literal strings
if (typeof property.value === 'string') {
return property.value;
}
// falls through
default:
if (node.computed) {
return '__COMPUTED_PROP__';
}
break;
}
}
}
/**
* 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 || [];
function markPropTypesAsUsed(node, parentNames) {
parentNames = parentNames || [];
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';
var name;
var allNames;
var properties;
switch (node.type) {
case 'MemberExpression':
name = getPropertyName(node.parent);
if (name) {
allNames = parentNames.concat(name);
if (node.parent.type === 'MemberExpression') {
markPropTypesAsUsed(node.parent, allNames);
}
// Do not mark computed props as used.
type = name !== '__COMPUTED_PROP__' ? 'direct' : null;
} else if (
node.parent.parent.declarations &&
node.parent.parent.declarations[0].id.properties &&
getKeyValue(node.parent.parent.declarations[0].id.properties[0])
) {
type = 'destructuring';
properties = node.parent.parent.declarations[0].id.properties;
}
break;
case 'VariableDeclarator':
type = 'destructuring';
for (var i = 0, j = node.id.properties.length; i < j; i++) {
if (
(node.id.properties[i].key.name !== 'props' && node.id.properties[i].key.value !== 'props') ||
node.id.properties[i].value.type !== 'ObjectPattern'
) {
continue;
}
properties = node.id.properties[i].value.properties;
break;
}
break;
default:
throw new Error(node.type + ' ASTNodes are not handled by markPropTypesAsUsed');
}
var component = componentList.getByNode(context, node);
var usedPropTypes = component && component.usedPropTypes || [];
switch (type) {
case 'direct':
// Ignore Object methods
if (Object.prototype[node.parent.property.name]) {
if (Object.prototype[name]) {
break;
}
usedPropTypes.push({
name: node.parent.property.name,
name: name,
allNames: allNames,
node: node.parent.property

@@ -142,11 +422,14 @@ });

case 'destructuring':
var properties = node.parent.parent.declarations[0].id.properties;
for (var i = 0, j = properties.length; i < j; i++) {
if (hasSpreadOperator(properties[i])) {
for (var k = 0, l = properties.length; k < l; k++) {
if (hasSpreadOperator(properties[k])) {
continue;
}
usedPropTypes.push({
name: properties[i].key.name,
node: properties[i]
});
var propName = getKeyValue(properties[k]);
if (propName) {
usedPropTypes.push({
name: propName,
allNames: [propName],
node: properties[k]
});
}
}

@@ -170,3 +453,3 @@ break;

var component = componentList.getByNode(context, node);
var declaredPropTypes = component && component.declaredPropTypes || [];
var declaredPropTypes = component && component.declaredPropTypes || {};
var ignorePropsValidation = false;

@@ -176,9 +459,30 @@

case 'ObjectExpression':
for (var i = 0, j = propTypes.properties.length; i < j; i++) {
var key = propTypes.properties[i].key;
declaredPropTypes.push(key.type === 'Identifier' ? key.name : key.value);
}
iterateProperties(propTypes.properties, function(key, value) {
declaredPropTypes[key] = buildReactDeclarationTypes(value);
});
break;
case 'MemberExpression':
declaredPropTypes.push(propTypes.property.name);
var curDeclaredPropTypes = declaredPropTypes;
// Walk the list of properties, until we reach the assignment
// ie: ClassX.propTypes.a.b.c = ...
while (
propTypes &&
propTypes.parent.type !== 'AssignmentExpression' &&
propTypes.property &&
curDeclaredPropTypes
) {
var propName = propTypes.property.name;
if (propName in curDeclaredPropTypes) {
curDeclaredPropTypes = curDeclaredPropTypes[propName].children;
propTypes = propTypes.parent;
} else {
// This will crash at runtime because we haven't seen this key before
// stop this and do not declare it
propTypes = null;
}
}
if (propTypes) {
curDeclaredPropTypes[propTypes.property.name] =
buildReactDeclarationTypes(propTypes.parent.right);
}
break;

@@ -196,3 +500,2 @@ case null:

});
}

@@ -205,6 +508,10 @@

function reportUndeclaredPropTypes(component) {
var name;
var allNames, name;
for (var i = 0, j = component.usedPropTypes.length; i < j; i++) {
name = component.usedPropTypes[i].name;
if (isDeclaredInComponent(component, name) || isIgnored(name)) {
allNames = component.usedPropTypes[i].allNames;
if (
isIgnored(name) ||
isDeclaredInComponent(component, allNames)
) {
continue;

@@ -215,3 +522,3 @@ }

component.name === componentUtil.DEFAULT_COMPONENT_NAME ? MISSING_MESSAGE : MISSING_MESSAGE_NAMED_COMP, {
name: name,
name: allNames.join('.').replace(/\.__COMPUTED_PROP__/g, '[]'),
component: component.name

@@ -237,2 +544,9 @@ }

VariableDeclarator: function(node) {
if (node.init.type !== 'ThisExpression' || node.id.type !== 'ObjectPattern') {
return;
}
markPropTypesAsUsed(node);
},
MemberExpression: function(node) {

@@ -285,2 +599,8 @@ var type;

});
if (componentUtil.isComponentDefinition(node)) {
componentList.set(context, node, {
isReactComponent: true
});
}
},

@@ -310,1 +630,14 @@

};
module.exports.schema = [{
type: 'object',
properties: {
ignore: {
type: 'array',
items: {
type: 'string'
}
}
},
additionalProperties: false
}];

@@ -43,1 +43,3 @@ /**

};
module.exports.schema = [];

@@ -48,1 +48,3 @@ /**

};
module.exports.schema = [];

@@ -63,2 +63,4 @@ /**

'statics',
'defaultProps',
'constructor',
'getDefaultProps',

@@ -372,1 +374,25 @@ 'getInitialState',

};
module.exports.schema = [{
type: 'object',
properties: {
order: {
type: 'array',
items: {
type: 'string'
}
},
groups: {
type: 'object',
patternProperties: {
'^.*$': {
type: 'array',
items: {
type: 'string'
}
}
}
}
},
additionalProperties: false
}];

@@ -84,6 +84,13 @@ /**

properties: {
declaration: {type: 'boolean'},
assignment: {type: 'boolean'},
return: {type: 'boolean'}
}
declaration: {
type: 'boolean'
},
assignment: {
type: 'boolean'
},
return: {
type: 'boolean'
}
},
additionalProperties: false
}];
{
"name": "eslint-plugin-react",
"version": "2.5.2",
"version": "2.6.0",
"author": "Yannick Croissant <yannick.croissant+npm@gmail.com>",

@@ -10,3 +10,3 @@ "description": "React specific linting rules for ESLint",

"lint": "eslint ./",
"unit-test": "istanbul cover --dir reports/coverage _mocha tests/**/*.js -- --reporter dot",
"unit-test": "istanbul cover --dir reports/coverage node_modules/mocha/bin/_mocha tests/**/*.js -- --reporter dot",
"coveralls": "cat ./reports/coverage/lcov.info | coveralls"

@@ -27,7 +27,7 @@ },

"devDependencies": {
"babel-eslint": "3.1.15",
"babel-eslint": "3.1.19",
"coveralls": "2.11.2",
"eslint": "0.22.1",
"eslint-tester": "0.7.0",
"istanbul": "0.3.15",
"eslint": "0.24.0",
"eslint-tester": "0.8.2",
"istanbul": "0.3.17",
"mocha": "2.2.5"

@@ -34,0 +34,0 @@ },

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

"react/react-in-jsx-scope": 1,
"react/require-extension": 1,
"react/self-closing-comp": 1,

@@ -83,2 +84,3 @@ "react/sort-comp": 1,

* [react-in-jsx-scope](docs/rules/react-in-jsx-scope.md): Prevent missing React when using JSX
* [require-extension](docs/rules/require-extension.md): Restrict file extensions that may be required
* [self-closing-comp](docs/rules/self-closing-comp.md): Prevent extra closing tags for components without children

@@ -85,0 +87,0 @@ * [sort-comp](docs/rules/sort-comp.md): Enforce component methods order

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