UNPKG

rxjs-tslint-rules

Version:
200 lines (199 loc) 9.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Walker = exports.Rule = void 0; var tslib_1 = require("tslib"); var decamelize_1 = tslib_1.__importDefault(require("decamelize")); var Lint = tslib_1.__importStar(require("tslint")); var tsutils = tslib_1.__importStar(require("tsutils")); var util_1 = require("../support/util"); var Rule = (function (_super) { tslib_1.__extends(Rule, _super); function Rule() { return _super !== null && _super.apply(this, arguments) || this; } Rule.prototype.applyWithProgram = function (sourceFile, program) { return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program)); }; Rule.metadata = { description: "Disallows unsafe switchMap usage in effects and epics.", options: { properties: { allow: { oneOf: [ { type: "string" }, { type: "array", items: { type: "string" } }, ], }, disallow: { oneOf: [ { type: "string" }, { type: "array", items: { type: "string" } }, ], }, observable: { oneOf: [ { type: "string" }, { type: "array", items: { type: "string" } }, ], }, }, type: "object", }, optionsDescription: Lint.Utils.dedent(templateObject_1 || (templateObject_1 = tslib_1.__makeTemplateObject(["\n An optional object with optional `allow`, `disallow` and `observable` properties.\n The properties can be specifed as regular expression strings or as arrays of words.\n The `allow` or `disallow` properties are mutually exclusive. Whether or not\n `switchMap` is allowed will depend upon the matching of action types with `allow` or `disallow`.\n The `observable` property is used to identify the action observables from which effects and epics are composed."], ["\n An optional object with optional \\`allow\\`, \\`disallow\\` and \\`observable\\` properties.\n The properties can be specifed as regular expression strings or as arrays of words.\n The \\`allow\\` or \\`disallow\\` properties are mutually exclusive. Whether or not\n \\`switchMap\\` is allowed will depend upon the matching of action types with \\`allow\\` or \\`disallow\\`.\n The \\`observable\\` property is used to identify the action observables from which effects and epics are composed."]))), requiresTypeInfo: true, ruleName: "rxjs-no-unsafe-switchmap", type: "functionality", typescriptOnly: true, }; Rule.FAILURE_STRING = "Unsafe switchMap usage in effects and epics is forbidden"; return Rule; }(Lint.Rules.TypedRule)); exports.Rule = Rule; var Walker = (function (_super) { tslib_1.__extends(Walker, _super); function Walker(sourceFile, rawOptions, program) { var _this = _super.call(this, sourceFile, rawOptions, program) || this; var _a = tslib_1.__read(_this.getOptions(), 1), options = _a[0]; if (options && (options.allow || options.disallow)) { _this.allowRegExp = Walker.createRegExp(options.allow); _this.disallowRegExp = Walker.createRegExp(options.disallow); _this.observableRegExp = new RegExp(options.observable || Walker.DEFAULT_OBSERVABLE, "i"); } else { _this.allowRegExp = null; _this.disallowRegExp = Walker.createRegExp(Walker.DEFAULT_DISALLOW); _this.observableRegExp = new RegExp(Walker.DEFAULT_OBSERVABLE, "i"); } return _this; } Walker.createRegExp = function (value) { if (!value || !value.length) { return null; } var flags = "i"; if (typeof value === "string") { return new RegExp(value, flags); } var words = value; var joined = words .map(function (word) { return String.raw(templateObject_2 || (templateObject_2 = tslib_1.__makeTemplateObject(["(\b|_)", "(\b|_)"], ["(\\b|_)", "(\\b|_)"])), word); }) .join("|"); return new RegExp("(" + joined + ")", flags); }; Walker.prototype.visitCallExpression = function (node) { var propertyAccessExpression = node.expression; if (tsutils.isPropertyAccessExpression(propertyAccessExpression)) { var observableExpression = propertyAccessExpression.expression; var observableIdentifier = undefined; if (tsutils.isIdentifier(observableExpression)) { observableIdentifier = observableExpression; } else if (tsutils.isPropertyAccessExpression(observableExpression)) { observableIdentifier = observableExpression.name; } if (observableIdentifier && this.observableRegExp.test(observableIdentifier.getText())) { var propertyName = propertyAccessExpression.name.getText(); var typeChecker = this.getTypeChecker(); var type = typeChecker.getTypeAtLocation(observableExpression); if (util_1.isReferenceType(type) && Walker.METHODS_REGEXP.test(propertyName) && util_1.couldBeType(type.target, "Observable")) { switch (propertyName) { case "ofType": this.walkPatchedTypes(node); break; case "pipe": this.walkPipedTypes(node); break; default: break; } } } } _super.prototype.visitCallExpression.call(this, node); }; Walker.prototype.shouldDisallow = function (args) { var _this = this; var names = args.map(function (arg) { return decamelize_1.default(arg.getText()); }); if (this.allowRegExp) { return !names.every(function (name) { return _this.allowRegExp.test(name); }); } if (this.disallowRegExp) { return names.some(function (name) { return _this.disallowRegExp.test(name); }); } return false; }; Walker.prototype.walkPatchedOperators = function (node) { var name = undefined; for (var parent_1 = node.parent; parent_1; parent_1 = parent_1.parent) { if (tsutils.isCallExpression(parent_1)) { if (name) { switch (name.getText()) { case "pipe": this.walkPipedOperators(parent_1); break; case "switchMap": this.addFailureAtNode(name, Rule.FAILURE_STRING); break; default: break; } } } else if (tsutils.isPropertyAccessExpression(parent_1)) { name = parent_1.name; } else { break; } } }; Walker.prototype.walkPatchedTypes = function (node) { if (this.shouldDisallow(node.arguments)) { this.walkPatchedOperators(node); } }; Walker.prototype.walkPipedOperators = function (node) { var _this = this; node.arguments.forEach(function (arg) { if (tsutils.isCallExpression(arg)) { var expression = arg.expression; if (tsutils.isIdentifier(expression) && expression.getText() === "switchMap") { _this.addFailureAtNode(expression, Rule.FAILURE_STRING); } } }); }; Walker.prototype.walkPipedTypes = function (node) { var _this = this; node.arguments.forEach(function (arg) { if (tsutils.isCallExpression(arg)) { var expression = arg.expression; if (tsutils.isIdentifier(expression) && expression.getText() === "ofType") { if (_this.shouldDisallow(arg.arguments)) { _this.walkPipedOperators(node); } } } }); }; Walker.METHODS_REGEXP = /(ofType|pipe)/; Walker.DEFAULT_DISALLOW = [ "add", "create", "delete", "post", "put", "remove", "set", "update", ]; Walker.DEFAULT_OBSERVABLE = String.raw(templateObject_3 || (templateObject_3 = tslib_1.__makeTemplateObject(["action(s|$)?"], ["action(s|\\$)?"]))); return Walker; }(Lint.ProgramAwareRuleWalker)); exports.Walker = Walker; var templateObject_1, templateObject_2, templateObject_3;