97 lines
2.5 KiB
JavaScript
97 lines
2.5 KiB
JavaScript
'use strict';
|
|
const {getStaticValue} = require('@eslint-community/eslint-utils');
|
|
const isShadowed = require('./utils/is-shadowed.js');
|
|
const {callOrNewExpressionSelector} = require('./selectors/index.js');
|
|
|
|
const MESSAGE_ID_MISSING_MESSAGE = 'missing-message';
|
|
const MESSAGE_ID_EMPTY_MESSAGE = 'message-is-empty-string';
|
|
const MESSAGE_ID_NOT_STRING = 'message-is-not-a-string';
|
|
const messages = {
|
|
[MESSAGE_ID_MISSING_MESSAGE]: 'Pass a message to the `{{constructorName}}` constructor.',
|
|
[MESSAGE_ID_EMPTY_MESSAGE]: 'Error message should not be an empty string.',
|
|
[MESSAGE_ID_NOT_STRING]: 'Error message should be a string.',
|
|
};
|
|
|
|
const selector = callOrNewExpressionSelector([
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
|
|
'Error',
|
|
'EvalError',
|
|
'RangeError',
|
|
'ReferenceError',
|
|
'SyntaxError',
|
|
'TypeError',
|
|
'URIError',
|
|
'InternalError',
|
|
'AggregateError',
|
|
]);
|
|
|
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
const create = context => ({
|
|
[selector](expression) {
|
|
if (isShadowed(context.getScope(), expression.callee)) {
|
|
return;
|
|
}
|
|
|
|
const constructorName = expression.callee.name;
|
|
const messageArgumentIndex = constructorName === 'AggregateError' ? 1 : 0;
|
|
const callArguments = expression.arguments;
|
|
|
|
// If message is `SpreadElement` or there is `SpreadElement` before message
|
|
if (callArguments.some((node, index) => index <= messageArgumentIndex && node.type === 'SpreadElement')) {
|
|
return;
|
|
}
|
|
|
|
const node = callArguments[messageArgumentIndex];
|
|
if (!node) {
|
|
return {
|
|
node: expression,
|
|
messageId: MESSAGE_ID_MISSING_MESSAGE,
|
|
data: {constructorName},
|
|
};
|
|
}
|
|
|
|
// These types can't be string, and `getStaticValue` may don't know the value
|
|
// Add more types, if issue reported
|
|
if (node.type === 'ArrayExpression' || node.type === 'ObjectExpression') {
|
|
return {
|
|
node,
|
|
messageId: MESSAGE_ID_NOT_STRING,
|
|
};
|
|
}
|
|
|
|
const staticResult = getStaticValue(node, context.getScope());
|
|
|
|
// We don't know the value of `message`
|
|
if (!staticResult) {
|
|
return;
|
|
}
|
|
|
|
const {value} = staticResult;
|
|
if (typeof value !== 'string') {
|
|
return {
|
|
node,
|
|
messageId: MESSAGE_ID_NOT_STRING,
|
|
};
|
|
}
|
|
|
|
if (value === '') {
|
|
return {
|
|
node,
|
|
messageId: MESSAGE_ID_EMPTY_MESSAGE,
|
|
};
|
|
}
|
|
},
|
|
});
|
|
|
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
module.exports = {
|
|
create,
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description: 'Enforce passing a `message` value when creating a built-in error.',
|
|
},
|
|
messages,
|
|
},
|
|
};
|