injector.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. var Module = require('./module');
  2. var autoAnnotate = require('./annotation').parse;
  3. var Injector = function(modules, parent) {
  4. parent = parent || {
  5. get: function(name) {
  6. currentlyResolving.push(name);
  7. throw error('No provider for "' + name + '"!');
  8. }
  9. };
  10. var currentlyResolving = [];
  11. var providers = this._providers = Object.create(parent._providers || null);
  12. var instances = this._instances = Object.create(null);
  13. instances.injector = this;
  14. var error = function(msg) {
  15. var stack = currentlyResolving.join(' -> ');
  16. currentlyResolving.length = 0;
  17. return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg);
  18. };
  19. var get = function(name) {
  20. if (!providers[name] && name.indexOf('.') !== -1) {
  21. var parts = name.split('.');
  22. var pivot = get(parts.shift());
  23. while(parts.length) {
  24. pivot = pivot[parts.shift()];
  25. }
  26. return pivot;
  27. }
  28. if (Object.hasOwnProperty.call(instances, name)) {
  29. return instances[name];
  30. }
  31. if (Object.hasOwnProperty.call(providers, name)) {
  32. if (currentlyResolving.indexOf(name) !== -1) {
  33. currentlyResolving.push(name);
  34. throw error('Can not resolve circular dependency!');
  35. }
  36. currentlyResolving.push(name);
  37. instances[name] = providers[name][0](providers[name][1]);
  38. currentlyResolving.pop();
  39. return instances[name];
  40. }
  41. return parent.get(name);
  42. };
  43. var instantiate = function(Type) {
  44. var instance = Object.create(Type.prototype);
  45. var returned = invoke(Type, instance);
  46. return typeof returned === 'object' ? returned : instance;
  47. };
  48. var invoke = function(fn, context) {
  49. if (typeof fn !== 'function') {
  50. throw error('Can not invoke "' + fn + '". Expected a function!');
  51. }
  52. var inject = fn.$inject && fn.$inject || autoAnnotate(fn);
  53. var dependencies = inject.map(function(dep) {
  54. return get(dep);
  55. });
  56. // TODO(vojta): optimize without apply
  57. return fn.apply(context, dependencies);
  58. };
  59. var createChild = function(modules, providersFromParent) {
  60. if (providersFromParent && providersFromParent.length) {
  61. var fromParentModule = Object.create(null);
  62. providersFromParent.forEach(function(name) {
  63. if (!providers[name]) {
  64. throw new Error('No provider for "' + name + '". Can not use provider from the parent!');
  65. }
  66. fromParentModule[name] = [providers[name][2], providers[name][1]];
  67. });
  68. modules.unshift(fromParentModule);
  69. }
  70. return new Injector(modules, this);
  71. };
  72. var factoryMap = {
  73. factory: invoke,
  74. type: instantiate,
  75. value: function(value) {
  76. return value;
  77. }
  78. };
  79. modules.forEach(function(module) {
  80. // TODO(vojta): handle wrong inputs (modules)
  81. if (module instanceof Module) {
  82. module.forEach(function(provider) {
  83. var name = provider[0];
  84. var type = provider[1];
  85. var value = provider[2];
  86. providers[name] = [factoryMap[type], value, type];
  87. });
  88. } else if (typeof module === 'object') {
  89. Object.keys(module).forEach(function(name) {
  90. var type = module[name][0];
  91. var value = module[name][1];
  92. providers[name] = [factoryMap[type], value, type];
  93. });
  94. }
  95. });
  96. // public API
  97. this.get = get;
  98. this.invoke = invoke;
  99. this.instantiate = instantiate;
  100. this.createChild = createChild;
  101. };
  102. module.exports = Injector;