From e9f6ec0367a7fea3431cb559a38cfdb33584779b Mon Sep 17 00:00:00 2001 From: Thomas Jarrand Date: Wed, 12 Feb 2020 14:59:47 +0100 Subject: [PATCH] Allow callback definitions --- .babelrc | 4 - README.md | 47 ++++++- dist/container.js | 7 +- dist/container.js.map | 2 +- package.json | 34 +++-- src/Container.js | 117 ++++++++++++------ src/ContainerDuplicateError.js | 9 +- src/ContainerNotFoundError.js | 8 +- ...ContainerUnsupportedDefinitionTypeError.js | 11 ++ src/index.js | 11 ++ test/Container.spec.js | 56 +++++++-- webpack.config.js | 37 +++--- 12 files changed, 243 insertions(+), 100 deletions(-) delete mode 100644 .babelrc create mode 100644 src/ContainerUnsupportedDefinitionTypeError.js create mode 100644 src/index.js diff --git a/.babelrc b/.babelrc deleted file mode 100644 index b30d23d..0000000 --- a/.babelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "presets": ["es2015"], - "plugins": ["transform-class-properties"] -} diff --git a/README.md b/README.md index bea672b..2ef50a8 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Microscopic dependency injection container Given the given class, you want to declare as a service: -```js +```javascript // MyApiClient.js export default class MyApiClient { constructor(host, key) { @@ -26,10 +26,12 @@ export default class MyApiClient { Set up your container like that: -```js +```javascript // my-container.js -import Container from '@elao/container.js'; +import { Container } from '@elao/container.js'; +import { createStore } from 'redux'; import MyApiClient from './MyApiClient'; +import reducer from './myReducer'; const container = new Container(); @@ -38,15 +40,50 @@ container.registerParameter('api:host', 'my.api.com'); container.registerParameter('api:key', 'xxxxxxxxxxx'); // Register a service: -container.registerDefinition('api', MyApiClient, ['api:host', 'api:key']); +container.registerService('api', MyApiClient, ['api:host', 'api:key', 'store']); + +// Register a callback: +container.registerCallback('store', () => createStore(reducer)); export default container; ``` Require the `api` service wherever you need it: -```js +```javascript import container from 'my-container.js'; container.get('api').login(); ``` + +### Requiring + +With ES modules: + +```javascript +import { Container } from '@elao/container.js'; +```` + +With CommonJS modules: + +```javascript +const { Container } = require('@elao/container.js'); +```` + +In the browser: + +```html + + + + + + + + + +``` diff --git a/dist/container.js b/dist/container.js index 95d0f27..72eebb2 100644 --- a/dist/container.js +++ b/dist/container.js @@ -1,7 +1,6 @@ /*! - * @elao/container.js - 2.0.1 + * @elao/container.js - 3.0.0 * https://github.com/Elao/container.js#readme - * Copyright 2017 élao + * Copyright 2020 élao */ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Container=t():e.Container=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=2)}([function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var s=function(e){function t(e,n){return r(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"A "+n+' has already been declared for the key "'+e+'".'))}return i(t,e),t}(Error);t.default=s},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var s=function(e){function t(e){return r(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,'Service or parameter "'+e+'" not found.'))}return i(t,e),t}(Error);t.default=s},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t2&&void 0!==arguments[2]?arguments[2]:[],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;e.isConstructor(n)?this.registerDefinition(t,n,r,o):this.registerParameter(t,n)}},{key:"registerDefinition",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;this.ensureUniqueness(e),this.services.set(e,{classname:t,name:e,dependencies:n,tag:r})}},{key:"registerParameter",value:function(e,t){this.ensureUniqueness(e),this.parameters.set(e,t)}},{key:"ensureUniqueness",value:function(e){if(this.parameters.has(e))throw new f.default(e,"parameter");if(this.services.has(e))throw new f.default(e,"service")}},{key:"get",value:function(e){if(this.cache.has(e))return this.cache.get(e);if(this.services.has(e))return this.resolve(this.services.get(e));if(this.parameters.has(e))return this.parameters.get(e);throw new a.default(e)}},{key:"getTaggedService",value:function(e){return Array.from(this.services.values()).filter(function(t){return t.tag===e})}},{key:"resolve",value:function(e){var t=e.dependencies.map(this.get),n=e.classname,r=new(Function.prototype.bind.apply(n,[null].concat(o(t))));return this.cache.set(e.name,r),r}}],[{key:"isConstructor",value:function(e){return"function"==typeof e&&""!==e.name}}]),e}();t.default=l}])}); -//# sourceMappingURL=container.js.map \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.containerjs=e():t.containerjs=e()}(window,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=0)}([function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){return!e||"object"!==r(e)&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}function i(t){var e="function"==typeof Map?new Map:void 0;return(i=function(t){if(null===t||(n=t,-1===Function.toString.call(n).indexOf("[native code]")))return t;var n;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,r)}function r(){return c(t,arguments,a(this).constructor)}return r.prototype=Object.create(t.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),f(r,t)})(t)}function u(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(t){return!1}}function c(t,e,n){return(c=u()?Reflect.construct:function(t,e,n){var r=[null];r.push.apply(r,e);var o=new(Function.bind.apply(t,r));return n&&f(o,n.prototype),o}).apply(null,arguments)}function f(t,e){return(f=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function a(t){return(a=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}n.r(e);var s=function(t){function e(t){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),o(this,a(e).call(this,'Service or parameter "'.concat(t,'" not found.')))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&f(t,e)}(e,t),e}(i(Error));function l(t){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function p(t,e){return!e||"object"!==l(e)&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}function y(t){var e="function"==typeof Map?new Map:void 0;return(y=function(t){if(null===t||(n=t,-1===Function.toString.call(n).indexOf("[native code]")))return t;var n;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,r)}function r(){return h(t,arguments,v(this).constructor)}return r.prototype=Object.create(t.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),d(r,t)})(t)}function b(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(t){return!1}}function h(t,e,n){return(h=b()?Reflect.construct:function(t,e,n){var r=[null];r.push.apply(r,e);var o=new(Function.bind.apply(t,r));return n&&d(o,n.prototype),o}).apply(null,arguments)}function d(t,e){return(d=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function v(t){return(v=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var m=function(t){function e(t,n){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),p(this,v(e).call(this,"A ".concat(n,' has already been declared for the key "').concat(t,'".')))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&d(t,e)}(e,t),e}(y(Error));function g(t){return(g="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function w(t,e){return!e||"object"!==g(e)&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}function O(t){var e="function"==typeof Map?new Map:void 0;return(O=function(t){if(null===t||(n=t,-1===Function.toString.call(n).indexOf("[native code]")))return t;var n;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,r)}function r(){return j(t,arguments,P(this).constructor)}return r.prototype=Object.create(t.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),_(r,t)})(t)}function S(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(t){return!1}}function j(t,e,n){return(j=S()?Reflect.construct:function(t,e,n){var r=[null];r.push.apply(r,e);var o=new(Function.bind.apply(t,r));return n&&_(o,n.prototype),o}).apply(null,arguments)}function _(t,e){return(_=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function P(t){return(P=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var R=function(t){function e(t){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),w(this,P(e).call(this,'Unsupported definition type "'.concat(t,'".')))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&_(t,e)}(e,t),e}(O(Error));function x(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(t){return!1}}function E(t,e,n){return(E=x()?Reflect.construct:function(t,e,n){var r=[null];r.push.apply(r,e);var o=new(Function.bind.apply(t,r));return n&&k(o,n.prototype),o}).apply(null,arguments)}function k(t,e){return(k=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function T(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e2&&void 0!==arguments[2]?arguments[2]:[],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];return t.isConstructor(n)?this.registerService(e,n,r,o):this.registerParameter(e,n)}},{key:"registerService",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];this.ensureUniqueness(t),this.definitions.set(t,{type:"service",classname:e,name:t,dependencies:n,tags:"string"==typeof r?[r]:r})}},{key:"registerCallback",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];this.ensureUniqueness(t),this.definitions.set(t,{type:"callback",callback:e,name:t,dependencies:n,tags:"string"==typeof r?[r]:r})}},{key:"registerParameter",value:function(t,e){this.ensureUniqueness(t),this.definitions.set(t,{type:"parameter",value:e})}},{key:"ensureUniqueness",value:function(t){if(this.definitions.has(t))throw new m(t,this.definitions.get(t).type)}},{key:"get",value:function(t){var e=this.instances.get(t);if(void 0!==e)return e;if(this.definitions.has(t)){var n=this.definitions.get(t);switch(n.type){case"parameter":return n.value;case"callback":return this.resolveCallback(n);case"service":return this.resolveService(n);default:throw new R(n.type)}}throw new s(t)}},{key:"getTaggedServices",value:function(t){return Array.from(this.definitions.values()).filter((function(e){return"parameter"!==e.type&&e.tags.includes(t)})).map((function(t){return t.name}))}},{key:"resolveService",value:function(t){var e=t.dependencies.map(this.get),n=E(t.classname,T(e));return this.instances.set(t.name,n),n}},{key:"resolveCallback",value:function(t){var e=t.dependencies.map(this.get),n=t.callback.apply(t,T(e));return this.instances.set(t.name,n),n}}],[{key:"isConstructor",value:function(t){return"function"==typeof t}}]),t}();n.d(e,"Container",(function(){return M})),n.d(e,"ContainerNotFoundError",(function(){return s})),n.d(e,"ContainerDuplicateError",(function(){return m})),n.d(e,"ContainerUnsupportedDefinitionTypeError",(function(){return R}))}])})); \ No newline at end of file diff --git a/dist/container.js.map b/dist/container.js.map index 6898f5f..35801a9 100644 --- a/dist/container.js.map +++ b/dist/container.js.map @@ -1 +1 @@ -{"version":3,"sources":[],"names":[],"mappings":"","file":"container.js","sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack://container/webpack/universalModuleDefinition","webpack://container/webpack/bootstrap","webpack://container/./src/ContainerNotFoundError.js","webpack://container/./src/ContainerDuplicateError.js","webpack://container/./src/ContainerUnsupportedDefinitionTypeError.js","webpack://container/./src/Container.js","webpack://container/./src/index.js"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","ContainerNotFoundError","Error","super","ContainerDuplicateError","type","ContainerUnsupportedDefinitionTypeError","RegExp","this","instances","Map","definitions","registerDefinition","registerService","dependencies","tags","isConstructor","registerParameter","classname","ensureUniqueness","set","callback","has","instance","undefined","definition","resolveCallback","resolveService","tag","Array","from","values","filter","includes","map","service","Constructor","console","log"],"mappings":"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","file":"container.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"container\"] = factory();\n\telse\n\t\troot[\"container\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","/**\n * Not found service or parameter error for Container\n */\nexport default class ContainerNotFoundError extends Error {\n /**\n * @param {String} name Definition name\n */\n constructor(name) {\n super(`Service or parameter \"${name}\" not found.`);\n }\n}\n","/**\n * Service or parameter already declared for this key error for Container\n */\nexport default class ContainerDuplicateError extends Error {\n /**\n * @param {String} key Definition name\n * @param {String} type Existing definition type\n */\n constructor(key, type) {\n super(`A ${type} has already been declared for the key \"${key}\".`);\n }\n}\n","/**\n * Definition is not supported\n */\nexport default class ContainerUnsupportedDefinitionTypeError extends Error {\n /**\n * @param {String} type Definition type\n */\n constructor(type) {\n super(`Unsupported definition type \"${type}\".`);\n }\n}\n","import ContainerNotFoundError from './ContainerNotFoundError';\nimport ContainerDuplicateError from './ContainerDuplicateError';\nimport ContainerUnsupportedDefinitionTypeError from './ContainerUnsupportedDefinitionTypeError';\n\nconst functionTester = new RegExp('^function ');\n\n/**\n * Microscopic dependency injection container\n */\nclass Container {\n /**\n * Contructor\n */\n constructor() {\n this.instances = new Map();\n this.definitions = new Map();\n\n this.get = this.get.bind(this);\n this.registerDefinition = this.registerService.bind(this);\n }\n\n /**\n * Register parameter, callback or service\n *\n * @param {String} name\n * @param {Scalar|Function} value\n * @param {Array} dependencies\n * @param {String|String[]} tags\n */\n register(name, value, dependencies = [], tags = []) {\n if (Container.isConstructor(value)) {\n return this.registerService(name, value, dependencies, tags);\n }\n\n return this.registerParameter(name, value);\n }\n\n /**\n * Register service\n *\n * @param {String} name\n * @param {Function} classname\n * @param {Array} dependencies\n * @param {String|String[]} tags\n */\n registerService(name, classname, dependencies = [], tags = []) {\n this.ensureUniqueness(name);\n this.definitions.set(name, {\n type: 'service',\n classname,\n name,\n dependencies,\n tags: typeof tags === 'string' ? [tags] : tags\n });\n }\n\n /**\n * Register service callback\n *\n * @param {String} name\n * @param {Function} callback\n * @param {Array} dependencies\n * @param {String|String[]} tags\n */\n registerCallback(name, callback, dependencies = [], tags = []) {\n this.ensureUniqueness(name);\n this.definitions.set(name, {\n type: 'callback',\n callback,\n name,\n dependencies,\n tags: typeof tags === 'string' ? [tags] : tags\n });\n }\n\n /**\n * Register parameter\n *\n * @param {String} name\n * @param {mixed} value\n */\n registerParameter(name, value) {\n this.ensureUniqueness(name);\n this.definitions.set(name, { type: 'parameter', value });\n }\n\n /**\n * Ensure that the given key is not used by a definition or a parameter\n *\n * @param {String} name\n *\n * @throw {ContainerDuplicateError}\n */\n ensureUniqueness(name) {\n if (this.definitions.has(name)) {\n throw new ContainerDuplicateError(name, this.definitions.get(name).type);\n }\n }\n\n /**\n * Get parameter or service identified by its name\n *\n * @param {String} name\n *\n * @return {mixed}\n */\n get(name) {\n const instance = this.instances.get(name);\n\n if (instance !== undefined) {\n return instance;\n }\n\n if (this.definitions.has(name)) {\n const definition = this.definitions.get(name);\n\n switch (definition.type) {\n case 'parameter':\n return definition.value;\n\n case 'callback':\n return this.resolveCallback(definition);\n\n case 'service':\n return this.resolveService(definition);\n\n default:\n throw new ContainerUnsupportedDefinitionTypeError(definition.type)\n }\n }\n\n throw new ContainerNotFoundError(name);\n }\n\n /**\n * Get services for a given tag.\n *\n * @param {String} tag\n *\n * @return {Array}\n */\n getTaggedServices(tag) {\n return Array\n .from(this.definitions.values())\n .filter(definition => definition.type !== 'parameter' && definition.tags.includes(tag))\n .map(definition => definition.name)\n ;\n }\n\n /**\n * Resolve service definition\n *\n * @param {Object} definition\n *\n * @return {mixed}\n */\n resolveService(definition) {\n const dependencies = definition.dependencies.map(this.get);\n const Constructor = definition.classname;\n const service = new Constructor(...dependencies);\n\n this.instances.set(definition.name, service);\n\n return service;\n }\n\n /**\n * Resolve callback definition\n *\n * @param {Object} definition\n *\n * @return {mixed}\n */\n resolveCallback(definition) {\n const dependencies = definition.dependencies.map(this.get);\n const service = definition.callback(...dependencies);\n\n this.instances.set(definition.name, service);\n\n return service;\n }\n\n /**\n * Is the given function a class constructor?\n *\n * @param {Function} classname\n *\n * @return {Boolean}\n */\n static isConstructor(classname) {\n console.log(classname);\n return typeof classname === 'function';\n }\n}\n\nexport default Container;\n","import Container from './Container';\nimport ContainerNotFoundError from './ContainerNotFoundError';\nimport ContainerDuplicateError from './ContainerDuplicateError';\nimport ContainerUnsupportedDefinitionTypeError from './ContainerUnsupportedDefinitionTypeError';\n\nexport {\n Container,\n ContainerNotFoundError,\n ContainerDuplicateError,\n ContainerUnsupportedDefinitionTypeError,\n};\n"],"sourceRoot":""} \ No newline at end of file diff --git a/package.json b/package.json index 7d87b11..beaf870 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@elao/container.js", - "version": "2.0.1", + "version": "3.0.0", "description": "Microscopic dependency injection container", "keywords": [ "dependency", @@ -15,7 +15,7 @@ }, "author": "Thomas Jarrand (http://thomas.jarrand.fr)", "license": "MIT", - "copyright": "Copyright 2017 élao", + "copyright": "Copyright 2020 élao", "bugs": { "url": "https://github.com/Elao/container.js/issues" }, @@ -23,18 +23,26 @@ "scripts": { "test": "jest", "build": "NODE_ENV=production webpack", - "start": "NODE_ENV=dev webpack --watch" + "start": "NODE_ENV=development webpack --watch" }, "devDependencies": { - "babel": "^6.23.0", - "babel-core": "^6.25.0", - "babel-eslint": "^7.2.3", - "babel-jest": "^20.0.3", - "babel-loader": "^7.0.0", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-polyfill": "^6.23.0", - "babel-preset-es2015": "^6.24.1", - "jest": "^20.0.4", - "webpack": "^2.6.1" + "@babel/cli": "^7.8.4", + "@babel/core": "^7.8.4", + "@babel/preset-env": "^7.8.4", + "babel-jest": "^25.1.0", + "babel-loader": "^8.0.6", + "jest": "^25.1.0", + "webpack": "^4.41.6", + "webpack-cli": "^3.3.11" + }, + "dependencies": { + "@babel/polyfill": "^7.8.3" + }, + "babel": { + "presets": [ + [ + "@babel/preset-env" + ] + ] } } diff --git a/src/Container.js b/src/Container.js index 34d564c..9987dcf 100644 --- a/src/Container.js +++ b/src/Container.js @@ -1,24 +1,24 @@ import ContainerNotFoundError from './ContainerNotFoundError'; import ContainerDuplicateError from './ContainerDuplicateError'; +import ContainerUnsupportedDefinitionTypeError from './ContainerUnsupportedDefinitionTypeError'; /** * Microscopic dependency injection container */ -class Container { +export default class Container { /** * Contructor */ constructor() { - this.cache = new Map(); - this.services = new Map(); - this.parameters = new Map(); + this.instances = new Map(); + this.definitions = new Map(); - this.resolve = this.resolve.bind(this); this.get = this.get.bind(this); + this.registerDefinition = this.registerService.bind(this); } /** - * Register parameter or service + * Register parameter, callback or service * * @param {String} name * @param {Scalar|Function} value @@ -27,24 +27,48 @@ class Container { */ register(name, value, dependencies = [], tags = []) { if (Container.isConstructor(value)) { - this.registerDefinition(name, value, dependencies, tags); - } else { - this.registerParameter(name, value); + return this.registerService(name, value, dependencies, tags); } + + return this.registerParameter(name, value); } /** - * Register service definition + * Register service * * @param {String} name * @param {Function} classname * @param {Array} dependencies * @param {String|String[]} tags */ - registerDefinition(name, classname, dependencies = [], tags = []) { - tags = typeof (tags) === 'string' ? [tags] : tags; + registerService(name, classname, dependencies = [], tags = []) { this.ensureUniqueness(name); - this.services.set(name, { classname, name, dependencies, tags }); + this.definitions.set(name, { + type: 'service', + classname, + name, + dependencies, + tags: typeof tags === 'string' ? [tags] : tags + }); + } + + /** + * Register service callback + * + * @param {String} name + * @param {Function} callback + * @param {Array} dependencies + * @param {String|String[]} tags + */ + registerCallback(name, callback, dependencies = [], tags = []) { + this.ensureUniqueness(name); + this.definitions.set(name, { + type: 'callback', + callback, + name, + dependencies, + tags: typeof tags === 'string' ? [tags] : tags + }); } /** @@ -55,7 +79,7 @@ class Container { */ registerParameter(name, value) { this.ensureUniqueness(name); - this.parameters.set(name, value); + this.definitions.set(name, { type: 'parameter', value }); } /** @@ -66,12 +90,8 @@ class Container { * @throw {ContainerDuplicateError} */ ensureUniqueness(name) { - if (this.parameters.has(name)) { - throw new ContainerDuplicateError(name, 'parameter'); - } - - if (this.services.has(name)) { - throw new ContainerDuplicateError(name, 'service'); + if (this.definitions.has(name)) { + throw new ContainerDuplicateError(name, this.definitions.get(name).type); } } @@ -83,16 +103,28 @@ class Container { * @return {mixed} */ get(name) { - if (this.cache.has(name)) { - return this.cache.get(name); - } + const instance = this.instances.get(name); - if (this.services.has(name)) { - return this.resolve(this.services.get(name)); + if (instance !== undefined) { + return instance; } - if (this.parameters.has(name)) { - return this.parameters.get(name); + if (this.definitions.has(name)) { + const definition = this.definitions.get(name); + + switch (definition.type) { + case 'parameter': + return definition.value; + + case 'callback': + return this.resolveCallback(definition); + + case 'service': + return this.resolveService(definition); + + default: + throw new ContainerUnsupportedDefinitionTypeError(definition.type) + } } throw new ContainerNotFoundError(name); @@ -107,24 +139,41 @@ class Container { */ getTaggedServices(tag) { return Array - .from(this.services.values()) - .filter(definition => definition.tags.includes(tag)) + .from(this.definitions.values()) + .filter(definition => definition.type !== 'parameter' && definition.tags.includes(tag)) + .map(definition => definition.name) ; } /** - * Resolve definition + * Resolve service definition * * @param {Object} definition * * @return {mixed} */ - resolve(definition) { + resolveService(definition) { const dependencies = definition.dependencies.map(this.get); const Constructor = definition.classname; const service = new Constructor(...dependencies); - this.cache.set(definition.name, service); + this.instances.set(definition.name, service); + + return service; + } + + /** + * Resolve callback definition + * + * @param {Object} definition + * + * @return {mixed} + */ + resolveCallback(definition) { + const dependencies = definition.dependencies.map(this.get); + const service = definition.callback(...dependencies); + + this.instances.set(definition.name, service); return service; } @@ -137,8 +186,6 @@ class Container { * @return {Boolean} */ static isConstructor(classname) { - return typeof (classname) === 'function' && classname.name !== ''; + return typeof classname === 'function'; } } - -export default Container; diff --git a/src/ContainerDuplicateError.js b/src/ContainerDuplicateError.js index 74ea367..71441d9 100644 --- a/src/ContainerDuplicateError.js +++ b/src/ContainerDuplicateError.js @@ -1,15 +1,12 @@ /** * Service or parameter already declared for this key error for Container */ -class ContainerDuplicateError extends Error { +export default class ContainerDuplicateError extends Error { /** - * Constructor - * - * @param {String} name Service name + * @param {String} key Definition name + * @param {String} type Existing definition type */ constructor(key, type) { super(`A ${type} has already been declared for the key "${key}".`); } } - -export default ContainerDuplicateError; diff --git a/src/ContainerNotFoundError.js b/src/ContainerNotFoundError.js index 99a5a25..9f91e63 100644 --- a/src/ContainerNotFoundError.js +++ b/src/ContainerNotFoundError.js @@ -1,15 +1,11 @@ /** * Not found service or parameter error for Container */ -class ContainerNotFoundError extends Error { +export default class ContainerNotFoundError extends Error { /** - * Constructor - * - * @param {String} name Service name + * @param {String} name Definition name */ constructor(name) { super(`Service or parameter "${name}" not found.`); } } - -export default ContainerNotFoundError; diff --git a/src/ContainerUnsupportedDefinitionTypeError.js b/src/ContainerUnsupportedDefinitionTypeError.js new file mode 100644 index 0000000..138d432 --- /dev/null +++ b/src/ContainerUnsupportedDefinitionTypeError.js @@ -0,0 +1,11 @@ +/** + * Definition is not supported + */ +export default class ContainerUnsupportedDefinitionTypeError extends Error { + /** + * @param {String} type Definition type + */ + constructor(type) { + super(`Unsupported definition type "${type}".`); + } +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..60880de --- /dev/null +++ b/src/index.js @@ -0,0 +1,11 @@ +import ContainerNotFoundError from './ContainerNotFoundError'; +import ContainerDuplicateError from './ContainerDuplicateError'; +import ContainerUnsupportedDefinitionTypeError from './ContainerUnsupportedDefinitionTypeError'; +import Container from './Container'; + +export { + Container, + ContainerNotFoundError, + ContainerDuplicateError, + ContainerUnsupportedDefinitionTypeError, +}; diff --git a/test/Container.spec.js b/test/Container.spec.js index 42ad9f2..05365f8 100644 --- a/test/Container.spec.js +++ b/test/Container.spec.js @@ -1,13 +1,40 @@ -import Container from '../src/Container'; +const { Container } = require('../dist/container.js'); describe('Container', () => { const container = new Container(); - class MyClass { } - class BarClass { } - container.registerDefinition('my:class', MyClass, [], ['foo_tag']); - container.registerDefinition('bar', BarClass, [], ['foo_tag', 'bar_tag']); + const instances = { + MyClass: 0, + BarClass: 0, + myCallback: 0, + }; + const myCallback = function foo(bar) { instances.myCallback++; return { bar }; }; + class MyClass { constructor() { instances.MyClass++; } } + class BarClass { constructor() { instances.BarClass++; } } + + container.registerParameter('my:parameter', 'foo'); + container.registerService('my:class', MyClass, [], ['foo_tag']); + container.registerService('bar', BarClass, ['my:class'], ['foo_tag', 'bar_tag']); + container.registerCallback('my:callback', myCallback, ['my:parameter']); describe('Container.get()', () => { + test('Class should only be instancieted once', () => { + expect(instances.MyClass).toEqual(0); + container.get('my:class'); + container.get('my:class') + expect(instances.MyClass).toEqual(1); + }); + + test('Callbacks should only be called once', () => { + expect(instances.myCallback).toEqual(0); + container.get('my:callback'); + container.get('my:callback') + expect(instances.myCallback).toEqual(1); + }); + + test('should return the expected parameter when asked for', () => { + expect(container.get('my:parameter')).toEqual('foo'); + }); + test('should return the expected service when asked for', () => { expect(container.get('my:class')).toBeInstanceOf(MyClass); }); @@ -21,17 +48,22 @@ describe('Container', () => { describe('Container.getTaggedServices()', () => { test('should return all services with a given tag', () => { - expect(container.getTaggedServices('foo_tag')).toEqual([ - { classname: MyClass, dependencies: [], name: 'my:class', tags: ['foo_tag'] }, - { classname: BarClass, dependencies: [], name: 'bar', tags: ['foo_tag', 'bar_tag'] } - ]); - expect(container.getTaggedServices('bar_tag')).toEqual([ - { classname: BarClass, dependencies: [], name: 'bar', tags: ['foo_tag', 'bar_tag'] } - ]); + expect(container.getTaggedServices('foo_tag')).toEqual(['my:class', 'bar']); + expect(container.getTaggedServices('bar_tag')).toEqual(['bar']); }); test('should return an empty array on undefined tag', () => { expect(container.getTaggedServices('foobar')).toEqual([]); }); }); + + describe('Container.isConstructor()', () => { + test('Should return true for a constructor', () => { + class Foo { constructor(a) {} } + expect(Container.isConstructor(Foo)).toEqual(true); + }); + test('Should return false for a string', () => { + expect(Container.isConstructor('foo')).toEqual(false); + }); + }); }); diff --git a/webpack.config.js b/webpack.config.js index 942226c..3c068d3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,32 +2,41 @@ const webpack = require('webpack'); const meta = require('./package.json'); module.exports = { - entry: './src/Container.js', - module: { - loaders: [ - { - test: /\.js$/, - exclude: /node_modules/, - loader: 'babel-loader' - }, - ] - }, + mode: process.env.NODE_ENV, + devtool: process.env.NODE_ENV === 'production' ? undefined : 'source-map', + entry: './src/index.js', output: { path: __dirname + '/dist', filename: 'container.js', - library: 'Container', + library: 'containerjs', libraryTarget: 'umd', }, devServer: { contentBase: './dist', }, - devtool: 'source-map', + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env'], + } + }, + ] + }, plugins: [ - new webpack.optimize.UglifyJsPlugin(), + //new webpack.optimize.UglifyJsPlugin(), new webpack.BannerPlugin([ `${meta.name} - ${meta.version}`, `${meta.homepage}`, `${meta.copyright}`, ].join('\n')) - ] + ], + resolve: { + alias: { + 'container': `${__dirname}/`, + } + } };