]>
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 | ||
34eeec05 | 85 | /** @type {import('../shared/types').Rule} */ |
eb39fafa DC |
86 | module.exports = { |
87 | meta: { | |
88 | type: "suggestion", | |
89 | ||
90 | docs: { | |
91 | description: "disallow the use of `alert`, `confirm`, and `prompt`", | |
eb39fafa DC |
92 | recommended: false, |
93 | url: "https://eslint.org/docs/rules/no-alert" | |
94 | }, | |
95 | ||
96 | schema: [], | |
97 | ||
98 | messages: { | |
99 | unexpected: "Unexpected {{name}}." | |
100 | } | |
101 | }, | |
102 | ||
103 | create(context) { | |
104 | return { | |
105 | CallExpression(node) { | |
6f036462 | 106 | const callee = skipChainExpression(node.callee), |
eb39fafa DC |
107 | currentScope = context.getScope(); |
108 | ||
109 | // without window. | |
110 | if (callee.type === "Identifier") { | |
111 | const name = callee.name; | |
112 | ||
113 | if (!isShadowed(currentScope, callee) && isProhibitedIdentifier(callee.name)) { | |
114 | context.report({ | |
115 | node, | |
116 | messageId: "unexpected", | |
117 | data: { name } | |
118 | }); | |
119 | } | |
120 | ||
121 | } else if (callee.type === "MemberExpression" && isGlobalThisReferenceOrGlobalWindow(currentScope, callee.object)) { | |
122 | const name = getPropertyName(callee); | |
123 | ||
124 | if (isProhibitedIdentifier(name)) { | |
125 | context.report({ | |
126 | node, | |
127 | messageId: "unexpected", | |
128 | data: { name } | |
129 | }); | |
130 | } | |
131 | } | |
132 | } | |
133 | }; | |
134 | ||
135 | } | |
136 | }; |