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
JavaScript
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));
}
;