UNPKG

tslint-folders

Version:

Custom TSLint rules for checking imports between packages and their folders, and generating relevant diagrams.

155 lines (154 loc) 8.16 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Rule = void 0; var Lint = require("tslint"); var ts = require("typescript"); var ImportRuleUtils_1 = require("./utils/ImportRuleUtils"); var ConfigFactory_1 = require("./config/ConfigFactory"); var GeneralRuleUtils_1 = require("./utils/GeneralRuleUtils"); var RuleId_1 = require("./RuleId"); var TsConfigParser_1 = require("./utils/TsConfigParser"); var DISALLOW_IMPORT_FROM_SELF_MESSAGE = "do not import a package from itself - use a relative path"; var DISALLOW_IMPORT_FROM_BANNED_MESSAGE = "do not use a banned import path from package"; var Rule = /** @class */ (function (_super) { __extends(Rule, _super); function Rule() { return _super !== null && _super.apply(this, arguments) || this; } Rule.prototype.apply = function (sourceFile) { var config = ConfigFactory_1.ConfigFactory.createForBetweenPackages(this.getOptions().ruleArguments); return this.applyWithFunction(sourceFile, walk, config); }; return Rule; }(Lint.Rules.AbstractRule)); exports.Rule = Rule; var walk = function (ctx) { return ts.forEachChild(ctx.sourceFile, checkNode); function checkNode(node) { if (node.kind === ts.SyntaxKind.ImportDeclaration) { visitImportDeclaration(node, ctx); } else if (node.kind === ts.SyntaxKind.ImportEqualsDeclaration) { visitImportEqualsDeclaration(node, ctx); } return ts.forEachChild(node, checkNode); } }; function visitImportDeclaration(node, ctx) { validate(node, node.moduleSpecifier.getText(), ctx); } function visitImportEqualsDeclaration(node, ctx) { validate(node, node.moduleReference.getText(), ctx); } function validate(node, text, ctx) { // algorithm: /* - determine this files PackageFolder, PackageSubFolder - if ThirdParty then skip - determine the PackageFolder, PackageSubFolder of the import - if ThirdParty then skip - disallowImportFromSelf -> disallow if same - check if the import is allowed */ var filePath = node.getSourceFile().fileName; var tsConfig = TsConfigParser_1.TsConfigParser.parseConfigNear(filePath); var thisPackageLocation = ImportRuleUtils_1.ImportRuleUtils.determinePackageLocationFromPath(filePath, RuleId_1.RuleId.TsfFoldersImportsBetweenPackages, ctx.options, ImportRuleUtils_1.PathSource.SourceFilePath, tsConfig); if (ImportRuleUtils_1.ImportRuleUtils.isThisPackageThirdParty(thisPackageLocation, node)) { return; } if (!thisPackageLocation.packageFolder) { throw new Error("unexpected: this package is not ThirdParty, but has no packageFolder in its location"); } var importPackageLocation = ImportRuleUtils_1.ImportRuleUtils.determinePackageLocationFromPath(text, RuleId_1.RuleId.TsfFoldersImportsBetweenPackages, ctx.options, ImportRuleUtils_1.PathSource.ImportText, tsConfig, thisPackageLocation); ImportRuleUtils_1.ImportRuleUtils.logPackageAndImport(node, thisPackageLocation, importPackageLocation); var isImportRecognised = !ImportRuleUtils_1.ImportRuleUtils.isPackageThirdParty(importPackageLocation); if (isImportRecognised && importPackageLocation.packageName === thisPackageLocation.packageName && ctx.options.disallowImportFromSelf.enabled && !ImportRuleUtils_1.ImportRuleUtils.shouldIgnoreFile(node, ctx.options.disallowImportFromSelf.ignorePaths)) { ctx.addFailureAtNode(node, GeneralRuleUtils_1.GeneralRuleUtils.buildFailureString(DISALLOW_IMPORT_FROM_SELF_MESSAGE, RuleId_1.RuleId.TsfFoldersImportsBetweenPackages)); return; } if (isImportRecognised && ctx.options.checkImportsBetweenPackages.enabled && !ImportRuleUtils_1.ImportRuleUtils.shouldIgnoreFile(node, ctx.options.checkImportsBetweenPackages.ignorePaths)) { if (!thisPackageLocation.packageFolder || !importPackageLocation.packageFolder) { return; } if (hasBannedImportPattern(ctx.options.checkImportsBetweenPackages, text, node, ctx)) { return; } if (importPackageLocation.packageFolder === thisPackageLocation.packageFolder) { if (!ctx.options.checkImportsBetweenPackages.checkSubFoldersEnabled) { return; } // TODO xxx extract fun? // check sub-folders if (thisPackageLocation.packageSubFolder && importPackageLocation.packageSubFolder && thisPackageLocation.packageSubFolder.importPath !== importPackageLocation.packageSubFolder.importPath) { if (thisPackageLocation.packageSubFolder.allowedToImport.some(function (allowed) { return allowed === "*"; })) { return; } if (!thisPackageLocation.packageSubFolder.allowedToImport.some(function (allowed) { return importPackageLocation.packageSubFolder.importPath === allowed; })) { var failureMessage = "'".concat(thisPackageLocation.packageName, "' sub folder '").concat(thisPackageLocation.packageSubFolder.importPath, "' is not allowed to import from '").concat(importPackageLocation.packageSubFolder.importPath, "'"); addFailureAtNodeWithMessage(node, failureMessage, ctx); } } } else { var thisPackageFolder = thisPackageLocation.packageFolder; if (thisPackageFolder.allowedToImport.find(function (allowed) { return allowed === "*"; })) { return; } if (!thisPackageFolder.allowedToImport.find(function (allowed) { return allowed === importPackageLocation.packageName; })) { var failureMessage = "'".concat(thisPackageLocation.packageName, "' is not allowed to import from '").concat(importPackageLocation.packageName, "'"); addFailureAtNodeWithMessage(node, failureMessage, ctx); } } } } function hasBannedImportPattern(checkImportsBetweenPackages, text, node, ctx) { var bannedImports = buildListOfBannedImports(checkImportsBetweenPackages); if (bannedImports && bannedImports.some(function (ban) { return text.indexOf(ban) >= 0; })) { ctx.addFailureAtNode(node, GeneralRuleUtils_1.GeneralRuleUtils.buildFailureString(DISALLOW_IMPORT_FROM_BANNED_MESSAGE, RuleId_1.RuleId.TsfFoldersImportsBetweenPackages)); return true; } return false; } function buildListOfBannedImports(checkImportsBetweenPackages) { var ban = checkImportsBetweenPackages.ban, banBlacklist = checkImportsBetweenPackages.banBlacklist, packages = checkImportsBetweenPackages.packages; if (!ban) { return []; } var bannedImports = []; packages .filter(function (pkg) { return !banBlacklist || !banBlacklist.some(function (b) { return pkg.importPath === b; }); }) .forEach(function (pkg) { ban.forEach(function (b) { bannedImports.push(b.replace("{PACKAGE}", pkg.importPath)); }); }); return bannedImports; } function addFailureAtNodeWithMessage(node, failureMessage, ctx) { ctx.addFailureAtNode(node, GeneralRuleUtils_1.GeneralRuleUtils.buildFailureString(failureMessage, RuleId_1.RuleId.TsfFoldersImportsBetweenPackages)); }