eslint-plugin-sonarjs
Version:
86 lines (85 loc) • 3.57 kB
JavaScript
"use strict";
/*
* SonarQube JavaScript Plugin
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* You can redistribute and/or modify this program under the terms of
* the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.isTrustedJQueryBinding = isTrustedJQueryBinding;
exports.isTrustedDocumentBinding = isTrustedDocumentBinding;
const ast_js_1 = require("../../helpers/ast.js");
const helpers_js_1 = require("./helpers.js");
/**
* Returns whether a `Variable`-def's declaration is an ambient, type-only
* declaration (`declare const $: JQueryStatic;`). A plain uninitialized binding
* (`let $;`) or a for-of/for-in binding also has a null `init`, so the TypeScript
* `declare` flag - not a null init - is what actually distinguishes the two.
*/
function isAmbientDeclaration(def) {
return def.parent?.declare === true;
}
function isTrustedJQueryBinding(scope, name, seen = new Set()) {
if (seen.has(name)) {
return false;
}
seen.add(name);
const variable = (0, ast_js_1.getVariableFromScope)(scope, name);
if (!variable?.defs.length) {
return true;
}
return variable.defs.some(def => {
if (def.type === 'ImportBinding') {
return def.parent.type === 'ImportDeclaration' && def.parent.source.value === 'jquery';
}
if (def.type !== 'Variable') {
return false;
}
if (def.node.init == null) {
return isAmbientDeclaration(def);
}
const init = (0, helpers_js_1.unwrapTypeAssertion)(def.node.init);
return ((init.type === 'CallExpression' && (0, ast_js_1.isRequireModule)(init, 'jquery')) ||
// `const $ = jQuery;` aliasing an already-trusted jQuery binding
((0, ast_js_1.isIdentifier)(init, 'jQuery') && isTrustedJQueryBinding(scope, 'jQuery', seen)));
});
}
function isTrustedDocumentBinding(scope) {
const variable = (0, ast_js_1.getVariableFromScope)(scope, 'document');
if (!variable?.defs.length) {
return true;
}
const windowNotShadowed = !(0, ast_js_1.getVariableFromScope)(scope, 'window')?.defs.length;
return variable.defs.some(def => {
if (def.type !== 'Variable') {
return false;
}
if (def.node.init == null) {
return isAmbientDeclaration(def);
}
if (!windowNotShadowed) {
return false;
}
const init = (0, helpers_js_1.unwrapTypeAssertion)(def.node.init);
if (init.type === 'MemberExpression') {
return (0, ast_js_1.isIdentifier)(init.object, 'window') && (0, ast_js_1.isIdentifier)(init.property, 'document');
}
if (def.node.id.type === 'ObjectPattern' && (0, ast_js_1.isIdentifier)(init, 'window')) {
// `const { document } = window;`
return def.node.id.properties.some(property => property.type === 'Property' &&
!property.computed &&
(0, ast_js_1.isIdentifier)(property.key, 'document'));
}
return false;
});
}