routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+147
@@ -0,0 +1,147 @@
|
||||
import {isCallExpression, isMethodCall} from './ast/index.js';
|
||||
import {removeParentheses} from './fix/index.js';
|
||||
import {isNodeMatchesNameOrPath, getCallExpressionTokens} from './utils/index.js';
|
||||
|
||||
const MESSAGE_ID_ERROR = 'prefer-structured-clone/error';
|
||||
const MESSAGE_ID_SUGGESTION = 'prefer-structured-clone/suggestion';
|
||||
const messages = {
|
||||
[MESSAGE_ID_ERROR]: 'Prefer `structuredClone(…)` over `{{description}}` to create a deep clone.',
|
||||
[MESSAGE_ID_SUGGESTION]: 'Switch to `structuredClone(…)`.',
|
||||
};
|
||||
|
||||
const lodashCloneDeepFunctions = [
|
||||
'_.cloneDeep',
|
||||
'lodash.cloneDeep',
|
||||
];
|
||||
|
||||
/** @param {import('eslint').Rule.RuleContext} context */
|
||||
const create = context => {
|
||||
const {functions: configFunctions} = context.options[0];
|
||||
const functions = [...configFunctions, ...lodashCloneDeepFunctions];
|
||||
|
||||
// `JSON.parse(JSON.stringify(…))`
|
||||
context.on('CallExpression', callExpression => {
|
||||
if (!(
|
||||
// `JSON.stringify()`
|
||||
isMethodCall(callExpression, {
|
||||
object: 'JSON',
|
||||
method: 'parse',
|
||||
argumentsLength: 1,
|
||||
optionalCall: false,
|
||||
optionalMember: false,
|
||||
})
|
||||
// `JSON.parse()`
|
||||
&& isMethodCall(callExpression.arguments[0], {
|
||||
object: 'JSON',
|
||||
method: 'stringify',
|
||||
argumentsLength: 1,
|
||||
optionalCall: false,
|
||||
optionalMember: false,
|
||||
})
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jsonParse = callExpression;
|
||||
const jsonStringify = callExpression.arguments[0];
|
||||
const {sourceCode} = context;
|
||||
|
||||
return {
|
||||
node: jsonParse,
|
||||
loc: {
|
||||
start: sourceCode.getLoc(jsonParse).start,
|
||||
end: sourceCode.getLoc(jsonStringify.callee).end,
|
||||
},
|
||||
messageId: MESSAGE_ID_ERROR,
|
||||
data: {
|
||||
description: 'JSON.parse(JSON.stringify(…))',
|
||||
},
|
||||
suggest: [
|
||||
{
|
||||
messageId: MESSAGE_ID_SUGGESTION,
|
||||
* fix(fixer) {
|
||||
yield fixer.replaceText(jsonParse.callee, 'structuredClone');
|
||||
|
||||
yield fixer.remove(jsonStringify.callee);
|
||||
yield removeParentheses(jsonStringify.callee, fixer, context);
|
||||
|
||||
const {
|
||||
openingParenthesisToken,
|
||||
closingParenthesisToken,
|
||||
trailingCommaToken,
|
||||
} = getCallExpressionTokens(jsonStringify, context);
|
||||
|
||||
yield fixer.remove(openingParenthesisToken);
|
||||
yield fixer.remove(closingParenthesisToken);
|
||||
if (trailingCommaToken) {
|
||||
yield fixer.remove(trailingCommaToken);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
// `_.cloneDeep(foo)`
|
||||
context.on('CallExpression', callExpression => {
|
||||
if (!isCallExpression(callExpression, {
|
||||
argumentsLength: 1,
|
||||
optional: false,
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {callee} = callExpression;
|
||||
const matchedFunction = functions.find(nameOrPath => isNodeMatchesNameOrPath(callee, nameOrPath));
|
||||
|
||||
if (!matchedFunction) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
node: callee,
|
||||
messageId: MESSAGE_ID_ERROR,
|
||||
data: {
|
||||
description: `${matchedFunction.trim()}(…)`,
|
||||
},
|
||||
suggest: [
|
||||
{
|
||||
messageId: MESSAGE_ID_SUGGESTION,
|
||||
fix: fixer => fixer.replaceText(callee, 'structuredClone'),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const schema = [
|
||||
{
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
functions: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
description: 'Additional functions to check.',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
const config = {
|
||||
create,
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Prefer using `structuredClone` to create a deep clone.',
|
||||
recommended: 'unopinionated',
|
||||
},
|
||||
hasSuggestions: true,
|
||||
schema,
|
||||
defaultOptions: [{functions: []}],
|
||||
messages,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
Reference in New Issue
Block a user