1 | 'use strict';
|
2 | const getDocumentationUrl = require('./utils/get-documentation-url');
|
3 |
|
4 | const errorConstructors = new Set([
|
5 | 'Error',
|
6 | 'EvalError',
|
7 | 'InternalError',
|
8 | 'RangeError',
|
9 | 'ReferenceError',
|
10 | 'SyntaxError',
|
11 | 'TypeError',
|
12 | 'URIError'
|
13 | ]);
|
14 |
|
15 | const isReferenceAssigned = expression => {
|
16 | if (expression.type === 'AssignmentExpression') {
|
17 | const assignedVariable = expression.left;
|
18 | return assignedVariable.type === 'Identifier' && assignedVariable.name;
|
19 | }
|
20 |
|
21 | return false;
|
22 | };
|
23 |
|
24 | const findIdentifierValues = (identifierNode, context) => {
|
25 | const scope = context.getScope(identifierNode);
|
26 | const declarations = scope.set.get(identifierNode.name);
|
27 | if (declarations === undefined) {
|
28 | return [];
|
29 | }
|
30 |
|
31 | const expressions = declarations.references.map(reference => reference.identifier.parent);
|
32 | const referenceValues = [];
|
33 | for (const expression of expressions) {
|
34 | if (isReferenceAssigned(expression)) {
|
35 | referenceValues.push(expression.right);
|
36 | } else if (expression.type === 'VariableDeclarator') {
|
37 | referenceValues.push(expression.init);
|
38 | }
|
39 | }
|
40 |
|
41 | return referenceValues;
|
42 | };
|
43 |
|
44 | const isEmptyMessageString = node => {
|
45 | return (
|
46 | node.arguments.length > 0 &&
|
47 | node.arguments[0].type === 'Literal' &&
|
48 | !node.arguments[0].value
|
49 | );
|
50 | };
|
51 |
|
52 | const reportError = (expressionNode, context) => {
|
53 | const error = expressionNode.callee;
|
54 | if (errorConstructors.has(error.name)) {
|
55 | if (expressionNode.arguments.length === 0) {
|
56 | context.report({
|
57 | node: expressionNode.parent,
|
58 | message: 'Pass a message to the error constructor.'
|
59 | });
|
60 | }
|
61 |
|
62 | if (isEmptyMessageString(expressionNode)) {
|
63 | context.report({
|
64 | node: expressionNode.parent,
|
65 | message: 'Error message should not be an empty string.'
|
66 | });
|
67 | }
|
68 | }
|
69 | };
|
70 |
|
71 | const checkErrorMessage = (node, context) => {
|
72 | if (node.type === 'Identifier') {
|
73 | const identifierValues = findIdentifierValues(node, context);
|
74 | for (const node of identifierValues) {
|
75 | checkErrorMessage(node, context);
|
76 | }
|
77 | } else if (node.type === 'NewExpression' || node.type === 'CallExpression') {
|
78 | reportError(node, context);
|
79 | }
|
80 | };
|
81 |
|
82 | const create = context => {
|
83 | const throwStatements = [];
|
84 | return {
|
85 | 'ThrowStatement'(throwStatement) {
|
86 | throwStatements.push(throwStatement);
|
87 | },
|
88 | 'Program:exit'() {
|
89 | for (const throwStatement of throwStatements) {
|
90 | checkErrorMessage(throwStatement.argument, context);
|
91 | }
|
92 | }
|
93 | };
|
94 | };
|
95 |
|
96 | module.exports = {
|
97 | create,
|
98 | meta: {
|
99 | type: 'problem',
|
100 | docs: {
|
101 | url: getDocumentationUrl(__filename)
|
102 | }
|
103 | }
|
104 | };
|