1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.Rule = void 0;
|
4 | const Lint = require("tslint");
|
5 | const devkit_1 = require("@nrwl/devkit");
|
6 | const runtime_lint_utils_1 = require("../utils/runtime-lint-utils");
|
7 | const path_1 = require("path");
|
8 | const target_project_locator_1 = require("nx/src/utils/target-project-locator");
|
9 | const graph_utils_1 = require("../utils/graph-utils");
|
10 | const fileutils_1 = require("../utilities/fileutils");
|
11 | class Rule extends Lint.Rules.AbstractRule {
|
12 | constructor(options, projectPath, projectGraph, targetProjectLocator, workspaceLayout) {
|
13 | super(options);
|
14 | this.projectPath = projectPath;
|
15 | this.projectGraph = projectGraph;
|
16 | this.targetProjectLocator = targetProjectLocator;
|
17 | this.workspaceLayout = workspaceLayout;
|
18 | if (!projectPath) {
|
19 | this.projectPath = (0, path_1.normalize)(devkit_1.workspaceRoot);
|
20 | if (!global.projectGraph) {
|
21 | const nxJson = (0, devkit_1.readNxJson)();
|
22 | global.workspaceLayout = nxJson.workspaceLayout;
|
23 | |
24 |
|
25 |
|
26 |
|
27 | try {
|
28 | global.projectGraph = (0, runtime_lint_utils_1.mapProjectGraphFiles)((0, devkit_1.readCachedProjectGraph)());
|
29 | }
|
30 | catch (_a) { }
|
31 | }
|
32 | this.workspaceLayout = global.workspaceLayout;
|
33 | this.projectGraph = global.projectGraph;
|
34 | if (!global.targetProjectLocator && this.projectGraph) {
|
35 | global.targetProjectLocator = new target_project_locator_1.TargetProjectLocator(this.projectGraph.nodes, this.projectGraph.externalNodes);
|
36 | }
|
37 | this.targetProjectLocator = global.targetProjectLocator;
|
38 | }
|
39 | }
|
40 | apply(sourceFile) {
|
41 | if (!this.projectGraph)
|
42 | return [];
|
43 | return this.applyWithWalker(new EnforceModuleBoundariesWalker(sourceFile, this.getOptions(), this.projectPath, this.projectGraph, this.targetProjectLocator, this.workspaceLayout));
|
44 | }
|
45 | }
|
46 | exports.Rule = Rule;
|
47 | class EnforceModuleBoundariesWalker extends Lint.RuleWalker {
|
48 | constructor(sourceFile, options, projectPath, projectGraph, targetProjectLocator, workspaceLayout) {
|
49 | super(sourceFile, options);
|
50 | this.projectPath = projectPath;
|
51 | this.projectGraph = projectGraph;
|
52 | this.targetProjectLocator = targetProjectLocator;
|
53 | this.workspaceLayout = workspaceLayout;
|
54 | this.enforceBuildableLibDependency = false;
|
55 | this.allowCircularSelfDependency = false;
|
56 | this.allow = Array.isArray(this.getOptions()[0].allow)
|
57 | ? this.getOptions()[0].allow.map((a) => `${a}`)
|
58 | : [];
|
59 | this.depConstraints = Array.isArray(this.getOptions()[0].depConstraints)
|
60 | ? this.getOptions()[0].depConstraints
|
61 | : [];
|
62 | this.enforceBuildableLibDependency =
|
63 | this.getOptions()[0].enforceBuildableLibDependency === true;
|
64 | this.allowCircularSelfDependency =
|
65 | this.getOptions()[0].allowCircularSelfDependency === true;
|
66 | }
|
67 | visitImportDeclaration(node) {
|
68 | const imp = node.moduleSpecifier
|
69 | .getText()
|
70 | .substring(1, node.moduleSpecifier.getText().length - 1);
|
71 |
|
72 | if (this.allow.some((a) => (0, runtime_lint_utils_1.matchImportWithWildcard)(a, imp))) {
|
73 | super.visitImportDeclaration(node);
|
74 | return;
|
75 | }
|
76 | const filePath = (0, runtime_lint_utils_1.getSourceFilePath)(this.getSourceFile().fileName, this.projectPath);
|
77 | const sourceProject = (0, runtime_lint_utils_1.findSourceProject)(this.projectGraph, filePath);
|
78 | if (!sourceProject) {
|
79 | super.visitImportDeclaration(node);
|
80 | return;
|
81 | }
|
82 | let targetProject = (0, runtime_lint_utils_1.getTargetProjectBasedOnRelativeImport)(imp, this.projectPath, this.projectGraph, filePath);
|
83 |
|
84 | if ((targetProject && sourceProject !== targetProject) ||
|
85 | (0, runtime_lint_utils_1.isAbsoluteImportIntoAnotherProject)(imp, this.workspaceLayout)) {
|
86 | this.addFailureAt(node.getStart(), node.getWidth(), `libraries cannot be imported by a relative or absolute path, and must begin with a npm scope`);
|
87 | return;
|
88 | }
|
89 | targetProject =
|
90 | targetProject ||
|
91 | (0, runtime_lint_utils_1.findProjectUsingImport)(this.projectGraph, this.targetProjectLocator, filePath, imp);
|
92 |
|
93 | if (!targetProject || targetProject.type === 'npm') {
|
94 | super.visitImportDeclaration(node);
|
95 | return;
|
96 | }
|
97 |
|
98 | if (sourceProject === targetProject) {
|
99 | if (!this.allowCircularSelfDependency &&
|
100 | !(0, fileutils_1.isRelativePath)(imp) &&
|
101 | !(0, runtime_lint_utils_1.isAngularSecondaryEntrypoint)(this.targetProjectLocator, imp)) {
|
102 | const error = `Projects should use relative imports to import from other files within the same project. Use "./path/to/file" instead of import from "${imp}"`;
|
103 | this.addFailureAt(node.getStart(), node.getWidth(), error);
|
104 | }
|
105 | else {
|
106 | super.visitImportDeclaration(node);
|
107 | }
|
108 | return;
|
109 | }
|
110 |
|
111 | const circularPath = (0, graph_utils_1.checkCircularPath)(this.projectGraph, sourceProject, targetProject);
|
112 | if (circularPath.length !== 0) {
|
113 | const path = circularPath.reduce((acc, v) => `${acc} -> ${v.name}`, sourceProject.name);
|
114 | const circularFilePath = (0, graph_utils_1.findFilesInCircularPath)(circularPath);
|
115 | const filePaths = circularFilePath
|
116 | .map((files) => (files.length > 1 ? `[${files.join(',')}]` : files[0]))
|
117 | .reduce((acc, files) => `${acc}\n- ${files}`, `- ${filePath}`);
|
118 | const error = `Circular dependency between "${sourceProject.name}" and "${targetProject.name}" detected: ${path}\n\nCircular file chain:\n${filePaths}`;
|
119 | this.addFailureAt(node.getStart(), node.getWidth(), error);
|
120 | return;
|
121 | }
|
122 |
|
123 | if (targetProject.type === 'app') {
|
124 | this.addFailureAt(node.getStart(), node.getWidth(), 'imports of apps are forbidden');
|
125 | return;
|
126 | }
|
127 |
|
128 | if (targetProject.type === 'e2e') {
|
129 | this.addFailureAt(node.getStart(), node.getWidth(), 'imports of e2e projects are forbidden');
|
130 | return;
|
131 | }
|
132 |
|
133 | if (this.enforceBuildableLibDependency === true &&
|
134 | sourceProject.type === 'lib' &&
|
135 | targetProject.type === 'lib') {
|
136 | if ((0, runtime_lint_utils_1.hasBuildExecutor)(sourceProject) && !(0, runtime_lint_utils_1.hasBuildExecutor)(targetProject)) {
|
137 | this.addFailureAt(node.getStart(), node.getWidth(), 'buildable libraries cannot import non-buildable libraries');
|
138 | return;
|
139 | }
|
140 | }
|
141 |
|
142 | if ((0, runtime_lint_utils_1.onlyLoadChildren)(this.projectGraph, sourceProject.name, targetProject.name, [])) {
|
143 | this.addFailureAt(node.getStart(), node.getWidth(), 'imports of lazy-loaded libraries are forbidden');
|
144 | return;
|
145 | }
|
146 |
|
147 | if (this.depConstraints.length > 0) {
|
148 | const constraints = (0, runtime_lint_utils_1.findConstraintsFor)(this.depConstraints, sourceProject);
|
149 |
|
150 | if (constraints.length === 0) {
|
151 | this.addFailureAt(node.getStart(), node.getWidth(), `A project without tags matching at least one constraint cannot depend on any libraries`);
|
152 | return;
|
153 | }
|
154 | for (let constraint of constraints) {
|
155 | if (constraint.onlyDependOnLibsWithTags &&
|
156 | (0, runtime_lint_utils_1.hasNoneOfTheseTags)(targetProject, constraint.onlyDependOnLibsWithTags)) {
|
157 | const error = `A project tagged with "${constraint.sourceTag}" can only depend on libs tagged with ${(0, runtime_lint_utils_1.stringifyTags)(constraint.onlyDependOnLibsWithTags)}`;
|
158 | this.addFailureAt(node.getStart(), node.getWidth(), error);
|
159 | return;
|
160 | }
|
161 | if (constraint.notDependOnLibsWithTags &&
|
162 | constraint.notDependOnLibsWithTags.length) {
|
163 | const projectPaths = (0, runtime_lint_utils_1.findDependenciesWithTags)(targetProject, constraint.notDependOnLibsWithTags, this.projectGraph);
|
164 | if (projectPaths.length > 0) {
|
165 | const error = `A project tagged with "${constraint.sourceTag}" can not depend on libs tagged with ${(0, runtime_lint_utils_1.stringifyTags)(constraint.notDependOnLibsWithTags)}\n\nViolation detected in:\n${projectPaths
|
166 | .map((projectPath) => `- ${projectPath.map((p) => p.name).join(' -> ')}`)
|
167 | .join('\n')}`;
|
168 | this.addFailureAt(node.getStart(), node.getWidth(), error);
|
169 | return;
|
170 | }
|
171 | }
|
172 | }
|
173 | }
|
174 | super.visitImportDeclaration(node);
|
175 | }
|
176 | }
|
177 |
|
\ | No newline at end of file |