Skip to content

Lint - add function-deprecation #780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 19, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions eslint-rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ module.exports = {
'component-deprecation': require('./lib/rules/component-deprecation'),
'assets-deprecation': require('./lib/rules/assets-deprecation'),
'typography-deprecation': require('./lib/rules/typography-deprecation'),
'function-deprecation': require('./lib/rules/function-deprecation'),
// for duplicate rules usage
'component-deprecation_warn': require('./lib/rules/component-deprecation'),
'assets-deprecation_warn': require('./lib/rules/assets-deprecation'),
'typography-deprecation_warn': require('./lib/rules/typography-deprecation'),
'component-deprecation_error': require('./lib/rules/component-deprecation'),
'assets-deprecation_error': require('./lib/rules/assets-deprecation'),
'typography-deprecation_error': require('./lib/rules/typography-deprecation'),
'function-deprecation_error': require('./lib/rules/function-deprecation'),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not adding a warning phase also? or we can just use the regular function-deprecation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

},
};
17 changes: 2 additions & 15 deletions eslint-rules/lib/rules/component-deprecation.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module.exports = {
recommended: true,
},
messages: {
uiLib: 'This component is deprecated or containes deprecated props.',
uiLib: 'This component is deprecated or contains deprecated props.',
},
fixable: 'code',
schema: [MAP_SCHEMA],
Expand Down Expand Up @@ -48,7 +48,7 @@ module.exports = {
return fixer.replaceText(node.name, fix);
case FIX_TYPES.COMPONENT_NAME:
if (node.type === 'ImportDeclaration') {
const index = getSpecifierIndex(node, options.name);
const index = utils.getSpecifierIndex(node, options.name);
return fixer.replaceText(node.specifiers[index], fix);
}
return fixer.replaceText(node.name, fix);
Expand All @@ -63,19 +63,6 @@ module.exports = {
}
}

function getSpecifierIndex(node, name) {
let matchIndex;
if (node && node.specifiers) {
_.forEach(node.specifiers, (s, index) => {
const x = _.get(s, 'imported.name');
if (x === name) {
matchIndex = index;
}
});
}
return matchIndex;
}

