UNPKG

5.78 kBPlain TextView Raw
1import * as Lint from "tslint";
2import * as ts from "typescript";
3
4import { ConfigFactory } from "./config/ConfigFactory";
5import { GeneralRuleUtils } from "./utils/GeneralRuleUtils";
6import { ImportRuleUtils, PathSource } from "./utils/ImportRuleUtils";
7
8export const IMPORTS_BETWEEN_PACKAGES_RULE_ID =
9 "tsf-folders-imports-between-packages";
10
11const DISALLOW_IMPORT_FROM_SELF_MESSAGE =
12 "do not import a package from itself - use a relative path";
13
14export class Rule extends Lint.Rules.AbstractRule {
15 apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
16 const walker = new ImportsWalker(sourceFile, this.getOptions());
17 this.applyWithWalker(walker);
18
19 return walker.getFailures();
20 }
21}
22
23class ImportsWalker extends Lint.RuleWalker {
24 visitImportDeclaration(node: ts.ImportDeclaration) {
25 this.validate(node, node.moduleSpecifier.getText());
26 }
27
28 visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration) {
29 this.validate(node, node.moduleReference.getText());
30
31 super.visitImportEqualsDeclaration(node);
32 }
33
34 private validate(node: ts.Node, text: string) {
35 // algorithm:
36 /*
37 - determine this files PackageFolder, PackageSubFolder
38 - if ThirdParty then skip
39 - determine the PackageFolder, PackageSubFolder of the import
40 - if ThirdParty then skip
41 - disallowImportFromSelf -> disallow if same
42 - check if the import is allowed
43 */
44
45 const config = ConfigFactory.createForBetweenPackages(this.getOptions());
46
47 const thisPackageLocation = ImportRuleUtils.determinePackageLocationFromPath(
48 node.getSourceFile().fileName,
49 IMPORTS_BETWEEN_PACKAGES_RULE_ID,
50 config,
51 PathSource.SourceFilePath
52 );
53
54 if (ImportRuleUtils.isThisPackageThirdParty(thisPackageLocation, node)) {
55 return;
56 }
57
58 if (!thisPackageLocation.packageFolder) {
59 throw new Error(
60 "unexpected: this package is not ThirdParty, but has no packageFolder in its location"
61 );
62 }
63
64 const importPackageLocation = ImportRuleUtils.determinePackageLocationFromPath(
65 text,
66 IMPORTS_BETWEEN_PACKAGES_RULE_ID,
67 config,
68 PathSource.ImportText,
69 thisPackageLocation
70 );
71
72 ImportRuleUtils.logPackageAndImport(
73 node,
74 thisPackageLocation,
75 importPackageLocation
76 );
77
78 const isImportRecognised = !ImportRuleUtils.isPackageThirdParty(
79 importPackageLocation
80 );
81
82 if (
83 isImportRecognised &&
84 importPackageLocation.packageName === thisPackageLocation.packageName &&
85 config.disallowImportFromSelf.enabled &&
86 !ImportRuleUtils.shouldIgnoreFile(
87 node,
88 config.disallowImportFromSelf.ignorePaths
89 )
90 ) {
91 this.addFailureAtNode(
92 node,
93 GeneralRuleUtils.buildFailureString(
94 DISALLOW_IMPORT_FROM_SELF_MESSAGE,
95 IMPORTS_BETWEEN_PACKAGES_RULE_ID
96 )
97 );
98 return;
99 }
100
101 if (
102 isImportRecognised &&
103 config.checkImportsBetweenPackages.enabled &&
104 !ImportRuleUtils.shouldIgnoreFile(
105 node,
106 config.checkImportsBetweenPackages.ignorePaths
107 )
108 ) {
109 if (
110 !thisPackageLocation.packageFolder ||
111 !importPackageLocation.packageFolder
112 ) {
113 return;
114 }
115
116 if (
117 importPackageLocation.packageFolder ===
118 thisPackageLocation.packageFolder
119 ) {
120 if (!config.checkImportsBetweenPackages.checkSubFoldersEnabled) {
121 return;
122 }
123
124 // TODO xxx extract fun?
125 // check sub-folders
126
127 if (
128 thisPackageLocation.packageSubFolder &&
129 importPackageLocation.packageSubFolder &&
130 thisPackageLocation.packageSubFolder.importPath !==
131 importPackageLocation.packageSubFolder.importPath
132 ) {
133 if (
134 thisPackageLocation.packageSubFolder.allowedToImport.some(
135 allowed => allowed === "*"
136 )
137 ) {
138 return;
139 }
140
141 if (
142 !thisPackageLocation.packageSubFolder.allowedToImport.some(
143 allowed => {
144 return (
145 importPackageLocation.packageSubFolder!.importPath === allowed
146 );
147 }
148 )
149 ) {
150 const failureMessage = `'${
151 thisPackageLocation.packageName
152 }' sub folder '${
153 thisPackageLocation.packageSubFolder.importPath
154 }' is not allowed to import from '${
155 importPackageLocation.packageSubFolder.importPath
156 }'`;
157
158 this.addFailureAtNodeWithMessage(node, failureMessage);
159 }
160 }
161 } else {
162 const thisPackageFolder = thisPackageLocation.packageFolder;
163 if (
164 thisPackageFolder.allowedToImport.find(allowed => allowed === "*")
165 ) {
166 return;
167 }
168
169 if (
170 !thisPackageFolder.allowedToImport.find(
171 allowed => allowed === importPackageLocation.packageName
172 )
173 ) {
174 const failureMessage = `'${
175 thisPackageLocation.packageName
176 }' is not allowed to import from '${
177 importPackageLocation.packageName
178 }'`;
179
180 this.addFailureAtNodeWithMessage(node, failureMessage);
181 }
182 }
183 }
184 }
185
186 private addFailureAtNodeWithMessage(node: ts.Node, failureMessage: string) {
187 this.addFailureAtNode(
188 node,
189 GeneralRuleUtils.buildFailureString(
190 failureMessage,
191 IMPORTS_BETWEEN_PACKAGES_RULE_ID
192 )
193 );
194 }
195}