react-native-ui-lib
Version:
<p align="center"> <img src="https://user-images.githubusercontent.com/1780255/105469025-56759000-5ca0-11eb-993d-3568c1fd54f4.png" height="250px" style="display:block"/> </p> <p align="center">UI Toolset & Components Library for React Native</p> <p a
197 lines (181 loc) • 6.11 kB
JavaScript
// @ts-ignore
const _ = require('lodash');
const {
organizeDeprecations,
addToImports,
getComponentLocalName,
getComponentName,
getPossibleDeprecations,
findValueNodeOfIdentifier,
handleError
} = require('../utils');
const RULE_ID = 'component-prop-deprecation';
const MAP_SCHEMA = {
type: 'object',
properties: {
component: {
type: 'string'
},
source: {
type: 'string'
},
props: {
type: 'array',
items: {
type: 'object',
required: ['prop', 'message'],
properties: {
prop: {
type: 'string'
},
message: {
type: 'string'
},
isRequired: {
type: 'boolean'
},
fix: {
type: 'object',
required: ['propName'],
properties: {
propName: {
type: 'string'
}
}
}
}
}
}
},
additionalProperties: true
};
module.exports = {
meta: {
docs: {
description: "Some of the component's props are deprecated",
category: 'Best Practices',
recommended: true
},
messages: {
uiLib: 'This component contains deprecated props.'
},
fixable: 'code',
schema: [MAP_SCHEMA]
},
create(context) {
function reportDeprecatedProps(data) {
try {
const {dueDate} = context.options[0];
const dueDateNotice = dueDate ? ` Please fix this issue by ${dueDate}!` : '';
const message = `The '${data.name}' component's prop '${data.prop}' is deprecated. ${data.message}${dueDateNotice}`;
context.report({
node: data.node,
message,
fix(fixer) {
if (data.fix) {
return fixer.replaceText(data.fixNode, data.fix.propName);
}
}
});
} catch (err) {
handleError(RULE_ID, err, context.getFilename());
}
}
function reportRequiredProps({node, name, prop, message: customMessage}) {
try {
const message = `The '${name}' component's prop '${prop}' is required. ${customMessage}`;
context.report({
node: node,
message
});
} catch (err) {
handleError(RULE_ID, err, context.getFilename());
}
}
const {deprecations} = context.options[0];
const organizedDeprecations = organizeDeprecations(deprecations);
const imports = [];
function checkPropDeprecation(node, fixNode, propName, deprecatedPropList, componentName) {
const deprecatedProp = _.find(deprecatedPropList, {prop: propName});
if (deprecatedProp && !deprecatedProp.isRequired) {
const {prop, message, fix} = deprecatedProp;
reportDeprecatedProps({node, name: componentName, prop, message, fixNode, fix});
}
return !!deprecatedProp;
}
function testAttributeForDeprecation(attribute, deprecatedPropList, componentName) {
let wasFound = false;
if (attribute.type === 'JSXAttribute') {
wasFound = checkPropDeprecation(
attribute,
attribute.name,
attribute.name.name,
deprecatedPropList,
componentName
);
} else if (attribute.type === 'JSXSpreadAttribute') {
const identifierName =
_.get(attribute, 'argument.name') ||
_.get(attribute, 'argument.callee.name') ||
_.get(attribute, 'argument.property.name');
const spreadSource = findValueNodeOfIdentifier(identifierName, context.getScope());
if (spreadSource) {
const properties = _.get(spreadSource, 'properties') || _.get(spreadSource, 'body.properties');
if (properties) {
_.forEach(properties, property => {
const key = _.get(property, 'key');
const propName = _.get(property, 'key.name');
wasFound = checkPropDeprecation(key, key, propName, deprecatedPropList, componentName);
});
}
}
}
return wasFound;
}
function deprecationCheck(node) {
imports.forEach(currentImport => {
const source = Object.keys(currentImport)[0];
const componentLocalName = getComponentLocalName(node);
if (componentLocalName) {
const deprecationSource = organizedDeprecations[source];
if (deprecationSource) {
// There are deprecations from this source
const componentName = getComponentName(componentLocalName, imports);
const foundPossibleDeprecations = getPossibleDeprecations(
componentLocalName,
imports,
currentImport,
deprecationSource
);
foundPossibleDeprecations.forEach(foundPossibleDeprecation => {
const deprecatedPropList = [...foundPossibleDeprecation.props];
const requiredPropList = _.remove(deprecatedPropList, p => !!p.isRequired);
const attributes = node.attributes;
/* handle deprecated props */
if (!_.isEmpty(deprecatedPropList)) {
attributes.forEach(attribute => {
testAttributeForDeprecation(attribute, deprecatedPropList, componentName);
});
}
/* handle required props */
let foundAttribute = false;
attributes.forEach(attribute => {
foundAttribute =
foundAttribute || testAttributeForDeprecation(attribute, requiredPropList, componentName);
});
if (!foundAttribute && requiredPropList[0]) {
const prop = requiredPropList[0];
reportRequiredProps({node, name: componentName, prop: prop.prop, message: prop.message});
}
});
}
}
});
}
return {
ImportDeclaration: node => addToImports(node, imports),
VariableDeclarator: node => addToImports(node, imports),
JSXOpeningElement: node => deprecationCheck(node)
};
}
};