UNPKG

4.31 kBJavaScriptView Raw
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
11const getPropertyName = require("../ast-utils").getStaticPropertyName;
12
13//------------------------------------------------------------------------------
14// Helpers
15//------------------------------------------------------------------------------
16
17/**
18 * Checks if the given name is a prohibited identifier.
19 * @param {string} name The name to check
20 * @returns {boolean} Whether or not the name is prohibited.
21 */
22function isProhibitedIdentifier(name) {
23 return /^(alert|confirm|prompt)$/.test(name);
24}
25
26/**
27 * Reports the given node and identifier name.
28 * @param {RuleContext} context The ESLint rule context.
29 * @param {ASTNode} node The node to report on.
30 * @param {string} identifierName The name of the identifier.
31 * @returns {void}
32 */
33function report(context, node, identifierName) {
34 context.report(node, "Unexpected {{name}}.", { name: identifierName });
35}
36
37/**
38 * Finds the escope reference in the given scope.
39 * @param {Object} scope The scope to search.
40 * @param {ASTNode} node The identifier node.
41 * @returns {Reference|null} Returns the found reference or null if none were found.
42 */
43function findReference(scope, node) {
44 const references = scope.references.filter(function(reference) {
45 return reference.identifier.range[0] === node.range[0] &&
46 reference.identifier.range[1] === node.range[1];
47 });
48
49 if (references.length === 1) {
50 return references[0];
51 }
52 return null;
53}
54
55/**
56 * Checks if the given identifier node is shadowed in the given scope.
57 * @param {Object} scope The current scope.
58 * @param {Object} globalScope The global scope.
59 * @param {string} node The identifier node to check
60 * @returns {boolean} Whether or not the name is shadowed.
61 */
62function isShadowed(scope, globalScope, node) {
63 const reference = findReference(scope, node);
64
65 return reference && reference.resolved && reference.resolved.defs.length > 0;
66}
67
68/**
69 * Checks if the given identifier node is a ThisExpression in the global scope or the global window property.
70 * @param {Object} scope The current scope.
71 * @param {Object} globalScope The global scope.
72 * @param {string} node The identifier node to check
73 * @returns {boolean} Whether or not the node is a reference to the global object.
74 */
75function isGlobalThisReferenceOrGlobalWindow(scope, globalScope, node) {
76 if (scope.type === "global" && node.type === "ThisExpression") {
77 return true;
78 } else if (node.name === "window") {
79 return !isShadowed(scope, globalScope, node);
80 }
81
82 return false;
83}
84
85//------------------------------------------------------------------------------
86// Rule Definition
87//------------------------------------------------------------------------------
88
89module.exports = {
90 meta: {
91 docs: {
92 description: "disallow the use of `alert`, `confirm`, and `prompt`",
93 category: "Best Practices",
94 recommended: false
95 },
96
97 schema: []
98 },
99
100 create(context) {
101 let globalScope;
102
103 return {
104
105 Program() {
106 globalScope = context.getScope();
107 },
108
109 CallExpression(node) {
110 const callee = node.callee,
111 currentScope = context.getScope();
112
113 // without window.
114 if (callee.type === "Identifier") {
115 const identifierName = callee.name;
116
117 if (!isShadowed(currentScope, globalScope, callee) && isProhibitedIdentifier(callee.name)) {
118 report(context, node, identifierName);
119 }
120
121 } else if (callee.type === "MemberExpression" && isGlobalThisReferenceOrGlobalWindow(currentScope, globalScope, callee.object)) {
122 const identifierName = getPropertyName(callee);
123
124 if (isProhibitedIdentifier(identifierName)) {
125 report(context, node, identifierName);
126 }
127 }
128
129 }
130 };
131
132 }
133};