UNPKG

10.4 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.createDisposableResolver = exports.createBuildResolver = exports.aliasTo = exports.asClass = exports.asFunction = exports.asValue = exports.RESOLVER = void 0;
4const lifetime_1 = require("./lifetime");
5const injection_mode_1 = require("./injection-mode");
6const utils_1 = require("./utils");
7const param_parser_1 = require("./param-parser");
8const errors_1 = require("./errors");
9/**
10 * RESOLVER symbol can be used by modules loaded by
11 * `loadModules` to configure their lifetime, injection mode, etc.
12 */
13exports.RESOLVER = Symbol('Awilix Resolver Config');
14/**
15 * Creates a simple value resolver where the given value will always be resolved.
16 *
17 * @param {string} name
18 * The name to register the value as.
19 *
20 * @param {*} value
21 * The value to resolve.
22 *
23 * @return {object}
24 * The resolver.
25 */
26function asValue(value) {
27 return {
28 resolve: () => value,
29 };
30}
31exports.asValue = asValue;
32/**
33 * Creates a factory resolver, where the given factory function
34 * will be invoked with `new` when requested.
35 *
36 * @param {string} name
37 * The name to register the value as.
38 *
39 * @param {Function} fn
40 * The function to register.
41 *
42 * @param {object} opts
43 * Additional options for the resolver.
44 *
45 * @return {object}
46 * The resolver.
47 */
48function asFunction(fn, opts) {
49 if (!(0, utils_1.isFunction)(fn)) {
50 throw new errors_1.AwilixTypeError('asFunction', 'fn', 'function', fn);
51 }
52 const defaults = {
53 lifetime: lifetime_1.Lifetime.TRANSIENT,
54 };
55 opts = makeOptions(defaults, opts, fn[exports.RESOLVER]);
56 const resolve = generateResolve(fn);
57 let result = Object.assign({ resolve }, opts);
58 return createDisposableResolver(createBuildResolver(result));
59}
60exports.asFunction = asFunction;
61/**
62 * Like a factory resolver, but for classes that require `new`.
63 *
64 * @param {string} name
65 * The name to register the value as.
66 *
67 * @param {Class} Type
68 * The function to register.
69 *
70 * @param {object} opts
71 * Additional options for the resolver.
72 *
73 * @return {object}
74 * The resolver.
75 */
76function asClass(Type, opts) {
77 if (!(0, utils_1.isFunction)(Type)) {
78 throw new errors_1.AwilixTypeError('asClass', 'Type', 'class', Type);
79 }
80 const defaults = {
81 lifetime: lifetime_1.Lifetime.TRANSIENT,
82 };
83 opts = makeOptions(defaults, opts, Type[exports.RESOLVER]);
84 // A function to handle object construction for us, as to make the generateResolve more reusable
85 const newClass = function newClass() {
86 return Reflect.construct(Type, arguments);
87 };
88 const resolve = generateResolve(newClass, Type);
89 return createDisposableResolver(createBuildResolver(Object.assign(Object.assign({}, opts), { resolve })));
90}
91exports.asClass = asClass;
92/**
93 * Resolves to the specified registration.
94 */
95function aliasTo(name) {
96 return {
97 resolve(container) {
98 return container.resolve(name);
99 },
100 };
101}
102exports.aliasTo = aliasTo;
103/**
104 * Given an options object, creates a fluid interface
105 * to manage it.
106 *
107 * @param {*} obj
108 * The object to return.
109 *
110 * @return {object}
111 * The interface.
112 */
113function createBuildResolver(obj) {
114 function setLifetime(value) {
115 return createBuildResolver(Object.assign(Object.assign({}, this), { lifetime: value }));
116 }
117 function setInjectionMode(value) {
118 return createBuildResolver(Object.assign(Object.assign({}, this), { injectionMode: value }));
119 }
120 function inject(injector) {
121 return createBuildResolver(Object.assign(Object.assign({}, this), { injector }));
122 }
123 return updateResolver(obj, {
124 setLifetime,
125 inject,
126 transient: partial(setLifetime, lifetime_1.Lifetime.TRANSIENT),
127 scoped: partial(setLifetime, lifetime_1.Lifetime.SCOPED),
128 singleton: partial(setLifetime, lifetime_1.Lifetime.SINGLETON),
129 setInjectionMode,
130 proxy: partial(setInjectionMode, injection_mode_1.InjectionMode.PROXY),
131 classic: partial(setInjectionMode, injection_mode_1.InjectionMode.CLASSIC),
132 });
133}
134exports.createBuildResolver = createBuildResolver;
135/**
136 * Given a resolver, returns an object with methods to manage the disposer
137 * function.
138 * @param obj
139 */
140function createDisposableResolver(obj) {
141 function disposer(dispose) {
142 return createDisposableResolver(Object.assign(Object.assign({}, this), { dispose }));
143 }
144 return updateResolver(obj, {
145 disposer,
146 });
147}
148exports.createDisposableResolver = createDisposableResolver;
149/**
150 * Partially apply arguments to the given function.
151 */
152function partial(fn, arg1) {
153 return function partiallyApplied() {
154 return fn.call(this, arg1);
155 };
156}
157/**
158 * Makes an options object based on defaults.
159 *
160 * @param {object} defaults
161 * Default options.
162 *
163 * @param {...} rest
164 * The input to check and possibly assign to the resulting object
165 *
166 * @return {object}
167 */
168function makeOptions(defaults, ...rest) {
169 return Object.assign({}, defaults, ...rest);
170}
171/**
172 * Creates a new resolver with props merged from both.
173 *
174 * @param source
175 * @param target
176 */
177function updateResolver(source, target) {
178 const result = Object.assign(Object.assign({}, source), target);
179 return result;
180}
181/**
182 * Returns a wrapped `resolve` function that provides values
183 * from the injector and defers to `container.resolve`.
184 *
185 * @param {AwilixContainer} container
186 * @param {Object} locals
187 * @return {Function}
188 */
189function wrapWithLocals(container, locals) {
190 return function wrappedResolve(name, resolveOpts) {
191 if (name in locals) {
192 return locals[name];
193 }
194 return container.resolve(name, resolveOpts);
195 };
196}
197/**
198 * Returns a new Proxy that checks the result from `injector`
199 * for values before delegating to the actual container.
200 *
201 * @param {Object} cradle
202 * @param {Function} injector
203 * @return {Proxy}
204 */
205function createInjectorProxy(container, injector) {
206 const locals = injector(container);
207 const allKeys = (0, utils_1.uniq)([
208 ...Reflect.ownKeys(container.cradle),
209 ...Reflect.ownKeys(locals),
210 ]);
211 // TODO: Lots of duplication here from the container proxy.
212 // Need to refactor.
213 const proxy = new Proxy({}, {
214 /**
215 * Resolves the value by first checking the locals, then the container.
216 */
217 get(target, name) {
218 if (name === Symbol.iterator) {
219 return function* iterateRegistrationsAndLocals() {
220 for (const prop in container.cradle) {
221 yield prop;
222 }
223 for (const prop in locals) {
224 yield prop;
225 }
226 };
227 }
228 if (name in locals) {
229 return locals[name];
230 }
231 return container.resolve(name);
232 },
233 /**
234 * Used for `Object.keys`.
235 */
236 ownKeys() {
237 return allKeys;
238 },
239 /**
240 * Used for `Object.keys`.
241 */
242 getOwnPropertyDescriptor(target, key) {
243 if (allKeys.indexOf(key) > -1) {
244 return {
245 enumerable: true,
246 configurable: true,
247 };
248 }
249 return undefined;
250 },
251 });
252 return proxy;
253}
254/**
255 * Returns a resolve function used to construct the dependency graph
256 *
257 * @this {Registration}
258 * The `this` context is a resolver.
259 *
260 * @param {Function} fn
261 * The function to construct
262 *
263 * @param {Function} dependencyParseTarget
264 * The function to parse for the dependencies of the construction target
265 *
266 * @param {boolean} isFunction
267 * Is the resolution target an actual function or a mask for a constructor?
268 *
269 * @return {Function}
270 * The function used for dependency resolution
271 */
272function generateResolve(fn, dependencyParseTarget) {
273 // If the function used for dependency parsing is falsy, use the supplied function
274 if (!dependencyParseTarget) {
275 dependencyParseTarget = fn;
276 }
277 // Parse out the dependencies
278 // NOTE: we do this regardless of whether PROXY is used or not,
279 // because if this fails, we want it to fail early (at startup) rather
280 // than at resolution time.
281 const dependencies = parseDependencies(dependencyParseTarget);
282 // Use a regular function instead of an arrow function to facilitate binding to the resolver.
283 return function resolve(container) {
284 // Because the container holds a global reolutionMode we need to determine it in the proper order of precedence:
285 // resolver -> container -> default value
286 const injectionMode = this.injectionMode ||
287 container.options.injectionMode ||
288 injection_mode_1.InjectionMode.PROXY;
289 if (injectionMode !== injection_mode_1.InjectionMode.CLASSIC) {
290 // If we have a custom injector, we need to wrap the cradle.
291 const cradle = this.injector
292 ? createInjectorProxy(container, this.injector)
293 : container.cradle;
294 // Return the target injected with the cradle
295 return fn(cradle);
296 }
297 // We have dependencies so we need to resolve them manually
298 if (dependencies.length > 0) {
299 const resolve = this.injector
300 ? wrapWithLocals(container, this.injector(container))
301 : container.resolve;
302 const children = dependencies.map((p) => resolve(p.name, { allowUnregistered: p.optional }));
303 return fn(...children);
304 }
305 return fn();
306 };
307}
308/**
309 * Parses the dependencies from the given function.
310 * If it's a class that extends another class, and it does
311 * not have a defined constructor, attempt to parse it's super constructor.
312 */
313function parseDependencies(fn) {
314 const result = (0, param_parser_1.parseParameterList)(fn.toString());
315 if (!result) {
316 // No defined constructor for a class, check if there is a parent
317 // we can parse.
318 const parent = Object.getPrototypeOf(fn);
319 if (typeof parent === 'function' && parent !== Function.prototype) {
320 // Try to parse the parent
321 return parseDependencies(parent);
322 }
323 return [];
324 }
325 return result;
326}
327//# sourceMappingURL=resolvers.js.map
\No newline at end of file