UNPKG

5.25 kBJavaScriptView Raw
1const _bindAll = require('lodash.bindall');
2const _keys = require('lodash.keys');
3const CallChain = require('./CallChain');
4
5class DependencyError {
6 constructor(callChain, error, errorObject) {
7 this.callChain = callChain;
8 this.error = error;
9 this.errorObject = errorObject;
10 }
11
12 print(logger) {
13 logger.error(
14 `${this.error} ${this.callChain.getHighlightedName()} in `,
15 this.callChain.getPath(true)
16 );
17 if (this.errorObject) {
18 logger.error(this.errorObject);
19 logger.error(this.errorObject.stack);
20 }
21 }
22
23 static cyclic(callChain) {
24 return new DependencyError(callChain, 'Cyclic Dependency');
25 }
26
27 static missingDependency(callChain) {
28 return new DependencyError(callChain, 'Missing Dependency');
29 }
30
31 static exception(callChain, exception) {
32 return new DependencyError(callChain, 'Exception in Dependency', exception);
33 }
34}
35
36class DependencyWarning {
37 constructor(callChain, name, warningObject) {
38 this.callChain = callChain;
39 this.name = name;
40 this.warningObject = warningObject;
41 }
42
43 print(logger) {
44 return logger.warn(
45 `${this.name} ${this.callChain.getHighlightedName()} in `,
46 this.callChain.getPath(true)
47 );
48 }
49
50 static unused(callChain) {
51 return new DependencyWarning(callChain, 'Unused dependency');
52 }
53}
54
55class DependencyResolver {
56 constructor(container, name, logger) {
57 _bindAll(this, 'resolveDependencies', 'resolveRegex');
58 this.container = container;
59 this.name = name;
60 this.logger = logger;
61 this.errors = [];
62 this.warnings = [];
63 this.resolvingFinished = false;
64 }
65
66 resolveArray(deps, callChain) {
67 let name;
68 const instances = [];
69 for (name of deps) {
70 instances.push(this.resolveDependencies(name, callChain));
71 }
72 return instances;
73 }
74
75 resolveMap(deps, callChain) {
76 let name;
77 const instances = {};
78 for (name of deps) {
79 instances[name] = this.resolveDependencies(name, callChain);
80 }
81 return instances;
82 }
83
84 resolveRegex(regex) {
85 const deps = _keys(this.container.dependencies)
86 .filter((key) => { // eslint-disable-line
87 return regex.test(key) && key !== '$injector' && key !== this.container.privateInjectorName();
88 });
89 return this.resolveMap(deps);
90 }
91
92 addInjectorDependency() {
93 this.$injector = this.container.addDependency('$injector', {
94 get: (name) => {
95 this.checkResolvingFinished(`cannot use $injector.get('${name}') asynchronously`);
96 return this.resolveDependencies(name, this.currentCallChain);
97 },
98 getRegex: (regex) => {
99 this.checkResolvingFinished(`cannot use $injector.getRegex(${regex}) asynchronously`);
100 return this.resolveRegex(regex, this.currentCallChain);
101 },
102 getMap: (deps) => {
103 this.checkResolvingFinished('cannot use $injector.getArray() asynchronously');
104 return this.resolveMap(deps, this.currentCallChain);
105 },
106 getAll: () => {
107 this.checkResolvingFinished('cannot use $injector.getAll() asynchronously');
108 return this.resolveRegex(/.+/, this.currentCallChain);
109 }
110 }, true).instance;
111
112 return this.$injector;
113 }
114
115 checkResolvingFinished(message) {
116 if (this.resolvingFinished) {
117 throw new Error(message);
118 }
119 }
120
121 resolveDependencies(name, chain) {
122 const callChain = this.currentCallChain = chain ? chain.add(name) : CallChain.create(name);
123 const dep = this.container.getDependency(name);
124 if (dep) {
125 if (dep.instance) {
126 return dep.instance;
127 }
128
129 if (callChain.hasCyclic()) {
130 return this.errors.push(DependencyError.cyclic(callChain));
131 }
132
133 const instances = this.resolveArray(dep.dependencies, callChain);
134
135 try {
136 let dependency;
137 const dependencies = dep.dependencies;
138 for (dependency of dependencies) {
139 if (dep.fn.toString().split(dependency).length <= 2) {
140 this.warnings.push(DependencyWarning.unused(callChain.add(dependency)));
141 }
142 }
143 dep.instance = dep.fn.apply(null, instances);
144 } catch (error) {
145 this.errors.push(DependencyError.exception(callChain, error));
146 }
147
148 return dep.instance;
149 }
150
151 this.errors.push(DependencyError.missingDependency(callChain));
152 return null;
153 }
154
155 printErrors() {
156 let error;
157 const results = [];
158 for (error of this.errors) {
159 results.push(error.print(this.logger));
160 }
161 return results;
162 }
163
164 printWarnings() {
165 let warning;
166 const results = [];
167 for (warning of this.warnings) {
168 results.push(warning.print(this.logger));
169 }
170 return results;
171 }
172
173 throwError() {
174 throw new Error('Resolver encountered errors');
175 }
176
177 resolve() {
178 this.addInjectorDependency();
179 this.dependency = this.resolveDependencies(this.name);
180 this.printWarnings();
181 if (this.errors.length > 0) {
182 this.printErrors();
183 this.throwError();
184 }
185 this.resolvingFinished = true;
186 return this.dependency;
187 }
188
189 static resolve(container, name, logger) {
190 const resolver = new DependencyResolver(container, name, logger);
191 resolver.resolve();
192 return resolver;
193 }
194}
195
196module.exports = DependencyResolver;