eslint-plugin-sonarjs
Version:
74 lines (73 loc) • 3.46 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isAssertion = isAssertion;
exports.isTSAssertion = isTSAssertion;
exports.isTSAssertionWithAssignmentFallback = isTSAssertionWithAssignmentFallback;
const typescript_1 = __importDefault(require("typescript"));
const module_js_1 = require("../module.js");
const module_ts_js_1 = require("../module-ts.js");
const ASSERTIONS_MODULE = 'aws-cdk-lib/assertions';
const TEMPLATE_ASSERTION_METHOD_NAMES = new Set([
'resourceCountIs',
'resourcePropertiesCountIs',
'hasResourceProperties',
'hasResource',
'allResources',
'allResourcesProperties',
'hasParameter',
'hasOutput',
'hasMapping',
'hasCondition',
'templateMatches',
]);
const ASSERTION_METHODS_BY_FACTORY = new Map([
['aws-cdk-lib.assertions.Template.fromStack', TEMPLATE_ASSERTION_METHOD_NAMES],
['aws-cdk-lib.assertions.Template.fromJSON', TEMPLATE_ASSERTION_METHOD_NAMES],
['aws-cdk-lib.assertions.Template.fromString', TEMPLATE_ASSERTION_METHOD_NAMES],
[
'aws-cdk-lib.assertions.Annotations.fromStack',
new Set(['hasError', 'hasNoError', 'hasWarning', 'hasNoWarning', 'hasInfo', 'hasNoInfo']),
],
['aws-cdk-lib.assertions.Tags.fromStack', new Set(['hasValues', 'hasNone'])],
]);
function isAssertion(context, node) {
return node.type === 'CallExpression' && isFQNAssertion((0, module_js_1.getFullyQualifiedName)(context, node));
}
function isTSAssertion(services, node) {
if (node.kind !== typescript_1.default.SyntaxKind.CallExpression) {
return false;
}
return isFQNAssertion((0, module_ts_js_1.getFullyQualifiedNameTS)(services, node));
}
function isTSAssertionWithAssignmentFallback(context, services, node) {
if (node.kind !== typescript_1.default.SyntaxKind.CallExpression) {
return false;
}
// The ESTree fallback below resolves names by walking scopes on every call expression, so it is
// gated on the file actually importing the module. `importsModuleTS` caches per source file.
if (!(0, module_ts_js_1.importsModuleTS)(node.getSourceFile(), [ASSERTIONS_MODULE])) {
return false;
}
// The type-aware resolver only follows declaration initializers. `isAssertion` delegates to
// `getFullyQualifiedName`, whose ESTree scope resolver follows a variable's unique write, such
// as an assertion object assigned in `beforeEach`.
// The map only holds nodes converted from the linted file, so `undefined` here means `node`
// belongs to another file (the caller follows calls into their implementation) and the
// `context`-bound scope resolver could not be used on it anyway. The cast is the usual
// TSESTree-to-ESTree widening: `isAssertion` only reads `type` and the shared node shape.
const estreeNode = services.tsNodeToESTreeNodeMap.get(node);
return estreeNode !== undefined && isAssertion(context, estreeNode);
}
function isFQNAssertion(fqn) {
const normalized = fqn?.replaceAll('/', '.');
if (normalized === undefined) {
return false;
}
const lastDot = normalized.lastIndexOf('.');
const factory = normalized.slice(0, lastDot);
const method = normalized.slice(lastDot + 1);
return ASSERTION_METHODS_BY_FACTORY.get(factory)?.has(method) ?? false;
}