(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.didi = {}))); }(this, (function (exports) { 'use strict'; var CLASS_PATTERN = /^class /; function isClass(fn) { return CLASS_PATTERN.test(fn.toString()); } function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } function annotate() { var args = Array.prototype.slice.call(arguments); if (args.length === 1 && isArray(args[0])) { args = args[0]; } var fn = args.pop(); fn.$inject = args; return fn; } // Current limitations: // - can't put into "function arg" comments // function /* (no parenthesis like this) */ (){} // function abc( /* xx (no parenthesis like this) */ a, b) {} // // Just put the comment before function or inside: // /* (((this is fine))) */ function(a, b) {} // function abc(a) { /* (((this is fine))) */} // // - can't reliably auto-annotate constructor; we'll match the // first constructor(...) pattern found which may be the one // of a nested class, too. var CONSTRUCTOR_ARGS = /constructor\s*[^(]*\(\s*([^)]*)\)/m; var FN_ARGS = /^function\s*[^(]*\(\s*([^)]*)\)/m; var FN_ARG = /\/\*([^*]*)\*\//m; function parse(fn) { if (typeof fn !== 'function') { throw new Error('Cannot annotate "' + fn + '". Expected a function!'); } var match = fn.toString().match(isClass(fn) ? CONSTRUCTOR_ARGS : FN_ARGS); // may parse class without constructor if (!match) { return []; } return match[1] && match[1].split(',').map(function (arg) { match = arg.match(FN_ARG); return match ? match[1].trim() : arg.trim(); }) || []; } function Module() { var providers = []; this.factory = function (name, factory) { providers.push([name, 'factory', factory]); return this; }; this.value = function (name, value) { providers.push([name, 'value', value]); return this; }; this.type = function (name, type) { providers.push([name, 'type', type]); return this; }; this.forEach = function (iterator) { providers.forEach(iterator); }; } var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function Injector(modules, parent) { parent = parent || { get: function get(name, strict) { currentlyResolving.push(name); if (strict === false) { return null; } else { throw error('No provider for "' + name + '"!'); } } }; var currentlyResolving = []; var providers = this._providers = Object.create(parent._providers || null); var instances = this._instances = Object.create(null); var self = instances.injector = this; var error = function error(msg) { var stack = currentlyResolving.join(' -> '); currentlyResolving.length = 0; return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg); }; /** * Return a named service. * * @param {String} name * @param {Boolean} [strict=true] if false, resolve missing services to null * * @return {Object} */ var get = function get(name, strict) { if (!providers[name] && name.indexOf('.') !== -1) { var parts = name.split('.'); var pivot = get(parts.shift()); while (parts.length) { pivot = pivot[parts.shift()]; } return pivot; } if (hasProp(instances, name)) { return instances[name]; } if (hasProp(providers, name)) { if (currentlyResolving.indexOf(name) !== -1) { currentlyResolving.push(name); throw error('Cannot resolve circular dependency!'); } currentlyResolving.push(name); instances[name] = providers[name][0](providers[name][1]); currentlyResolving.pop(); return instances[name]; } return parent.get(name, strict); }; var fnDef = function fnDef(fn) { var locals = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (typeof fn !== 'function') { if (isArray(fn)) { fn = annotate(fn.slice()); } else { throw new Error('Cannot invoke "' + fn + '". Expected a function!'); } } var inject = fn.$inject || parse(fn); var dependencies = inject.map(function (dep) { if (hasProp(locals, dep)) { return locals[dep]; } else { return get(dep); } }); return { fn: fn, dependencies: dependencies }; }; var instantiate = function instantiate(Type) { var _fnDef = fnDef(Type), dependencies = _fnDef.dependencies, fn = _fnDef.fn; return new (Function.prototype.bind.apply(fn, [null].concat(_toConsumableArray(dependencies))))(); }; var invoke = function invoke(func, context, locals) { var _fnDef2 = fnDef(func, locals), dependencies = _fnDef2.dependencies, fn = _fnDef2.fn; return fn.call.apply(fn, [context].concat(_toConsumableArray(dependencies))); }; var createPrivateInjectorFactory = function createPrivateInjectorFactory(privateChildInjector) { return annotate(function (key) { return privateChildInjector.get(key); }); }; var createChild = function createChild(modules, forceNewInstances) { if (forceNewInstances && forceNewInstances.length) { var fromParentModule = Object.create(null); var matchedScopes = Object.create(null); var privateInjectorsCache = []; var privateChildInjectors = []; var privateChildFactories = []; var provider; var cacheIdx; var privateChildInjector; var privateChildInjectorFactory; for (var name in providers) { provider = providers[name]; if (forceNewInstances.indexOf(name) !== -1) { if (provider[2] === 'private') { cacheIdx = privateInjectorsCache.indexOf(provider[3]); if (cacheIdx === -1) { privateChildInjector = provider[3].createChild([], forceNewInstances); privateChildInjectorFactory = createPrivateInjectorFactory(privateChildInjector); privateInjectorsCache.push(provider[3]); privateChildInjectors.push(privateChildInjector); privateChildFactories.push(privateChildInjectorFactory); fromParentModule[name] = [privateChildInjectorFactory, name, 'private', privateChildInjector]; } else { fromParentModule[name] = [privateChildFactories[cacheIdx], name, 'private', privateChildInjectors[cacheIdx]]; } } else { fromParentModule[name] = [provider[2], provider[1]]; } matchedScopes[name] = true; } if ((provider[2] === 'factory' || provider[2] === 'type') && provider[1].$scope) { /* jshint -W083 */ forceNewInstances.forEach(function (scope) { if (provider[1].$scope.indexOf(scope) !== -1) { fromParentModule[name] = [provider[2], provider[1]]; matchedScopes[scope] = true; } }); } } forceNewInstances.forEach(function (scope) { if (!matchedScopes[scope]) { throw new Error('No provider for "' + scope + '". Cannot use provider from the parent!'); } }); modules.unshift(fromParentModule); } return new Injector(modules, self); }; var factoryMap = { factory: invoke, type: instantiate, value: function value(_value) { return _value; } }; modules.forEach(function (module) { function arrayUnwrap(type, value) { if (type !== 'value' && isArray(value)) { value = annotate(value.slice()); } return value; } // TODO(vojta): handle wrong inputs (modules) if (module instanceof Module) { module.forEach(function (provider) { var name = provider[0]; var type = provider[1]; var value = provider[2]; providers[name] = [factoryMap[type], arrayUnwrap(type, value), type]; }); } else if ((typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object') { if (module.__exports__) { var clonedModule = Object.keys(module).reduce(function (m, key) { if (key.substring(0, 2) !== '__') { m[key] = module[key]; } return m; }, Object.create(null)); var privateInjector = new Injector((module.__modules__ || []).concat([clonedModule]), self); var getFromPrivateInjector = annotate(function (key) { return privateInjector.get(key); }); module.__exports__.forEach(function (key) { providers[key] = [getFromPrivateInjector, key, 'private', privateInjector]; }); } else { Object.keys(module).forEach(function (name) { if (module[name][2] === 'private') { providers[name] = module[name]; return; } var type = module[name][0]; var value = module[name][1]; providers[name] = [factoryMap[type], arrayUnwrap(type, value), type]; }); } } }); // public API this.get = get; this.invoke = invoke; this.instantiate = instantiate; this.createChild = createChild; } // helpers ///////////////// function hasProp(obj, prop) { return Object.hasOwnProperty.call(obj, prop); } exports.annotate = annotate; exports.Module = Module; exports.Injector = Injector; Object.defineProperty(exports, '__esModule', { value: true }); })));