]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Rule to flag use of alert, confirm, prompt | |
3 | * @author Nicholas C. Zakas | |
4 | */ | |
5 | "use strict"; | |
6 | ||
7 | //------------------------------------------------------------------------------ | |
8 | // Requirements | |
9 | //------------------------------------------------------------------------------ | |
10 | ||
11 | const { | |
12 | getStaticPropertyName: getPropertyName, | |
6f036462 TL |
13 | getVariableByName, |
14 | skipChainExpression | |
eb39fafa DC |
15 | } = require("./utils/ast-utils"); |
16 | ||
17 | //------------------------------------------------------------------------------ | |
18 | // Helpers | |
19 | //------------------------------------------------------------------------------ | |
20 | ||
21 | /** | |
22 | * Checks if the given name is a prohibited identifier. | |
23 | * @param {string} name The name to check | |
24 | * @returns {boolean} Whether or not the name is prohibited. | |
25 | */ | |
26 | function isProhibitedIdentifier(name) { | |
27 | return /^(alert|confirm|prompt)$/u.test(name); | |
28 | } | |
29 | ||
30 | /** | |
31 | * Finds the eslint-scope reference in the given scope. | |
32 | * @param {Object} scope The scope to search. | |
33 | * @param {ASTNode} node The identifier node. | |
34 | * @returns {Reference|null} Returns the found reference or null if none were found. | |
35 | */ | |
36 | function findReference(scope, node) { | |
37 | const references = scope.references.filter(reference => reference.identifier.range[0] === node.range[0] && | |
38 | reference.identifier.range[1] === node.range[1]); | |
39 | ||
40 | if (references.length === 1) { | |
41 | return references[0]; | |
42 | } | |
43 | return null; | |
44 | } | |
45 | ||
46 | /** | |
47 | * Checks if the given identifier node is shadowed in the given scope. | |
48 | * @param {Object} scope The current scope. | |
49 | * @param {string} node The identifier node to check | |
50 | * @returns {boolean} Whether or not the name is shadowed. | |
51 | */ | |
52 | function isShadowed(scope, node) { | |
53 | const reference = findReference(scope, node); | |
54 | ||
55 | return reference && reference.resolved && reference.resolved.defs.length > 0; | |
56 | } | |
57 | ||
58 | /** | |
59 | * Checks if the given identifier node is a ThisExpression in the global scope or the global window property. | |
60 | * @param {Object} scope The current scope. | |
61 | * @param {string} node The identifier node to check | |
62 | * @returns {boolean} Whether or not the node is a reference to the global object. | |
63 | */ | |
64 | function isGlobalThisReferenceOrGlobalWindow(scope, node) { | |
65 | if (scope.type === "global" && node.type === "ThisExpression") { | |
66 | return true; | |
67 | } | |
6f036462 TL |
68 | if ( |
69 | node.type === "Identifier" && | |
70 | ( | |
71 | node.name === "window" || | |
72 | (node.name === "globalThis" && getVariableByName(scope, "globalThis")) | |
73 | ) | |
74 | ) { | |
eb39fafa DC |
75 | return !isShadowed(scope, node); |
76 | } | |
77 | ||
78 | return false; | |
79 | } | |
80 | ||
81 | //------------------------------------------------------------------------------ | |
82 | // Rule Definition | |
83 | //------------------------------------------------------------------------------ | |
84 | ||
85 | module.exports = { | |
86 | meta: { | |
87 | type: "suggestion", | |
88 | ||
89 | docs: { | |
90 | description: "disallow the use of `alert`, `confirm`, and `prompt`", | |
eb39fafa DC |
91 | recommended: false, |
92 | url: "https://eslint.org/docs/rules/no-alert" | |
93 | }, | |
94 | ||
95 | schema: [], | |
96 | ||
97 | messages: { | |
98 | unexpected: "Unexpected {{name}}." | |
99 | } | |
100 | }, | |
101 | ||
102 | create(context) { | |
103 | return { | |
104 | CallExpression(node) { | |
6f036462 | 105 | const callee = skipChainExpression(node.callee), |
eb39fafa DC |
106 | currentScope = context.getScope(); |
107 | ||
108 | // without window. | |
109 | if (callee.type === "Identifier") { | |
110 | const name = callee.name; | |
111 | ||
112 | if (!isShadowed(currentScope, callee) && isProhibitedIdentifier(callee.name)) { | |
113 | context.report({ | |
114 | node, | |
115 | messageId: "unexpected", | |
116 | data: { name } | |
117 | }); | |
118 | } | |
119 | ||
120 | } else if (callee.type === "MemberExpression" && isGlobalThisReferenceOrGlobalWindow(currentScope, callee.object)) { | |
121 | const name = getPropertyName(callee); | |
122 | ||
123 | if (isProhibitedIdentifier(name)) { | |
124 | context.report({ | |
125 | node, | |
126 | messageId: "unexpected", | |
127 | data: { name } | |
128 | }); | |
129 | } | |
130 | } | |
131 | } | |
132 | }; | |
133 | ||
134 | } | |
135 | }; |