index.esm.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. var CLASS_PATTERN = /^class /;
  2. function isClass(fn) {
  3. return CLASS_PATTERN.test(fn.toString());
  4. }
  5. function isArray(obj) {
  6. return Object.prototype.toString.call(obj) === '[object Array]';
  7. }
  8. function annotate() {
  9. var args = Array.prototype.slice.call(arguments);
  10. if (args.length === 1 && isArray(args[0])) {
  11. args = args[0];
  12. }
  13. var fn = args.pop();
  14. fn.$inject = args;
  15. return fn;
  16. }
  17. // Current limitations:
  18. // - can't put into "function arg" comments
  19. // function /* (no parenthesis like this) */ (){}
  20. // function abc( /* xx (no parenthesis like this) */ a, b) {}
  21. //
  22. // Just put the comment before function or inside:
  23. // /* (((this is fine))) */ function(a, b) {}
  24. // function abc(a) { /* (((this is fine))) */}
  25. //
  26. // - can't reliably auto-annotate constructor; we'll match the
  27. // first constructor(...) pattern found which may be the one
  28. // of a nested class, too.
  29. var CONSTRUCTOR_ARGS = /constructor\s*[^(]*\(\s*([^)]*)\)/m;
  30. var FN_ARGS = /^function\s*[^(]*\(\s*([^)]*)\)/m;
  31. var FN_ARG = /\/\*([^*]*)\*\//m;
  32. function parse(fn) {
  33. if (typeof fn !== 'function') {
  34. throw new Error('Cannot annotate "' + fn + '". Expected a function!');
  35. }
  36. var match = fn.toString().match(isClass(fn) ? CONSTRUCTOR_ARGS : FN_ARGS);
  37. // may parse class without constructor
  38. if (!match) {
  39. return [];
  40. }
  41. return match[1] && match[1].split(',').map(function (arg) {
  42. match = arg.match(FN_ARG);
  43. return match ? match[1].trim() : arg.trim();
  44. }) || [];
  45. }
  46. function Module() {
  47. var providers = [];
  48. this.factory = function (name, factory) {
  49. providers.push([name, 'factory', factory]);
  50. return this;
  51. };
  52. this.value = function (name, value) {
  53. providers.push([name, 'value', value]);
  54. return this;
  55. };
  56. this.type = function (name, type) {
  57. providers.push([name, 'type', type]);
  58. return this;
  59. };
  60. this.forEach = function (iterator) {
  61. providers.forEach(iterator);
  62. };
  63. }
  64. 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; };
  65. 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); } }
  66. function Injector(modules, parent) {
  67. parent = parent || {
  68. get: function get(name, strict) {
  69. currentlyResolving.push(name);
  70. if (strict === false) {
  71. return null;
  72. } else {
  73. throw error('No provider for "' + name + '"!');
  74. }
  75. }
  76. };
  77. var currentlyResolving = [];
  78. var providers = this._providers = Object.create(parent._providers || null);
  79. var instances = this._instances = Object.create(null);
  80. var self = instances.injector = this;
  81. var error = function error(msg) {
  82. var stack = currentlyResolving.join(' -> ');
  83. currentlyResolving.length = 0;
  84. return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg);
  85. };
  86. /**
  87. * Return a named service.
  88. *
  89. * @param {String} name
  90. * @param {Boolean} [strict=true] if false, resolve missing services to null
  91. *
  92. * @return {Object}
  93. */
  94. var get = function get(name, strict) {
  95. if (!providers[name] && name.indexOf('.') !== -1) {
  96. var parts = name.split('.');
  97. var pivot = get(parts.shift());
  98. while (parts.length) {
  99. pivot = pivot[parts.shift()];
  100. }
  101. return pivot;
  102. }
  103. if (hasProp(instances, name)) {
  104. return instances[name];
  105. }
  106. if (hasProp(providers, name)) {
  107. if (currentlyResolving.indexOf(name) !== -1) {
  108. currentlyResolving.push(name);
  109. throw error('Cannot resolve circular dependency!');
  110. }
  111. currentlyResolving.push(name);
  112. instances[name] = providers[name][0](providers[name][1]);
  113. currentlyResolving.pop();
  114. return instances[name];
  115. }
  116. return parent.get(name, strict);
  117. };
  118. var fnDef = function fnDef(fn) {
  119. var locals = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  120. if (typeof fn !== 'function') {
  121. if (isArray(fn)) {
  122. fn = annotate(fn.slice());
  123. } else {
  124. throw new Error('Cannot invoke "' + fn + '". Expected a function!');
  125. }
  126. }
  127. var inject = fn.$inject || parse(fn);
  128. var dependencies = inject.map(function (dep) {
  129. if (hasProp(locals, dep)) {
  130. return locals[dep];
  131. } else {
  132. return get(dep);
  133. }
  134. });
  135. return {
  136. fn: fn,
  137. dependencies: dependencies
  138. };
  139. };
  140. var instantiate = function instantiate(Type) {
  141. var _fnDef = fnDef(Type),
  142. dependencies = _fnDef.dependencies,
  143. fn = _fnDef.fn;
  144. return new (Function.prototype.bind.apply(fn, [null].concat(_toConsumableArray(dependencies))))();
  145. };
  146. var invoke = function invoke(func, context, locals) {
  147. var _fnDef2 = fnDef(func, locals),
  148. dependencies = _fnDef2.dependencies,
  149. fn = _fnDef2.fn;
  150. return fn.call.apply(fn, [context].concat(_toConsumableArray(dependencies)));
  151. };
  152. var createPrivateInjectorFactory = function createPrivateInjectorFactory(privateChildInjector) {
  153. return annotate(function (key) {
  154. return privateChildInjector.get(key);
  155. });
  156. };
  157. var createChild = function createChild(modules, forceNewInstances) {
  158. if (forceNewInstances && forceNewInstances.length) {
  159. var fromParentModule = Object.create(null);
  160. var matchedScopes = Object.create(null);
  161. var privateInjectorsCache = [];
  162. var privateChildInjectors = [];
  163. var privateChildFactories = [];
  164. var provider;
  165. var cacheIdx;
  166. var privateChildInjector;
  167. var privateChildInjectorFactory;
  168. for (var name in providers) {
  169. provider = providers[name];
  170. if (forceNewInstances.indexOf(name) !== -1) {
  171. if (provider[2] === 'private') {
  172. cacheIdx = privateInjectorsCache.indexOf(provider[3]);
  173. if (cacheIdx === -1) {
  174. privateChildInjector = provider[3].createChild([], forceNewInstances);
  175. privateChildInjectorFactory = createPrivateInjectorFactory(privateChildInjector);
  176. privateInjectorsCache.push(provider[3]);
  177. privateChildInjectors.push(privateChildInjector);
  178. privateChildFactories.push(privateChildInjectorFactory);
  179. fromParentModule[name] = [privateChildInjectorFactory, name, 'private', privateChildInjector];
  180. } else {
  181. fromParentModule[name] = [privateChildFactories[cacheIdx], name, 'private', privateChildInjectors[cacheIdx]];
  182. }
  183. } else {
  184. fromParentModule[name] = [provider[2], provider[1]];
  185. }
  186. matchedScopes[name] = true;
  187. }
  188. if ((provider[2] === 'factory' || provider[2] === 'type') && provider[1].$scope) {
  189. /* jshint -W083 */
  190. forceNewInstances.forEach(function (scope) {
  191. if (provider[1].$scope.indexOf(scope) !== -1) {
  192. fromParentModule[name] = [provider[2], provider[1]];
  193. matchedScopes[scope] = true;
  194. }
  195. });
  196. }
  197. }
  198. forceNewInstances.forEach(function (scope) {
  199. if (!matchedScopes[scope]) {
  200. throw new Error('No provider for "' + scope + '". Cannot use provider from the parent!');
  201. }
  202. });
  203. modules.unshift(fromParentModule);
  204. }
  205. return new Injector(modules, self);
  206. };
  207. var factoryMap = {
  208. factory: invoke,
  209. type: instantiate,
  210. value: function value(_value) {
  211. return _value;
  212. }
  213. };
  214. modules.forEach(function (module) {
  215. function arrayUnwrap(type, value) {
  216. if (type !== 'value' && isArray(value)) {
  217. value = annotate(value.slice());
  218. }
  219. return value;
  220. }
  221. // TODO(vojta): handle wrong inputs (modules)
  222. if (module instanceof Module) {
  223. module.forEach(function (provider) {
  224. var name = provider[0];
  225. var type = provider[1];
  226. var value = provider[2];
  227. providers[name] = [factoryMap[type], arrayUnwrap(type, value), type];
  228. });
  229. } else if ((typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object') {
  230. if (module.__exports__) {
  231. var clonedModule = Object.keys(module).reduce(function (m, key) {
  232. if (key.substring(0, 2) !== '__') {
  233. m[key] = module[key];
  234. }
  235. return m;
  236. }, Object.create(null));
  237. var privateInjector = new Injector((module.__modules__ || []).concat([clonedModule]), self);
  238. var getFromPrivateInjector = annotate(function (key) {
  239. return privateInjector.get(key);
  240. });
  241. module.__exports__.forEach(function (key) {
  242. providers[key] = [getFromPrivateInjector, key, 'private', privateInjector];
  243. });
  244. } else {
  245. Object.keys(module).forEach(function (name) {
  246. if (module[name][2] === 'private') {
  247. providers[name] = module[name];
  248. return;
  249. }
  250. var type = module[name][0];
  251. var value = module[name][1];
  252. providers[name] = [factoryMap[type], arrayUnwrap(type, value), type];
  253. });
  254. }
  255. }
  256. });
  257. // public API
  258. this.get = get;
  259. this.invoke = invoke;
  260. this.instantiate = instantiate;
  261. this.createChild = createChild;
  262. }
  263. // helpers /////////////////
  264. function hasProp(obj, prop) {
  265. return Object.hasOwnProperty.call(obj, prop);
  266. }
  267. export { annotate, Module, Injector };