line_push/node_modules/eslint-plugin-unicorn/rules/no-unused-properties.js
2022-07-21 03:28:35 +00:00

246 lines
5.1 KiB
JavaScript

'use strict';
const getDocumentationUrl = require('./utils/get-documentation-url');
const getDeclaratorOrPropertyValue = declaratorOrProperty => {
return declaratorOrProperty.init || declaratorOrProperty.value;
};
const isMemberExpressionCall = memberExpression => {
return (
memberExpression.parent &&
memberExpression.parent.type === 'CallExpression' &&
memberExpression.parent.callee === memberExpression
);
};
const isMemberExpressionAssignment = memberExpression => {
return (
memberExpression.parent &&
memberExpression.parent.type === 'AssignmentExpression'
);
};
const isMemberExpressionComputedBeyondPrediction = memberExpression => {
return (
memberExpression.computed && memberExpression.property.type !== 'Literal'
);
};
const specialProtoPropertyKey = {
type: 'Identifier',
name: '__proto__'
};
const propertyKeysEqual = (keyA, keyB) => {
if (keyA.type === 'Identifier') {
if (keyB.type === 'Identifier') {
return keyA.name === keyB.name;
}
if (keyB.type === 'Literal') {
return keyA.name === keyB.value;
}
}
if (keyA.type === 'Literal') {
if (keyB.type === 'Identifier') {
return keyA.value === keyB.name;
}
if (keyB.type === 'Literal') {
return keyA.value === keyB.value;
}
}
return false;
};
const objectPatternMatchesObjectExprPropertyKey = (pattern, key) => {
return pattern.properties.some(property => {
if (property.type === 'ExperimentalRestProperty') {
return true;
}
return propertyKeysEqual(property.key, key);
});
};
const isLeafDeclaratorOrProperty = declaratorOrProperty => {
const value = getDeclaratorOrPropertyValue(declaratorOrProperty);
if (!value) {
return true;
}
if (value.type !== 'ObjectExpression') {
return true;
}
return false;
};
const isUnusedVariable = variable => {
const hasReadReference = variable.references.some(reference => reference.isRead());
return !hasReadReference;
};
const create = context => {
const getPropertyDisplayName = property => {
if (property.key.type === 'Identifier') {
return property.key.name;
}
if (property.key.type === 'Literal') {
return property.key.value;
}
return context.getSource(property.key);
};
const checkProperty = (property, references, path) => {
if (references.length === 0) {
context.report({
node: property,
message: 'Property `{{name}}` is defined but never used.',
data: {
name: getPropertyDisplayName(property)
}
});
return;
}
checkObject(property, references, path);
};
const checkProperties = (objectExpression, references, path = []) => {
objectExpression.properties.forEach(property => {
const {key} = property;
if (!key) {
return;
}
if (propertyKeysEqual(key, specialProtoPropertyKey)) {
return;
}
const nextPath = path.concat(key);
const nextReferences = references
.map(reference => {
const {parent} = reference.identifier;
if (reference.init) {
if (
parent.type === 'VariableDeclarator' &&
parent.parent.type === 'VariableDeclaration' &&
parent.parent.parent.type === 'ExportNamedDeclaration'
) {
return {identifier: parent};
}
return undefined;
}
if (parent.type === 'MemberExpression') {
if (
isMemberExpressionAssignment(parent) ||
isMemberExpressionCall(parent) ||
isMemberExpressionComputedBeyondPrediction(parent) ||
propertyKeysEqual(parent.property, key)
) {
return {identifier: parent};
}
return undefined;
}
if (
parent.type === 'VariableDeclarator' &&
parent.id.type === 'ObjectPattern'
) {
if (objectPatternMatchesObjectExprPropertyKey(parent.id, key)) {
return {identifier: parent};
}
return undefined;
}
if (
parent.type === 'AssignmentExpression' &&
parent.left.type === 'ObjectPattern'
) {
if (objectPatternMatchesObjectExprPropertyKey(parent.left, key)) {
return {identifier: parent};
}
return undefined;
}
return reference;
})
.filter(Boolean);
checkProperty(property, nextReferences, nextPath);
});
};
const checkObject = (declaratorOrProperty, references, path) => {
if (isLeafDeclaratorOrProperty(declaratorOrProperty)) {
return;
}
const value = getDeclaratorOrPropertyValue(declaratorOrProperty);
checkProperties(value, references, path);
};
const checkVariable = variable => {
if (variable.defs.length !== 1) {
return;
}
if (isUnusedVariable(variable)) {
return;
}
const [definition] = variable.defs;
checkObject(definition.node, variable.references);
};
const checkVariables = scope => {
scope.variables.forEach(variable => checkVariable(variable));
};
const checkChildScopes = scope => {
scope.childScopes.forEach(scope => checkScope(scope));
};
const checkScope = scope => {
if (scope.type === 'global') {
return checkChildScopes(scope);
}
checkVariables(scope);
return checkChildScopes(scope);
};
return {
'Program:exit'() {
checkScope(context.getScope());
}
};
};
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
url: getDocumentationUrl(__filename)
}
}
};