Files
react-native/tools/eslint/rules/require-extends-error.js
T
Tim Yung 4f83498462 RN: Replace error-subclass-name with require-extends-error ESLint Rule
Summary:
Removes the `error-subclass-name` ESLint rule from `react-native/eslint-plugin`, and implements a new `require-extends-error` ESLint rule inside the React Native repository. This rule was only intended to be used for internal development of React Native.

This will change `react-native/eslint-plugin` to no longer provide the `error-subclass-name` rule.

NOTE: One behavior difference here is that I also implemented the desired behavior of checking classes that extend `Library.SomeError`.

Changelog:
[General][Removed] - `react-native/eslint-plugin` no longer provides the `error-subclass-name` rule.

Reviewed By: lunaleaps

Differential Revision: D39858882

fbshipit-source-id: 27b53216d77a15b3425bd9669dbc9d954c1c61da
2022-09-27 14:37:29 -07:00

110 lines
2.9 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
module.exports = {
meta: {
type: 'problem',
docs: {
description: "Require error classes to extend 'Error'",
recommended: true,
},
messages: {
errorClass:
"'{{name}}' must extend an error class (like 'Error') because its name is in PascalCase and ends with 'Error'.",
errorSuperClass:
"'{{name}}' may not be the name of an error class. It should be in PascalCase and end with 'Error'.",
errorSuperClassMissingName:
"An error class should have a PascalCase name ending with 'Error'.",
errorFunction:
"'{{name}}' is a reserved name. PascalCase names ending with 'Error' are reserved for error classes and may not be used for regular functions. Either rename this function or convert it to a class that extends 'Error'.",
},
schema: [],
},
create(context) {
return {
ClassDeclaration(node) {
processClass(node);
},
ClassExpression(node) {
processClass(node);
},
FunctionExpression(node) {
processFunction(node);
},
FunctionDeclaration(node) {
processFunction(node);
},
};
function processClass(node) {
const {id, superClass} = node;
// First, handle all cases in which superclass is not error-like.
if (superClass == null || !isSuperClassErrorLike(superClass)) {
if (id == null || !isIdentifierErrorLike(id)) {
return;
}
context.report({
node: id,
messageId: 'errorClass',
data: {name: id.name},
});
return;
}
// Then, handle all cases in which superclass is error-like.
if (id == null) {
context.report({
node,
messageId: 'errorSuperClassMissingName',
});
} else if (!isIdentifierErrorLike(id)) {
context.report({
node: id,
messageId: 'errorSuperClass',
data: {name: id.name},
});
}
}
function processFunction(node) {
const {id} = node;
if (id == null || !isIdentifierErrorLike(id)) {
return;
}
context.report({
node: id,
messageId: 'errorFunction',
data: {name: id.name},
});
}
},
};
function isSuperClassErrorLike(node) {
if (node.type === 'Identifier') {
return isNameErrorLike(node.name);
}
if (node.type === 'MemberExpression') {
return isSuperClassErrorLike(node.property);
}
return false;
}
function isIdentifierErrorLike(node) {
return node.type === 'Identifier' && isNameErrorLike(node.name);
}
function isNameErrorLike(name) {
return typeof name === 'string' && /^([A-Z].*)?Error$/.test(name);
}