123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- /**
- * @fileoverview Rule to flag use of alert, confirm, prompt
- * @author Nicholas C. Zakas
- */
- "use strict";
- //------------------------------------------------------------------------------
- // Requirements
- //------------------------------------------------------------------------------
- const {
- getStaticPropertyName: getPropertyName,
- getVariableByName,
- skipChainExpression
- } = require("./utils/ast-utils");
- //------------------------------------------------------------------------------
- // Helpers
- //------------------------------------------------------------------------------
- /**
- * Checks if the given name is a prohibited identifier.
- * @param {string} name The name to check
- * @returns {boolean} Whether or not the name is prohibited.
- */
- function isProhibitedIdentifier(name) {
- return /^(alert|confirm|prompt)$/u.test(name);
- }
- /**
- * Finds the eslint-scope reference in the given scope.
- * @param {Object} scope The scope to search.
- * @param {ASTNode} node The identifier node.
- * @returns {Reference|null} Returns the found reference or null if none were found.
- */
- function findReference(scope, node) {
- const references = scope.references.filter(reference => reference.identifier.range[0] === node.range[0] &&
- reference.identifier.range[1] === node.range[1]);
- if (references.length === 1) {
- return references[0];
- }
- return null;
- }
- /**
- * Checks if the given identifier node is shadowed in the given scope.
- * @param {Object} scope The current scope.
- * @param {string} node The identifier node to check
- * @returns {boolean} Whether or not the name is shadowed.
- */
- function isShadowed(scope, node) {
- const reference = findReference(scope, node);
- return reference && reference.resolved && reference.resolved.defs.length > 0;
- }
- /**
- * Checks if the given identifier node is a ThisExpression in the global scope or the global window property.
- * @param {Object} scope The current scope.
- * @param {string} node The identifier node to check
- * @returns {boolean} Whether or not the node is a reference to the global object.
- */
- function isGlobalThisReferenceOrGlobalWindow(scope, node) {
- if (scope.type === "global" && node.type === "ThisExpression") {
- return true;
- }
- if (
- node.type === "Identifier" &&
- (
- node.name === "window" ||
- (node.name === "globalThis" && getVariableByName(scope, "globalThis"))
- )
- ) {
- return !isShadowed(scope, node);
- }
- return false;
- }
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
- module.exports = {
- meta: {
- type: "suggestion",
- docs: {
- description: "disallow the use of `alert`, `confirm`, and `prompt`",
- category: "Best Practices",
- recommended: false,
- url: "https://eslint.org/docs/rules/no-alert"
- },
- schema: [],
- messages: {
- unexpected: "Unexpected {{name}}."
- }
- },
- create(context) {
- return {
- CallExpression(node) {
- const callee = skipChainExpression(node.callee),
- currentScope = context.getScope();
- // without window.
- if (callee.type === "Identifier") {
- const name = callee.name;
- if (!isShadowed(currentScope, callee) && isProhibitedIdentifier(callee.name)) {
- context.report({
- node,
- messageId: "unexpected",
- data: { name }
- });
- }
- } else if (callee.type === "MemberExpression" && isGlobalThisReferenceOrGlobalWindow(currentScope, callee.object)) {
- const name = getPropertyName(callee);
- if (isProhibitedIdentifier(name)) {
- context.report({
- node,
- messageId: "unexpected",
- data: { name }
- });
- }
- }
- }
- };
- }
- };
|