diff --git a/lib/util/Components.js b/lib/util/Components.js index 5e78e664fb..37f751f1f5 100644 --- a/lib/util/Components.js +++ b/lib/util/Components.js @@ -11,164 +11,166 @@ const variableUtil = require('./variable'); const pragmaUtil = require('./pragma'); const astUtil = require('./ast'); -const usedPropTypesAreEquivalent = (propA, propB) => { - if (propA.name === propB.name) { - if (!propA.allNames && !propB.allNames) { - return true; - } else if (Array.isArray(propA.allNames) && Array.isArray(propB.allNames) && propA.allNames.join('') === propB.allNames.join('')) { - return true; - } - return false; - } - return false; -}; - -const mergeUsedPropTypes = (propsList, newPropsList) => { - const propsToAdd = []; - newPropsList.forEach(newProp => { - const newPropisAlreadyInTheList = propsList.some(prop => usedPropTypesAreEquivalent(prop, newProp)); - if (!newPropisAlreadyInTheList) { - propsToAdd.push(newProp); - } - }); - return propsList.concat(propsToAdd); -}; - - /** * Components * @class */ -function Components() { - this._list = {}; - this._getId = function(node) { +class Components { + constructor() { + this._list = {}; + } + + _getId(node) { return node && node.range.join(':'); - }; -} + } -/** - * Add a node to the components list, or update it if it's already in the list - * - * @param {ASTNode} node The AST node being added. - * @param {Number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes) - * @returns {Object} Added component object - */ -Components.prototype.add = function(node, confidence) { - const id = this._getId(node); - if (this._list[id]) { - if (confidence === 0 || this._list[id].confidence === 0) { - this._list[id].confidence = 0; - } else { - this._list[id].confidence = Math.max(this._list[id].confidence, confidence); + /** + * Add a node to the components list, or update it if it's already in the list + * + * @param {ASTNode} node The AST node being added. + * @param {Number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes) + * @returns {Object} Added component object + */ + add(node, confidence) { + const id = this._getId(node); + if (this._list[id]) { + if (confidence === 0 || this._list[id].confidence === 0) { + this._list[id].confidence = 0; + } else { + this._list[id].confidence = Math.max(this._list[id].confidence, confidence); + } + return this._list[id]; } + this._list[id] = { + node: node, + confidence: confidence + }; return this._list[id]; } - this._list[id] = { - node: node, - confidence: confidence - }; - return this._list[id]; -}; - -/** - * Find a component in the list using its node - * - * @param {ASTNode} node The AST node being searched. - * @returns {Object} Component object, undefined if the component is not found - */ -Components.prototype.get = function(node) { - const id = this._getId(node); - return this._list[id]; -}; -/** - * Update a component in the list - * - * @param {ASTNode} node The AST node being updated. - * @param {Object} props Additional properties to add to the component. - */ -Components.prototype.set = function(node, props) { - while (node && !this._list[this._getId(node)]) { - node = node.parent; - } - if (!node) { - return; - } - const id = this._getId(node); - let copyUsedPropTypes; - if (this._list[id]) { - // usedPropTypes is an array. _extend replaces existing array with a new one which caused issue #1309. - // preserving original array so it can be merged later on. - copyUsedPropTypes = this._list[id].usedPropTypes && this._list[id].usedPropTypes.slice(); + /** + * Find a component in the list using its node + * + * @param {ASTNode} node The AST node being searched. + * @returns {Object} Component object, undefined if the component is not found + */ + get(node) { + const id = this._getId(node); + return this._list[id]; } - this._list[id] = util._extend(this._list[id], props); - if (this._list[id] && props.usedPropTypes) { - this._list[id].usedPropTypes = mergeUsedPropTypes(copyUsedPropTypes || [], props.usedPropTypes); + + /** + * Update a component in the list + * + * @param {ASTNode} node The AST node being updated. + * @param {Object} props Additional properties to add to the component. + */ + set(node, props) { + while (node && !this._list[this._getId(node)]) { + node = node.parent; + } + if (!node) { + return; + } + const id = this._getId(node); + let copyUsedPropTypes; + if (this._list[id]) { + // usedPropTypes is an array. _extend replaces existing array with a new one which caused issue #1309. + // preserving original array so it can be merged later on. + copyUsedPropTypes = this._list[id].usedPropTypes && this._list[id].usedPropTypes.slice(); + } + this._list[id] = util._extend(this._list[id], props); + if (this._list[id] && props.usedPropTypes) { + this._list[id].usedPropTypes = this._mergeUsedPropTypes(copyUsedPropTypes || [], props.usedPropTypes); + } } -}; -/** - * Return the components list - * Components for which we are not confident are not returned - * - * @returns {Object} Components list - */ -Components.prototype.list = function() { - const list = {}; - const usedPropTypes = {}; - // Find props used in components for which we are not confident - for (const i in this._list) { - if (!has(this._list, i) || this._list[i].confidence >= 2) { - continue; + /** + * Return the components list + * Components for which we are not confident are not returned + * + * @returns {Object} Components list + */ + list() { + const list = {}; + const usedPropTypes = {}; + // Find props used in components for which we are not confident + for (const i in this._list) { + if (!has(this._list, i) || this._list[i].confidence >= 2) { + continue; + } + let component = null; + let node = null; + node = this._list[i].node; + while (!component && node.parent) { + node = node.parent; + // Stop moving up if we reach a decorator + if (node.type === 'Decorator') { + break; + } + component = this.get(node); + } + if (component) { + const newUsedProps = (this._list[i].usedPropTypes || []).filter(propType => !propType.node || propType.node.kind !== 'init'); + + const componentId = this._getId(component.node); + usedPropTypes[componentId] = (usedPropTypes[componentId] || []).concat(newUsedProps); + } } - let component = null; - let node = null; - node = this._list[i].node; - while (!component && node.parent) { - node = node.parent; - // Stop moving up if we reach a decorator - if (node.type === 'Decorator') { - break; + // Assign used props in not confident components to the parent component + for (const j in this._list) { + if (!has(this._list, j) || this._list[j].confidence < 2) { + continue; + } + const id = this._getId(this._list[j].node); + list[j] = this._list[j]; + if (usedPropTypes[id]) { + list[j].usedPropTypes = (list[j].usedPropTypes || []).concat(usedPropTypes[id]); } - component = this.get(node); } - if (component) { - const newUsedProps = (this._list[i].usedPropTypes || []).filter(propType => !propType.node || propType.node.kind !== 'init'); + return list; + } - const componentId = this._getId(component.node); - usedPropTypes[componentId] = (usedPropTypes[componentId] || []).concat(newUsedProps); + /** + * Return the length of the components list + * Components for which we are not confident are not counted + * + * @returns {Number} Components list length + */ + length() { + let length = 0; + for (const i in this._list) { + if (!has(this._list, i) || this._list[i].confidence < 2) { + continue; + } + length++; } + return length; } - // Assign used props in not confident components to the parent component - for (const j in this._list) { - if (!has(this._list, j) || this._list[j].confidence < 2) { - continue; - } - const id = this._getId(this._list[j].node); - list[j] = this._list[j]; - if (usedPropTypes[id]) { - list[j].usedPropTypes = (list[j].usedPropTypes || []).concat(usedPropTypes[id]); - } + + _mergeUsedPropTypes(propsList, newPropsList) { + const propsToAdd = []; + newPropsList.forEach(newProp => { + const newPropisAlreadyInTheList = propsList.some(prop => this._usedPropTypesAreEquivalent(prop, newProp)); + if (!newPropisAlreadyInTheList) { + propsToAdd.push(newProp); + } + }); + return propsList.concat(propsToAdd); } - return list; -}; -/** - * Return the length of the components list - * Components for which we are not confident are not counted - * - * @returns {Number} Components list length - */ -Components.prototype.length = function() { - let length = 0; - for (const i in this._list) { - if (!has(this._list, i) || this._list[i].confidence < 2) { - continue; + _usedPropTypesAreEquivalent(propA, propB) { + if (propA.name === propB.name) { + if (!propA.allNames && !propB.allNames) { + return true; + } else if (Array.isArray(propA.allNames) && Array.isArray(propB.allNames) && propA.allNames.join('') === propB.allNames.join('')) { + return true; + } + return false; } - length++; + return false; } - return length; -}; +} function componentRule(rule, context) { const createClass = pragmaUtil.getCreateClassFromContext(context);