function checkPropDeprecation(node, propName, deprecatedPropList, componentName) {
const deprecatedProp = _.find(deprecatedPropList, {prop: propName});
if (deprecatedProp) {
Expand Down
218 changes: 218 additions & 0 deletions eslint-rules/lib/rules/function-deprecation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
const _ = require("lodash");
const utils = require("../utils");

const MAP_SCHEMA = {
type: "object",
additionalProperties: true,
};

const FIX_TYPES = {
PROP_NAME: "propName",
FUNCTION_NAME: "functionName",
};

module.exports = {
meta: {
docs: {
description: "function or some of the props sent to it are deprecated",
category: "Best Practices",
recommended: true,
},
messages: {
uiLib: "This function is deprecated or contains deprecated props.",
},
fixable: "code",
schema: [MAP_SCHEMA],
},
create(context) {
function reportDeprecatedFunction(node, options) {
try {
const { dueDate } = context.options[0];
const dueDateNotice = dueDate
? ` Please fix this issue by ${dueDate}!`
: "";
const msg =
options.prop === undefined
? `The '${options.name}' function is deprecated. ${options.message}${dueDateNotice}`
: `The '${options.name}' function's prop '${options.prop}' is deprecated. ${options.message}${dueDateNotice}`;

context.report({
node,
message: `${msg}`,
fix(fixer) {
if (options.fix) {
const type = Object.keys(options.fix)[0];
const fix = Object.values(options.fix)[0];
let fixed;
// console.warn('fix');
switch (type) {
case FIX_TYPES.PROP_NAME:
// Fix for prop name change only (when prop's value and type does not change)
// console.warn('fix prop');
// console.warn('from', node.arguments[options.argumentIndex]);
const prop = _.find(node.arguments[options.argumentIndex].properties, prop => prop.key.name === options.prop);
const propIndex = node.arguments[options.argumentIndex].properties.indexOf(prop);
fixed = fixer.replaceText(node.arguments[options.argumentIndex].properties[propIndex], fix)
// console.warn('to', fixed);
return fixed;
case FIX_TYPES.FUNCTION_NAME:
if (node.type === "ImportDeclaration") {
// console.warn('fix function import');
const index = utils.getSpecifierIndex(node, options.name);
// console.warn('from', node.specifiers[index]);
fixed = fixer.replaceText(node.specifiers[index], fix);
// console.warn('to', fixed);
return fixed;
}

// console.warn('fix function not import');
// console.warn('from', node.callee.name);
fixed = fixer.replaceText(node.callee, fix)
// console.warn('to', fixed);
return fixed;
default:
break;
}
}
},
});
} catch (err) {
console.log("Found error in: ", err, context.getFilename());
}
}

const { deprecations, source } = context.options[0];
const relevantDeprecationsData = [];
let everythingIsImported = false;

function getDeprecation(value) {
if (value && value.name) {
const name = value.name;
return _.find(
deprecations,
(deprecation) => deprecation.function === name
);
}
}

function searchForPossibleDeprecation(node) {
const importSource = node.source.value;
if (source === importSource) {
const specifiers = node.specifiers;
if (specifiers) {
_.map(specifiers, (specifier) => {
const deprecation = getDeprecation(specifier.imported);
if (deprecation) {
let type = FIX_TYPES.PROP_NAME;
if (!deprecation.arguments) {
type = FIX_TYPES.FUNCTION_NAME;
reportDeprecatedFunction(node, {
name: deprecation.function,
message: deprecation.message,
fix: deprecation.fix,
});
}

relevantDeprecationsData.push({
localFunctionName: specifier.local.name,
type,
deprecation,
});
}
});
}

if (relevantDeprecationsData.length === 0) { // someone is importing everything (*)
everythingIsImported = true;
_.map(deprecations, deprecation => {
relevantDeprecationsData.push({
localFunctionName: deprecation.function,
type: deprecation.arguments ? FIX_TYPES.PROP_NAME : FIX_TYPES.FUNCTION_NAME,
deprecation,
});
});
}
}
}

function findRelevantDeprecation(functionName) {
return _.find(
relevantDeprecationsData,
(relevantDeprecationData) =>
relevantDeprecationData.localFunctionName === functionName
);
}

function getArgumentsSent(node) {
const argumentsSent = [];
_.map(node.arguments, argument => {
const propsSentToArgument = [];
if (argument.properties) {
_.map(argument.properties, prop => {
if (prop.key && prop.key.name) {
propsSentToArgument.push(prop.key.name);
}
});
}

argumentsSent.push(propsSentToArgument);
});

return argumentsSent;
}

function getFunctionName(node) {
const propName = everythingIsImported ? 'callee.property.name' : 'callee.name';
return _.get(node, propName);
}

function testCallExpression(node) {
const functionName = getFunctionName(node);
if (functionName) {
const relevantDeprecation = findRelevantDeprecation(functionName);
if (relevantDeprecation) {
if (relevantDeprecation.type === FIX_TYPES.PROP_NAME) {
const argumentsSent = getArgumentsSent(node);
_.map(relevantDeprecation.deprecation.arguments, (argument, index) => {
if (argument.props && argument.props.length > 0 && argumentsSent.length >= index) {
const deprecationProps = argument.props;
const sentProps = argumentsSent[index];
_.map(sentProps, sentProp => {
const deprecationProp = _.find(deprecationProps, deprecationProp => deprecationProp.prop === sentProp);
if (deprecationProp) {
reportDeprecatedFunction(node, {
name: functionName,
message: deprecationProp.message,
argumentIndex: index,
prop: deprecationProp.prop,
fix: deprecationProp.fix,
});
}
});
}
});
} else {
reportDeprecatedFunction(node, {
name: relevantDeprecation.deprecation.function,
message: relevantDeprecation.deprecation.message,
fix: relevantDeprecation.deprecation.fix,
});
}
}
}
}

return {
ImportDeclaration: (node) => searchForPossibleDeprecation(node),
CallExpression: (node) => relevantDeprecationsData.length > 0 && testCallExpression(node),

// MemberExpression: node => localImportSpecifier && testMemberDeprecation(node),
// JSXAttribute: node => testJSXAttribute(node),
// JSXOpeningElement: node => testJSXOpeningElement(node),
// ObjectExpression: node => testObjectExpression(node),
// VariableDeclarator: node => testVariableDeclarator(node),
// Property: node => testProperty(node),
// JSXSpreadAttribute: node => testJSXSpreadAttribute(node)
};
},
};
14 changes: 14 additions & 0 deletions eslint-rules/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ function getLocalImportSpecifier(node, source, defaultImportName) {
return localImportSpecifier;
}

function getSpecifierIndex(node, name) {
let matchIndex;
if (node && node.specifiers) {
_.forEach(node.specifiers, (s, index) => {
const x = _.get(s, 'imported.name');
if (x === name) {
matchIndex = index;
}
});
}
return matchIndex;
}

module.exports = {
isPropFont,
findAndReportHardCodedValues,
Expand All @@ -87,4 +100,5 @@ module.exports = {
isLiteral,
findValueNodeOfIdentifier,
getLocalImportSpecifier,
getSpecifierIndex,
};
2 changes: 1 addition & 1 deletion eslint-rules/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-uilib",
"version": "1.0.25",
"version": "1.0.26",
"description": "uilib set of eslint rules",
"keywords": [
"eslint",
Expand Down
25 changes: 25 additions & 0 deletions eslint-rules/tests/function_deprecation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"function": "deprecatedFunction",
"source": "our-source",
"message": "Please use the 'validFunction' function instead (fix is available).",
"fix": {"functionName": "validFunction"}
},
{
"function": "validFunction",
"source": "our-source",
"message": "",
"arguments": [
{},
{
"props": [
{
"prop": "deprecatedProp",
"message": "Please use the 'validProp' prop instead (fix is available).",
"fix": {"propName": "validProp"}
}
]
}
]
}
]
Loading