knifecycle
Advanced tools
Comparing version 10.0.3 to 11.0.0-beta.0
@@ -0,1 +1,5 @@ | ||
# [11.0.0-beta.0](https://github.com/nfroidure/knifecycle/compare/v10.0.3...v11.0.0-beta.0) (2020-11-18) | ||
## [10.0.3](https://github.com/nfroidure/knifecycle/compare/v10.0.2...v10.0.3) (2020-10-18) | ||
@@ -2,0 +6,0 @@ |
@@ -12,2 +12,8 @@ "use strict"; | ||
}); | ||
Object.defineProperty(exports, "SPECIAL_PROPS_PREFIX", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.SPECIAL_PROPS_PREFIX; | ||
} | ||
}); | ||
Object.defineProperty(exports, "DECLARATION_SEPARATOR", { | ||
@@ -25,2 +31,38 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "ALLOWED_INITIALIZER_TYPES", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.ALLOWED_INITIALIZER_TYPES; | ||
} | ||
}); | ||
Object.defineProperty(exports, "ALLOWED_SPECIAL_PROPS", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.ALLOWED_SPECIAL_PROPS; | ||
} | ||
}); | ||
Object.defineProperty(exports, "parseInjections", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.parseInjections; | ||
} | ||
}); | ||
Object.defineProperty(exports, "readFunctionName", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.readFunctionName; | ||
} | ||
}); | ||
Object.defineProperty(exports, "reuseSpecialProps", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.reuseSpecialProps; | ||
} | ||
}); | ||
Object.defineProperty(exports, "parseName", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.parseName; | ||
} | ||
}); | ||
Object.defineProperty(exports, "name", { | ||
@@ -74,8 +116,2 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "options", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.options; | ||
} | ||
}); | ||
Object.defineProperty(exports, "extra", { | ||
@@ -87,8 +123,2 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "reuseSpecialProps", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.reuseSpecialProps; | ||
} | ||
}); | ||
Object.defineProperty(exports, "initializer", { | ||
@@ -130,2 +160,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "wrapInitializer", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.wrapInitializer; | ||
} | ||
}); | ||
Object.defineProperty(exports, "handler", { | ||
@@ -143,8 +179,2 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "wrapInitializer", { | ||
enumerable: true, | ||
get: function () { | ||
return _util.wrapInitializer; | ||
} | ||
}); | ||
Object.defineProperty(exports, "parseDependencyDeclaration", { | ||
@@ -172,3 +202,4 @@ enumerable: true, | ||
/* eslint max-len: ["warn", { "ignoreComments": true }] */ | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
const debug = (0, _debug.default)('knifecycle'); | ||
@@ -195,3 +226,2 @@ const DISPOSE = '$dispose'; | ||
const E_UNDEFINED_CONSTANT_INITIALIZER = 'E_UNDEFINED_CONSTANT_INITIALIZER'; | ||
const E_NON_SINGLETON_CONSTANT_INITIALIZER = 'E_NON_SINGLETON_CONSTANT_INITIALIZER'; | ||
const E_BAD_VALUED_NON_CONSTANT_INITIALIZER = 'E_BAD_VALUED_NON_CONSTANT_INITIALIZER'; | ||
@@ -239,2 +269,18 @@ /* Architecture Note #1: Knifecycle | ||
constructor() { | ||
_defineProperty(this, "_silosCounter", void 0); | ||
_defineProperty(this, "_silosContexts", void 0); | ||
_defineProperty(this, "_initializers", void 0); | ||
_defineProperty(this, "_initializerResolvers", void 0); | ||
_defineProperty(this, "_singletonsServicesHandles", void 0); | ||
_defineProperty(this, "_singletonsServicesDescriptors", void 0); | ||
_defineProperty(this, "_singletonsServicesShutdownsPromises", void 0); | ||
_defineProperty(this, "shutdownPromise", void 0); | ||
this._silosCounter = 0; | ||
@@ -248,12 +294,10 @@ this._silosContexts = new Set(); | ||
this.register((0, _util.constant)(INSTANCE, this)); | ||
this.register((0, _util.initializer)({ | ||
const initInjectorProvider = (0, _util.initializer)({ | ||
name: INJECTOR, | ||
type: 'provider', | ||
inject: [SILO_CONTEXT], | ||
options: { | ||
// Despite its global definition, the injector | ||
// depends on the silo context and then needs | ||
// to be instanciated once per silo. | ||
singleton: false | ||
} | ||
// Despite its global definition, the injector | ||
// depends on the silo context and then needs | ||
// to be instanciated once per silo. | ||
singleton: false | ||
}, async ({ | ||
@@ -263,5 +307,7 @@ $siloContext | ||
service: async (dependenciesDeclarations) => _buildFinalHash(await this._initializeDependencies($siloContext, $siloContext.name, dependenciesDeclarations, { | ||
injectorContext: true | ||
injectorContext: true, | ||
autoloading: false | ||
}), dependenciesDeclarations) | ||
}))); | ||
})); | ||
this.register(initInjectorProvider); | ||
} | ||
@@ -304,3 +350,3 @@ /* Architecture Note #1.3: Registering initializers | ||
if (typeof initializer !== 'function') { | ||
if (typeof initializer !== 'function' && typeof initializer !== 'object') { | ||
throw new _yerror.default(E_BAD_INITIALIZER, initializer); | ||
@@ -310,3 +356,3 @@ } | ||
initializer[_util.SPECIAL_PROPS.INJECT] = initializer[_util.SPECIAL_PROPS.INJECT] || []; | ||
initializer[_util.SPECIAL_PROPS.OPTIONS] = initializer[_util.SPECIAL_PROPS.OPTIONS] || {}; | ||
initializer[_util.SPECIAL_PROPS.SINGLETON] = initializer[_util.SPECIAL_PROPS.SINGLETON] || false; | ||
initializer[_util.SPECIAL_PROPS.TYPE] = initializer[_util.SPECIAL_PROPS.TYPE] || _util.ALLOWED_INITIALIZER_TYPES[0]; | ||
@@ -318,4 +364,4 @@ | ||
if (initializer[_util.SPECIAL_PROPS.NAME] === AUTOLOAD && !initializer[_util.SPECIAL_PROPS.OPTIONS].singleton) { | ||
throw new _yerror.default(E_BAD_AUTOLOADER, initializer[_util.SPECIAL_PROPS.OPTIONS]); | ||
if (initializer[_util.SPECIAL_PROPS.NAME] === AUTOLOAD && !initializer[_util.SPECIAL_PROPS.SINGLETON]) { | ||
throw new _yerror.default(E_BAD_AUTOLOADER, initializer[_util.SPECIAL_PROPS.SINGLETON]); | ||
} | ||
@@ -325,12 +371,17 @@ | ||
throw new _yerror.default(E_BAD_INITIALIZER_TYPE, initializer[_util.SPECIAL_PROPS.NAME], initializer[_util.SPECIAL_PROPS.TYPE], _util.ALLOWED_INITIALIZER_TYPES); | ||
} | ||
} // Temporary cast constants into providers | ||
// Best would be to threat each differently | ||
// at dependencies initialization level to boost performances | ||
if (initializer[_util.SPECIAL_PROPS.TYPE] === 'constant') { | ||
if ('undefined' === typeof initializer[_util.SPECIAL_PROPS.VALUE]) { | ||
const value = initializer[_util.SPECIAL_PROPS.VALUE]; | ||
if ('undefined' === typeof value) { | ||
throw new _yerror.default(E_UNDEFINED_CONSTANT_INITIALIZER, initializer[_util.SPECIAL_PROPS.NAME]); | ||
} | ||
if (!initializer[_util.SPECIAL_PROPS.OPTIONS].singleton) { | ||
throw new _yerror.default(E_NON_SINGLETON_CONSTANT_INITIALIZER, initializer[_util.SPECIAL_PROPS.NAME]); | ||
} | ||
initializer = (0, _util.provider)(async () => ({ | ||
service: value | ||
}), initializer[_util.SPECIAL_PROPS.NAME], [], true); | ||
} else if ('undefined' !== typeof initializer[_util.SPECIAL_PROPS.VALUE]) { | ||
@@ -380,8 +431,6 @@ throw new _yerror.default(E_BAD_VALUED_NON_CONSTANT_INITIALIZER, initializer[_util.SPECIAL_PROPS.NAME]); | ||
preloaded: true, | ||
promise: Promise.resolve({ | ||
// We do not directly use initializer[SPECIAL_PROPS.VALUE] here | ||
// since it looks like there is a bug with Babel build that | ||
// change functions to empty litteral objects | ||
service: initializer() | ||
}) | ||
// We do not directly use initializer[SPECIAL_PROPS.VALUE] here | ||
// since it looks like there is a bug with Babel build that | ||
// change functions to empty litteral objects | ||
promise: initializer() | ||
}); | ||
@@ -410,3 +459,3 @@ } | ||
if (rootServiceName === childServiceName) { | ||
throw new _yerror.default(...[E_CIRCULAR_DEPENDENCY, rootServiceName].concat(declarationsStacks).concat(childDependencyDeclaration)); | ||
throw new _yerror.default(E_CIRCULAR_DEPENDENCY, ...[rootServiceName].concat(declarationsStacks).concat(childDependencyDeclaration)); | ||
} | ||
@@ -453,3 +502,7 @@ | ||
classes = {} | ||
} = {}) { | ||
} = { | ||
shapes: [], | ||
styles: [], | ||
classes: {} | ||
}) { | ||
const servicesProviders = this._initializers; | ||
@@ -532,3 +585,3 @@ const links = Array.from(servicesProviders.keys()).filter(provider => !provider.startsWith('$')).reduce((links, serviceName) => { | ||
siloContext.servicesDescriptors.set(FATAL_ERROR, { | ||
siloContext.servicesDescriptors.set(FATAL_ERROR, Promise.resolve({ | ||
service: { | ||
@@ -542,9 +595,9 @@ promise: new Promise((resolve, reject) => { | ||
} | ||
}); // Make the siloContext available for internal injections | ||
})); // Make the siloContext available for internal injections | ||
siloContext.servicesDescriptors.set(SILO_CONTEXT, { | ||
siloContext.servicesDescriptors.set(SILO_CONTEXT, Promise.resolve({ | ||
service: siloContext | ||
}); // Create a provider for the shutdown special dependency | ||
})); // Create a provider for the shutdown special dependency | ||
siloContext.servicesDescriptors.set(DISPOSE, { | ||
siloContext.servicesDescriptors.set(DISPOSE, Promise.resolve({ | ||
service: async () => { | ||
@@ -605,3 +658,3 @@ siloContext.shutdownPromise = siloContext.shutdownPromise || _shutdownNextServices(siloContext.servicesSequence); | ||
dispose: Promise.resolve.bind(Promise) | ||
}); | ||
})); | ||
@@ -640,6 +693,6 @@ this._silosContexts.add(siloContext); | ||
async destroy() { | ||
this.shutdownPromise = this.shutdownPromise || Promise.all([...this._silosContexts].map(siloContext => { | ||
const $dispose = siloContext.servicesDescriptors.get(DISPOSE).service; | ||
this.shutdownPromise = this.shutdownPromise || Promise.all([...this._silosContexts].map(async siloContext => { | ||
const $dispose = (await siloContext.servicesDescriptors.get(DISPOSE)).service; | ||
return $dispose(); | ||
})); | ||
})).then(() => undefined); | ||
debug('Shutting down Knifecycle instance.'); | ||
@@ -663,3 +716,3 @@ return this.shutdownPromise; | ||
* @return {Promise} | ||
* Service dependencies hash promise. | ||
* Service descriptor promise. | ||
*/ | ||
@@ -683,3 +736,3 @@ | ||
let initializer = await this._findInitializer(siloContext, serviceName, { | ||
const initializer = await this._findInitializer(siloContext, serviceName, { | ||
injectorContext, | ||
@@ -717,3 +770,3 @@ autoloading | ||
if (initializer[_util.SPECIAL_PROPS.OPTIONS].singleton) { | ||
if (initializer[_util.SPECIAL_PROPS.SINGLETON]) { | ||
const handlesSet = new Set(); | ||
@@ -733,3 +786,3 @@ handlesSet.add(siloContext.name); | ||
if (AUTOLOAD === this.serviceName) { | ||
if (AUTOLOAD === serviceName) { | ||
siloContext.servicesSequence.unshift([AUTOLOAD]); | ||
@@ -745,3 +798,3 @@ } | ||
}) { | ||
let initializer = this._initializers.get(serviceName); | ||
const initializer = this._initializers.get(serviceName); | ||
@@ -883,3 +936,3 @@ if (initializer) { | ||
siloContext.servicesDescriptors.set(serviceName, serviceDescriptor); | ||
siloContext.servicesDescriptors.set(serviceName, Promise.resolve(serviceDescriptor)); | ||
} catch (err) { | ||
@@ -889,3 +942,3 @@ debug('Error initializing a service descriptor:', serviceName, err.stack); | ||
if (E_UNMATCHED_DEPENDENCY === err.code) { | ||
throw _yerror.default.wrap(...[err, E_UNMATCHED_DEPENDENCY, serviceName].concat(err.params)); | ||
throw _yerror.default.wrap(err, E_UNMATCHED_DEPENDENCY, ...[serviceName].concat(err.params)); | ||
} | ||
@@ -947,3 +1000,3 @@ | ||
if (!serviceDescriptor) { | ||
return {}.undef; | ||
return undefined; | ||
} | ||
@@ -983,4 +1036,2 @@ | ||
return shapes.reduce((shapedService, shape) => { | ||
let matches; | ||
if (shapedService) { | ||
@@ -990,3 +1041,3 @@ return shapedService; | ||
matches = shape.pattern.exec(serviceName); | ||
const matches = shape.pattern.exec(serviceName); | ||
@@ -1037,3 +1088,3 @@ if (!matches) { | ||
return servicePromise.then(_service_ => Promise.resolve({ | ||
return servicePromise.then(_service_ => ({ | ||
service: _service_ | ||
@@ -1040,0 +1091,0 @@ })); |
@@ -43,3 +43,3 @@ "use strict"; | ||
if (nodeIsALeaf || node.__childNodes.every(nodeIsInBatches.bind(null, batches))) { | ||
if (nodeIsALeaf || node.__childNodes.every(childNode => nodeIsInBatches(batches, childNode))) { | ||
return batch.concat(node.__name); | ||
@@ -46,0 +46,0 @@ } |
550
dist/util.js
@@ -6,3 +6,11 @@ "use strict"; | ||
}); | ||
exports.parseInjections = parseInjections; | ||
exports.readFunctionName = readFunctionName; | ||
exports.parseName = parseName; | ||
exports.reuseSpecialProps = reuseSpecialProps; | ||
exports.constant = constant; | ||
exports.service = service; | ||
exports.autoService = autoService; | ||
exports.provider = provider; | ||
exports.autoProvider = autoProvider; | ||
exports.wrapInitializer = wrapInitializer; | ||
@@ -13,16 +21,9 @@ exports.inject = inject; | ||
exports.autoInject = autoInject; | ||
exports.parseInjections = parseInjections; | ||
exports.alsoInject = alsoInject; | ||
exports.extra = extra; | ||
exports.options = options; | ||
exports.singleton = singleton; | ||
exports.name = name; | ||
exports.autoName = autoName; | ||
exports.parseName = parseName; | ||
exports.type = type; | ||
exports.initializer = initializer; | ||
exports.constant = constant; | ||
exports.service = service; | ||
exports.autoService = autoService; | ||
exports.provider = provider; | ||
exports.autoProvider = autoProvider; | ||
exports.handler = handler; | ||
@@ -32,3 +33,3 @@ exports.autoHandler = autoHandler; | ||
exports.stringifyDependencyDeclaration = stringifyDependencyDeclaration; | ||
exports.ALLOWED_INITIALIZER_TYPES = exports.OPTIONAL_FLAG = exports.DECLARATION_SEPARATOR = exports.ALLOWED_SPECIAL_PROPS = exports.SPECIAL_PROPS = exports.SPECIAL_PROPS_PREFIX = void 0; | ||
exports.ALLOWED_SPECIAL_PROPS = exports.SPECIAL_PROPS = exports.SPECIAL_PROPS_PREFIX = exports.ALLOWED_INITIALIZER_TYPES = exports.OPTIONAL_FLAG = exports.DECLARATION_SEPARATOR = void 0; | ||
@@ -41,2 +42,3 @@ var _yerror = _interopRequireDefault(require("yerror")); | ||
/* eslint @typescript-eslint/ban-types:0 */ | ||
const debug = (0, _debug.default)('knifecycle'); | ||
@@ -66,9 +68,15 @@ /* Architecture Note #1.2: Creating initializers | ||
const DECLARATION_SEPARATOR = '>'; | ||
exports.DECLARATION_SEPARATOR = DECLARATION_SEPARATOR; | ||
const OPTIONAL_FLAG = '?'; | ||
exports.OPTIONAL_FLAG = OPTIONAL_FLAG; | ||
const ALLOWED_INITIALIZER_TYPES = ['provider', 'service', 'constant']; | ||
exports.ALLOWED_INITIALIZER_TYPES = ALLOWED_INITIALIZER_TYPES; | ||
const SPECIAL_PROPS_PREFIX = '$'; | ||
exports.SPECIAL_PROPS_PREFIX = SPECIAL_PROPS_PREFIX; | ||
const SPECIAL_PROPS = { | ||
TYPE: `${SPECIAL_PROPS_PREFIX}type`, | ||
NAME: `${SPECIAL_PROPS_PREFIX}name`, | ||
INJECT: `${SPECIAL_PROPS_PREFIX}inject`, | ||
OPTIONS: `${SPECIAL_PROPS_PREFIX}options`, | ||
NAME: `${SPECIAL_PROPS_PREFIX}name`, | ||
TYPE: `${SPECIAL_PROPS_PREFIX}type`, | ||
SINGLETON: `${SPECIAL_PROPS_PREFIX}singleton`, | ||
EXTRA: `${SPECIAL_PROPS_PREFIX}extra`, | ||
@@ -80,19 +88,52 @@ VALUE: `${SPECIAL_PROPS_PREFIX}value` | ||
exports.ALLOWED_SPECIAL_PROPS = ALLOWED_SPECIAL_PROPS; | ||
const DECLARATION_SEPARATOR = '>'; | ||
exports.DECLARATION_SEPARATOR = DECLARATION_SEPARATOR; | ||
const OPTIONAL_FLAG = '?'; | ||
exports.OPTIONAL_FLAG = OPTIONAL_FLAG; | ||
const ALLOWED_INITIALIZER_TYPES = ['provider', 'service', 'constant']; | ||
exports.ALLOWED_INITIALIZER_TYPES = ALLOWED_INITIALIZER_TYPES; | ||
const E_BAD_INJECT_IN_CONSTANT = 'E_BAD_INJECT_IN_CONSTANT'; | ||
const E_CONSTANT_INJECTION = 'E_CONSTANT_INJECTION'; | ||
function parseInjections(source, options) { | ||
const matches = source.match(/^\s*(?:async\s+function(?:\s+\w+)?|async)\s*\(\s*\{\s*([^{}]+)(\s*\.\.\.[^{}]+|)\s*\}/); | ||
if (!matches) { | ||
if (!source.match(/^\s*async/)) { | ||
throw new _yerror.default('E_NON_ASYNC_INITIALIZER', source); | ||
} | ||
if (options && options.allowEmpty && source.match(/^\s*(?:async\s+function(?:\s+\w+)?|async)\s*\(\s*\)/)) { | ||
return []; | ||
} | ||
throw new _yerror.default('E_AUTO_INJECTION_FAILURE', source); | ||
} | ||
return matches[1].trim().replace(/,$/, '').split(/\s*,\s*/).map(s => s.trim()).filter(s => !s.startsWith('...')).map(injection => (injection.includes('=') ? '?' : '') + injection.split(/\s*=\s*/).shift().split(/\s*:\s*/).shift()).filter(injection => !/[)(\][]/.test(injection)); | ||
} | ||
function readFunctionName(aFunction) { | ||
if (typeof aFunction !== 'function') { | ||
throw new _yerror.default('E_AUTO_NAMING_FAILURE', typeof aFunction); | ||
} | ||
const functionName = parseName(aFunction.name || ''); | ||
if (!functionName) { | ||
throw new _yerror.default('E_AUTO_NAMING_FAILURE', aFunction.name); | ||
} | ||
return functionName; | ||
} | ||
function parseName(functionName) { | ||
return functionName.split(' ').pop().replace(/^init(?:ialize)?([A-Z])/, (_, $1) => $1.toLowerCase()); | ||
} | ||
/** | ||
* Apply special props to the given function from another one | ||
* @param {Function} from The initialization function in which to pick the props | ||
* @param {Function} to The initialization function from which to build the new one | ||
* Apply special props to the given initializer from another one | ||
* and optionally amend with new special props | ||
* @param {Function} from The initializer in which to pick the props | ||
* @param {Function} to The initializer from which to build the new one | ||
* @param {Object} [amend={}] Some properties to override | ||
* @return {Function} The newly built function | ||
* @return {Function} The newly built initializer | ||
*/ | ||
function reuseSpecialProps(from, to, amend = {}) { | ||
const uniqueInitializer = to.bind(null); | ||
return [...new Set(Object.keys(from).concat(Object.keys(amend)))].filter(prop => prop.startsWith(SPECIAL_PROPS_PREFIX)).reduce((fn, prop) => { | ||
@@ -110,5 +151,202 @@ const value = 'undefined' !== typeof amend[prop] ? amend[prop] : from[prop]; | ||
return fn; | ||
}, to.bind()); | ||
}, uniqueInitializer); | ||
} | ||
/** | ||
* Decorator that creates an initializer for a constant value | ||
* @param {String} name | ||
* The constant's name. | ||
* @param {any} value | ||
* The constant's value | ||
* @return {Function} | ||
* Returns a new constant initializer | ||
* @example | ||
* import Knifecycle, { constant, service } from 'knifecycle'; | ||
* | ||
* const { printAnswer } = new Knifecycle() | ||
* .register(constant('THE_NUMBER', value)) | ||
* .register(constant('log', console.log.bind(console))) | ||
* .register(service( | ||
* async ({ THE_NUMBER, log }) => () => log(THE_NUMBER), | ||
* 'printAnswer', | ||
* ['THE_NUMBER', 'log'], | ||
* )) | ||
* .run(['printAnswer']); | ||
* | ||
* printAnswer(); // 42 | ||
*/ | ||
function constant(name, value) { | ||
const contantLooksLikeAnInitializer = value instanceof Function && value[SPECIAL_PROPS.INJECT]; | ||
if (contantLooksLikeAnInitializer) { | ||
throw new _yerror.default(E_CONSTANT_INJECTION, value[SPECIAL_PROPS.INJECT]); | ||
} | ||
debug(`Created an initializer from a constant: ${name}.`); | ||
return { | ||
$type: 'constant', | ||
$name: name, | ||
$value: value | ||
}; | ||
} | ||
/** | ||
* Decorator that creates an initializer from a service builder | ||
* @param {Function} serviceBuilder | ||
* An async function to build the service | ||
* @param {String} [name] | ||
* The service's name | ||
* @param {Array<String>} [dependencies] | ||
* The service's injected dependencies | ||
* @param {Boolean} [singleton] | ||
* Whether the service is a singleton or not | ||
* @param {any} [extra] | ||
* Eventual extra informations | ||
* @return {Function} | ||
* Returns a new initializer | ||
* @example | ||
* import Knifecycle, { constant, service } from 'knifecycle'; | ||
* | ||
* const { printAnswer } = new Knifecycle() | ||
* .register(constant('THE_NUMBER', value)) | ||
* .register(constant('log', console.log.bind(console))) | ||
* .register(service( | ||
* async ({ THE_NUMBER, log }) => () => log(THE_NUMBER), | ||
* 'printAnswer', | ||
* ['THE_NUMBER', 'log'], | ||
* true | ||
* )) | ||
* .run(['printAnswer']); | ||
* | ||
* printAnswer(); // 42 | ||
*/ | ||
function service(serviceBuilder, name, dependencies, singleton, extra) { | ||
if (!serviceBuilder) { | ||
throw new _yerror.default('E_NO_SERVICE_BUILDER'); | ||
} | ||
name = name || serviceBuilder[SPECIAL_PROPS.NAME] || 'anonymous'; | ||
dependencies = dependencies || serviceBuilder[SPECIAL_PROPS.INJECT] || []; | ||
singleton = typeof singleton === 'undefined' ? serviceBuilder[SPECIAL_PROPS.SINGLETON] || false : singleton; | ||
extra = extra || serviceBuilder[SPECIAL_PROPS.EXTRA] || []; | ||
debug(`Created an initializer from a service builder: ${name}.`); | ||
const uniqueInitializer = reuseSpecialProps(serviceBuilder, serviceBuilder, { | ||
[SPECIAL_PROPS.TYPE]: 'service', | ||
[SPECIAL_PROPS.NAME]: name, | ||
[SPECIAL_PROPS.INJECT]: dependencies, | ||
[SPECIAL_PROPS.SINGLETON]: singleton, | ||
[SPECIAL_PROPS.EXTRA]: extra | ||
}); | ||
return uniqueInitializer; | ||
} | ||
/** | ||
* Decorator that creates an initializer from a service | ||
* builder by automatically detecting its name | ||
* and dependencies | ||
* @param {Function} serviceBuilder | ||
* An async function to build the service | ||
* @return {Function} | ||
* Returns a new initializer | ||
*/ | ||
function autoService(serviceBuilder) { | ||
const name = readFunctionName(serviceBuilder); | ||
const source = serviceBuilder.toString(); | ||
const dependencies = parseInjections(source, { | ||
allowEmpty: true | ||
}); | ||
return service(serviceBuilder, name, dependencies); | ||
} | ||
/** | ||
* Decorator that creates an initializer for a provider | ||
* builder | ||
* @param {Function} providerBuilder | ||
* An async function to build the service provider | ||
* @param {String} [name] | ||
* The service's name | ||
* @param {Array<String>} [dependencies] | ||
* The service's dependencies | ||
* @param {Boolean} [singleton] | ||
* Whether the service is a singleton or not | ||
* @param {any} [extra] | ||
* Eventual extra informations | ||
* @return {Function} | ||
* Returns a new provider initializer | ||
* @example | ||
* | ||
* import Knifecycle, { provider } from 'knifecycle' | ||
* import fs from 'fs'; | ||
* | ||
* const $ = new Knifecycle(); | ||
* | ||
* $.register(provider(configProvider, 'config')); | ||
* | ||
* async function configProvider() { | ||
* return new Promise((resolve, reject) { | ||
* fs.readFile('config.js', function(err, data) { | ||
* let config; | ||
* | ||
* if(err) { | ||
* reject(err); | ||
* return; | ||
* } | ||
* | ||
* try { | ||
* config = JSON.parse(data.toString); | ||
* } catch (err) { | ||
* reject(err); | ||
* return; | ||
* } | ||
* | ||
* resolve({ | ||
* service: config, | ||
* }); | ||
* }); | ||
* }); | ||
* } | ||
*/ | ||
function provider(providerBuilder, name, dependencies, singleton, extra) { | ||
if (!providerBuilder) { | ||
throw new _yerror.default('E_NO_PROVIDER_BUILDER'); | ||
} | ||
name = name || providerBuilder[SPECIAL_PROPS.NAME] || 'anonymous'; | ||
dependencies = dependencies || providerBuilder[SPECIAL_PROPS.INJECT] || []; | ||
singleton = typeof singleton === 'undefined' ? providerBuilder[SPECIAL_PROPS.SINGLETON] || false : singleton; | ||
extra = extra || providerBuilder[SPECIAL_PROPS.EXTRA] || []; | ||
debug(`Created an initializer from a provider builder: ${name || 'anonymous'}.`); | ||
const uniqueInitializer = reuseSpecialProps(providerBuilder, providerBuilder, { | ||
[SPECIAL_PROPS.TYPE]: 'provider', | ||
[SPECIAL_PROPS.NAME]: name, | ||
[SPECIAL_PROPS.INJECT]: dependencies, | ||
[SPECIAL_PROPS.SINGLETON]: singleton, | ||
[SPECIAL_PROPS.EXTRA]: extra | ||
}); | ||
return uniqueInitializer; | ||
} | ||
/** | ||
* Decorator that creates an initializer from a provider | ||
* builder by automatically detecting its name | ||
* and dependencies | ||
* @param {Function} providerBuilder | ||
* An async function to build the service provider | ||
* @return {Function} | ||
* Returns a new provider initializer | ||
*/ | ||
function autoProvider(providerBuilder) { | ||
const name = readFunctionName(providerBuilder); | ||
const source = providerBuilder.toString(); | ||
const dependencies = parseInjections(source, { | ||
allowEmpty: true | ||
}); | ||
return provider(providerBuilder, name, dependencies); | ||
} | ||
/** | ||
* Allows to wrap an initializer to add extra initialization steps | ||
@@ -136,3 +374,3 @@ * @param {Function} wrapper | ||
* List of dependencies declarations to declare which | ||
* services the initializer needs to resolve its | ||
* services the initializer needs to provide its | ||
* own service | ||
@@ -224,24 +462,2 @@ * @param {Function} initializer | ||
} | ||
function parseInjections(source, { | ||
allowEmpty = false | ||
} = { | ||
allowEmpty: false | ||
}) { | ||
const matches = source.match(/^\s*(?:async\s+function(?:\s+\w+)?|async)\s*\(\s*\{\s*([^{}]+)(\s*\.\.\.[^{}]+|)\s*\}/); | ||
if (!matches) { | ||
if (!source.match(/^\s*async/)) { | ||
throw new _yerror.default('E_NON_ASYNC_INITIALIZER', source); | ||
} | ||
if (allowEmpty && source.match(/^\s*(?:async\s+function(?:\s+\w+)?|async)\s*\(\s*\)/)) { | ||
return []; | ||
} | ||
throw new _yerror.default('E_AUTO_INJECTION_FAILURE', source); | ||
} | ||
return matches[1].trim().replace(/,$/, '').split(/\s*,\s*/).map(s => s.trim()).filter(s => !s.startsWith('...')).map(injection => (injection.includes('=') ? '?' : '') + injection.split(/\s*=\s*/).shift().split(/\s*:\s*/).shift()).filter(injection => !/[)(\][]/.test(injection)); | ||
} | ||
/** | ||
@@ -336,12 +552,8 @@ * Decorator creating a new initializer with some | ||
/** | ||
* Decorator to amend an initializer options. | ||
* @param {Object} options | ||
* Options to set to the initializer | ||
* @param {Object} options.singleton | ||
* Define the initializer service as a singleton | ||
* (one instance for several runs) | ||
* Decorator to set an initializer singleton option. | ||
* @param {Function} initializer | ||
* The initializer to tweak | ||
* @param {Function} [merge=true] | ||
* Whether options should be merged or not | ||
* @param {boolean} [isSingleton=true] | ||
* Define the initializer singleton option | ||
* (one instance for several runs if true) | ||
* @return {Function} | ||
@@ -351,3 +563,3 @@ * Returns a new initializer | ||
* | ||
* import Knifecycle, { inject, options } from 'knifecycle'; | ||
* import Knifecycle, { inject, singleton } from 'knifecycle'; | ||
* import myServiceInitializer from './service'; | ||
@@ -358,3 +570,3 @@ * | ||
* inject(['ENV'], | ||
* options({ singleton: true}, myServiceInitializer) | ||
* singleton(myServiceInitializer) | ||
* ), | ||
@@ -366,7 +578,7 @@ * 'myService', | ||
function options(options, initializer, merge = true) { | ||
function singleton(initializer, isSingleton = true) { | ||
const uniqueInitializer = reuseSpecialProps(initializer, initializer, { | ||
[SPECIAL_PROPS.OPTIONS]: merge ? Object.assign({}, initializer[SPECIAL_PROPS.OPTIONS] || {}, options) : options | ||
[SPECIAL_PROPS.SINGLETON]: isSingleton | ||
}); | ||
debug('Wrapped an initializer with options:', options); | ||
debug('Marked an initializer as singleton:', isSingleton); | ||
return uniqueInitializer; | ||
@@ -417,20 +629,2 @@ } | ||
} | ||
function readFunctionName(aFunction) { | ||
if (typeof aFunction !== 'function') { | ||
throw new _yerror.default('E_AUTO_NAMING_FAILURE', typeof aFunction); | ||
} | ||
const functionName = parseName(aFunction.name || ''); | ||
if (!functionName) { | ||
throw new _yerror.default('E_AUTO_NAMING_FAILURE', aFunction.name); | ||
} | ||
return functionName; | ||
} | ||
function parseName(functionName) { | ||
return functionName.split(' ').pop().replace(/^init(?:ialize)?([A-Z])/, (_, $1) => $1.toLowerCase()); | ||
} | ||
/** | ||
@@ -505,197 +699,2 @@ * Decorator to set an initializer type. | ||
/** | ||
* Decorator that creates an initializer for a constant value | ||
* @param {String} name | ||
* The constant's name. | ||
* @param {any} initializer | ||
* The constant's value | ||
* @return {Function} | ||
* Returns a new initializer | ||
* @example | ||
* import Knifecycle, { constant, service } from 'knifecycle'; | ||
* | ||
* const { printAnswer } = new Knifecycle() | ||
* .register(constant('THE_NUMBER', value)) | ||
* .register(constant('log', console.log.bind(console))) | ||
* .register(service( | ||
* async ({ THE_NUMBER, log }) => () => log(THE_NUMBER), | ||
* 'printAnswer', | ||
* ['THE_NUMBER', 'log'], | ||
* )) | ||
* .run(['printAnswer']); | ||
* | ||
* printAnswer(); // 42 | ||
*/ | ||
function constant(name, value) { | ||
const contantLooksLikeAnInitializer = value instanceof Function && value[SPECIAL_PROPS.INJECT]; | ||
if (contantLooksLikeAnInitializer) { | ||
throw new _yerror.default(E_CONSTANT_INJECTION, value[SPECIAL_PROPS.INJECT]); | ||
} | ||
const uniqueInitializer = initializer({ | ||
name: name, | ||
type: 'constant', | ||
options: { | ||
singleton: true | ||
}, | ||
inject: [], | ||
value: value | ||
}, deliverConstantValue.bind(null, value)); | ||
debug(`Created an initializer from a constant: ${name}.`); | ||
return uniqueInitializer; | ||
} | ||
/** | ||
* Decorator that creates an initializer for a service | ||
* @param {Function} builder | ||
* An initializer returning the service promise | ||
* @param {String} [name] | ||
* The service's name | ||
* @param {Array<String>} [dependencies] | ||
* The service's dependencies | ||
* @param {Object} [options] | ||
* Options attached to the built initializer | ||
* @return {Function} | ||
* Returns a new initializer | ||
* @example | ||
* import Knifecycle, { constant, service } from 'knifecycle'; | ||
* | ||
* const { printAnswer } = new Knifecycle() | ||
* .register(constant('THE_NUMBER', value)) | ||
* .register(constant('log', console.log.bind(console))) | ||
* .register(service( | ||
* async ({ THE_NUMBER, log }) => () => log(THE_NUMBER), | ||
* 'printAnswer', | ||
* ['THE_NUMBER', 'log'], | ||
* { singleton: true } | ||
* )) | ||
* .run(['printAnswer']); | ||
* | ||
* printAnswer(); // 42 | ||
*/ | ||
function service(builder, name, dependencies, options) { | ||
if (!builder) { | ||
throw new _yerror.default('E_NO_SERVICE_BUILDER'); | ||
} | ||
const uniqueInitializer = reuseSpecialProps(builder, builder, { | ||
[SPECIAL_PROPS.NAME]: name, | ||
[SPECIAL_PROPS.TYPE]: 'service', | ||
[SPECIAL_PROPS.INJECT]: dependencies, | ||
[SPECIAL_PROPS.OPTIONS]: options | ||
}); | ||
debug(`Created an initializer from a service builder: ${name || 'anonymous'}.`); | ||
return uniqueInitializer; | ||
} | ||
/** | ||
* Decorator that auto creates a service | ||
* @param {Function} initializer | ||
* An initializer returning the service promise | ||
* @return {Function} | ||
* Returns a new initializer | ||
*/ | ||
function autoService(serviceBuilder) { | ||
const name = readFunctionName(serviceBuilder); | ||
const source = serviceBuilder.toString(); | ||
const dependencies = parseInjections(source, { | ||
allowEmpty: true | ||
}); | ||
return initializer({ | ||
name, | ||
type: 'service', | ||
inject: dependencies | ||
}, serviceBuilder); | ||
} | ||
/** | ||
* Decorator that creates an initializer for a provider | ||
* @param {Function} builder | ||
* A builder returning the provider promise | ||
* @param {String} [name] | ||
* The service's name | ||
* @param {Array<String>} [dependencies] | ||
* The service's dependencies | ||
* @param {Object} [options] | ||
* Options attached to the built initializer | ||
* @return {Function} | ||
* Returns a new initializer | ||
* @example | ||
* | ||
* import Knifecycle, { provider } from 'knifecycle' | ||
* import fs from 'fs'; | ||
* | ||
* const $ = new Knifecycle(); | ||
* | ||
* $.register(provider(configProvider, 'config')); | ||
* | ||
* async function configProvider() { | ||
* return new Promise((resolve, reject) { | ||
* fs.readFile('config.js', function(err, data) { | ||
* let config; | ||
* | ||
* if(err) { | ||
* reject(err); | ||
* return; | ||
* } | ||
* | ||
* try { | ||
* config = JSON.parse(data.toString); | ||
* } catch (err) { | ||
* reject(err); | ||
* return; | ||
* } | ||
* | ||
* resolve({ | ||
* service: config, | ||
* }); | ||
* }); | ||
* }); | ||
* } | ||
*/ | ||
function provider(builder, name, dependencies, options) { | ||
if (!builder) { | ||
throw new _yerror.default('E_NO_PROVIDER_BUILDER'); | ||
} | ||
const uniqueInitializer = reuseSpecialProps(builder, builder, { | ||
[SPECIAL_PROPS.NAME]: name, | ||
[SPECIAL_PROPS.TYPE]: 'provider', | ||
[SPECIAL_PROPS.INJECT]: dependencies, | ||
[SPECIAL_PROPS.OPTIONS]: options | ||
}); | ||
debug(`Created an initializer from a provider builder: ${name || 'anonymous'}.`); | ||
return uniqueInitializer; | ||
} | ||
/** | ||
* Decorator that auto creates a provider | ||
* @param {Function} initializer | ||
* An initializer returning the provider promise | ||
* @return {Function} | ||
* Returns a new initializer | ||
*/ | ||
function autoProvider(providerBuilder) { | ||
const name = readFunctionName(providerBuilder); | ||
const source = providerBuilder.toString(); | ||
const dependencies = parseInjections(source, { | ||
allowEmpty: true | ||
}); | ||
return initializer({ | ||
name, | ||
type: 'provider', | ||
inject: dependencies | ||
}, providerBuilder); | ||
} | ||
async function deliverConstantValue(value) { | ||
return value; | ||
} | ||
/** | ||
* Shortcut to create an initializer with a simple handler | ||
@@ -727,5 +726,5 @@ * @param {Function} handlerFunction | ||
function handler(handlerFunction, name, dependencies, options) { | ||
function handler(handlerFunction, name, dependencies, singleton, extra) { | ||
name = name || handlerFunction[SPECIAL_PROPS.NAME]; | ||
dependencies = dependencies || handlerFunction[SPECIAL_PROPS.INJECT]; | ||
dependencies = dependencies || handlerFunction[SPECIAL_PROPS.INJECT] || []; | ||
@@ -740,3 +739,4 @@ if (!name) { | ||
inject: dependencies, | ||
options | ||
singleton, | ||
extra | ||
}, async (...args) => handlerFunction.bind(null, ...args)); | ||
@@ -743,0 +743,0 @@ } |
146
package.json
{ | ||
"name": "knifecycle", | ||
"version": "10.0.3", | ||
"version": "11.0.0-beta.0", | ||
"description": "Manage your NodeJS processes's lifecycle automatically with an unobtrusive dependency injection implementation.", | ||
"main": "dist/index", | ||
"module": "dist/index.mjs", | ||
"types": "src/index.d.ts", | ||
"types": "dist/index.d.ts", | ||
"repository": { | ||
@@ -13,3 +13,3 @@ "type": "git", | ||
"engines": { | ||
"node": ">=10.19.0" | ||
"node": ">=12.19.0" | ||
}, | ||
@@ -21,13 +21,15 @@ "metapak": { | ||
"eslint", | ||
"codeclimate", | ||
"babel", | ||
"mocha", | ||
"typescript", | ||
"jest", | ||
"travis", | ||
"karma", | ||
"jsdocs", | ||
"jsarch" | ||
"jsarch", | ||
"codeclimate" | ||
], | ||
"data": { | ||
"testsFiles": "src/*.mocha.js", | ||
"files": "src/*.js", | ||
"files": "'src/**/*.ts'", | ||
"testsFiles": "'src/**/*.test.ts'", | ||
"distFiles": "'dist/**/*.js'", | ||
"ignore": [ | ||
@@ -50,21 +52,22 @@ "dist" | ||
"scripts": { | ||
"architecture": "jsarch src/*.js > ARCHITECTURE.md && git add ARCHITECTURE.md", | ||
"architecture": "jsarch 'src/**/*.ts' > ARCHITECTURE.md && git add ARCHITECTURE.md", | ||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md", | ||
"cli": "env NODE_ENV=${NODE_ENV:-cli}", | ||
"compile": "rimraf -f 'dist' && npm run compile:cjs && npm run compile:mjs", | ||
"compile:cjs": "babel --env-name=cjs --out-dir=dist --source-maps=true src", | ||
"compile:mjs": "babel --env-name=mjs --out-file-extension=.mjs --out-dir=dist --source-maps=true src", | ||
"cover": "nyc npm test && nyc report --reporter=html --reporter=text", | ||
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls && rm -rf ./coverage", | ||
"compile:cjs": "babel --env-name=cjs --out-dir=dist --extensions '.ts,.js' --source-maps=true src", | ||
"compile:mjs": "babel --env-name=mjs --out-file-extension=.mjs --out-dir=dist --extensions '.ts,.js' --source-maps=true src", | ||
"cover": "npm run jest -- --coverage", | ||
"coveralls": "npm run cover && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage", | ||
"cz": "env NODE_ENV=${NODE_ENV:-cli} git cz", | ||
"doc": "echo \"# API\" > API.md; jsdoc2md src/*.js >> API.md && git add API.md", | ||
"doc": "echo \"# API\" > API.md; jsdoc2md 'dist/**/*.js' >> API.md && git add API.md", | ||
"jest": "NODE_ENV=test jest", | ||
"karma": "karma start karma.conf.js", | ||
"lint": "eslint src/*.js", | ||
"lint": "eslint 'src/**/*.ts'", | ||
"metapak": "metapak", | ||
"mocha": "mocha --require '@babel/register' src/*.mocha.js", | ||
"precz": "npm run lint && npm t && npm run compile && npm run doc && npm run architecture && npm run metapak -- -s", | ||
"precz": "npm run compile && npm run types && npm run lint && npm t && npm run doc && npm run architecture && npm run metapak -- -s", | ||
"prepublish": "npm run compile", | ||
"prettier": "prettier --write src/*.js", | ||
"preversion": "npm run compile && npm t && npm run lint && npm run doc && npm run architecture && npm run metapak -- -s", | ||
"test": "npm run mocha && npm run karma", | ||
"prettier": "prettier --write 'src/**/*.ts'", | ||
"preversion": "npm run compile && npm run types && npm run lint && npm t && npm run doc && npm run architecture && npm run metapak -- -s", | ||
"test": "npm run jest && npm run karma", | ||
"types": "rimraf -f 'dist/**/*.d.ts' && tsc --project . --declaration --emitDeclarationOnly --outDir dist", | ||
"version": "npm run changelog" | ||
@@ -86,32 +89,40 @@ }, | ||
"devDependencies": { | ||
"@babel/cli": "^7.10.5", | ||
"@babel/core": "^7.11.1", | ||
"@babel/plugin-proposal-object-rest-spread": "^7.11.0", | ||
"@babel/preset-env": "^7.11.0", | ||
"@babel/register": "^7.10.5", | ||
"@babel/cli": "^7.12.1", | ||
"@babel/core": "^7.12.3", | ||
"@babel/plugin-proposal-class-properties": "^7.12.1", | ||
"@babel/plugin-proposal-object-rest-spread": "^7.12.1", | ||
"@babel/preset-env": "^7.12.1", | ||
"@babel/preset-typescript": "^7.12.1", | ||
"@babel/register": "^7.12.1", | ||
"@types/jest": "^26.0.15", | ||
"@typescript-eslint/eslint-plugin": "^4.8.1", | ||
"@typescript-eslint/parser": "^4.8.1", | ||
"babel-eslint": "^10.1.0", | ||
"browserify": "^16.5.1", | ||
"browserify": "^17.0.0", | ||
"commitizen": "^4.1.2", | ||
"conventional-changelog-cli": "^2.1.0", | ||
"conventional-changelog-cli": "^2.1.1", | ||
"coveralls": "^3.1.0", | ||
"cz-conventional-changelog": "^3.2.0", | ||
"eslint": "^7.7.0", | ||
"cz-conventional-changelog": "^3.3.0", | ||
"eslint": "^7.13.0", | ||
"eslint-plugin-prettier": "^3.1.4", | ||
"jest": "^26.6.3", | ||
"jsarch": "^3.0.0", | ||
"jsdoc-to-markdown": "^6.0.1", | ||
"karma": "^5.1.0", | ||
"karma": "^5.2.3", | ||
"karma-browserify": "^7.0.0", | ||
"karma-chrome-launcher": "^3.1.0", | ||
"karma-firefox-launcher": "^1.3.0", | ||
"karma-firefox-launcher": "^2.1.0", | ||
"karma-mocha": "^2.0.1", | ||
"karma-sauce-launcher": "^4.1.2", | ||
"karma-sauce-launcher": "^4.3.3", | ||
"metapak": "^3.1.10", | ||
"metapak-nfroidure": "10.2.5", | ||
"mocha": "^8.1.1", | ||
"nyc": "^15.1.0", | ||
"prettier": "^2.0.5", | ||
"sinon": "^9.0.1" | ||
"metapak-nfroidure": "11.0.3", | ||
"mocha": "8.2.1", | ||
"prettier": "^2.1.2", | ||
"rimraf": "^3.0.2", | ||
"sinon": "^9.2.1", | ||
"typescript": "^4.0.5" | ||
}, | ||
"dependencies": { | ||
"debug": "^4.2.0", | ||
"type-fest": "^0.19.0", | ||
"yerror": "^5.0.0" | ||
@@ -132,2 +143,3 @@ }, | ||
"prettier", | ||
"@typescript-eslint/parser", | ||
"@babel/cli", | ||
@@ -138,10 +150,12 @@ "@babel/core", | ||
"@babel/plugin-proposal-object-rest-spread", | ||
"@babel/preset-typescript", | ||
"@babel/plugin-proposal-class-properties", | ||
"babel-eslint", | ||
"mocha", | ||
"babel-core", | ||
"typescript", | ||
"jest", | ||
"coveralls", | ||
"nyc", | ||
"karma", | ||
"karma-chrome-launcher", | ||
"karma-firefox-launcher", | ||
"karma-mocha", | ||
"jsdoc-to-markdown", | ||
@@ -151,12 +165,13 @@ "jsarch" | ||
}, | ||
"nyc": { | ||
"exclude": [ | ||
"**/*.mocha.js", | ||
"**/*.conf.js", | ||
"dist" | ||
] | ||
"contributors": [], | ||
"bugs": { | ||
"url": "https://github.com/nfroidure/knifecycle/issues" | ||
}, | ||
"contributors": [], | ||
"homepage": "https://github.com/nfroidure/knifecycle#readme", | ||
"eslintConfig": { | ||
"extends": "eslint:recommended", | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/eslint-recommended", | ||
"plugin:@typescript-eslint/recommended" | ||
], | ||
"parserOptions": { | ||
@@ -178,3 +193,7 @@ "ecmaVersion": 2018, | ||
"prettier/prettier": "error" | ||
} | ||
}, | ||
"parser": "@typescript-eslint/parser", | ||
"ignorePatterns": [ | ||
"*.d.ts" | ||
] | ||
}, | ||
@@ -188,9 +207,8 @@ "prettier": { | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/nfroidure/knifecycle/issues" | ||
}, | ||
"homepage": "https://github.com/nfroidure/knifecycle#readme", | ||
"babel": { | ||
"plugins": [], | ||
"plugins": [ | ||
"@babel/proposal-class-properties" | ||
], | ||
"presets": [ | ||
"@babel/typescript", | ||
[ | ||
@@ -200,3 +218,3 @@ "@babel/env", | ||
"targets": { | ||
"node": "10.19.0" | ||
"node": "12.19.0" | ||
} | ||
@@ -237,3 +255,21 @@ } | ||
"sourceMaps": true | ||
}, | ||
"jest": { | ||
"coverageReporters": [ | ||
"lcov" | ||
], | ||
"testPathIgnorePatterns": [ | ||
"/node_modules/" | ||
], | ||
"roots": [ | ||
"<rootDir>/src" | ||
] | ||
}, | ||
"jsarch": { | ||
"parserOptions": { | ||
"plugins": [ | ||
"typescript" | ||
] | ||
} | ||
} | ||
} |
369
README.md
@@ -417,11 +417,37 @@ [//]: # ( ) | ||
## Functions | ||
## Members | ||
<dl> | ||
<dt><a href="#initInitializerBuilder">initInitializerBuilder(services)</a> ⇒ <code>Promise.<function()></code></dt> | ||
<dt><a href="#default">default</a> ⇒ <code>Promise.<function()></code></dt> | ||
<dd><p>Instantiate the initializer builder service</p> | ||
</dd> | ||
</dl> | ||
## Functions | ||
<dl> | ||
<dt><a href="#reuseSpecialProps">reuseSpecialProps(from, to, [amend])</a> ⇒ <code>function</code></dt> | ||
<dd><p>Apply special props to the given function from another one</p> | ||
<dd><p>Apply special props to the given initializer from another one | ||
and optionally amend with new special props</p> | ||
</dd> | ||
<dt><a href="#constant">constant(name, value)</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that creates an initializer for a constant value</p> | ||
</dd> | ||
<dt><a href="#service">service(serviceBuilder, [name], [dependencies], [singleton], [extra])</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that creates an initializer from a service builder</p> | ||
</dd> | ||
<dt><a href="#autoService">autoService(serviceBuilder)</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that creates an initializer from a service | ||
builder by automatically detecting its name | ||
and dependencies</p> | ||
</dd> | ||
<dt><a href="#provider">provider(providerBuilder, [name], [dependencies], [singleton], [extra])</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that creates an initializer for a provider | ||
builder</p> | ||
</dd> | ||
<dt><a href="#autoProvider">autoProvider(providerBuilder)</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that creates an initializer from a provider | ||
builder by automatically detecting its name | ||
and dependencies</p> | ||
</dd> | ||
<dt><a href="#wrapInitializer">wrapInitializer(wrapper, baseInitializer)</a> ⇒ <code>function</code></dt> | ||
@@ -456,4 +482,4 @@ <dd><p>Allows to wrap an initializer to add extra initialization steps</p> | ||
</dd> | ||
<dt><a href="#options">options(options, initializer, [merge])</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator to amend an initializer options.</p> | ||
<dt><a href="#singleton">singleton(initializer, [isSingleton])</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator to set an initializer singleton option.</p> | ||
</dd> | ||
@@ -472,17 +498,2 @@ <dt><a href="#name">name(name, initializer)</a> ⇒ <code>function</code></dt> | ||
</dd> | ||
<dt><a href="#constant">constant(name, initializer)</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that creates an initializer for a constant value</p> | ||
</dd> | ||
<dt><a href="#service">service(builder, [name], [dependencies], [options])</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that creates an initializer for a service</p> | ||
</dd> | ||
<dt><a href="#autoService">autoService(initializer)</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that auto creates a service</p> | ||
</dd> | ||
<dt><a href="#provider">provider(builder, [name], [dependencies], [options])</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that creates an initializer for a provider</p> | ||
</dd> | ||
<dt><a href="#autoProvider">autoProvider(initializer)</a> ⇒ <code>function</code></dt> | ||
<dd><p>Decorator that auto creates a provider</p> | ||
</dd> | ||
<dt><a href="#handler">handler(handlerFunction, [name], [dependencies], [options])</a> ⇒ <code>function</code></dt> | ||
@@ -626,3 +637,3 @@ <dd><p>Shortcut to create an initializer with a simple handler</p> | ||
**Kind**: instance method of [<code>Knifecycle</code>](#Knifecycle) | ||
**Returns**: <code>Promise</code> - Service dependencies hash promise. | ||
**Returns**: <code>Promise</code> - Service descriptor promise. | ||
@@ -671,8 +682,8 @@ | Param | Type | Description | | ||
<a name="initInitializerBuilder"></a> | ||
<a name="default"></a> | ||
## initInitializerBuilder(services) ⇒ <code>Promise.<function()></code> | ||
## default ⇒ <code>Promise.<function()></code> | ||
Instantiate the initializer builder service | ||
**Kind**: global function | ||
**Kind**: global variable | ||
**Returns**: <code>Promise.<function()></code> - A promise of the buildInitializer function | ||
@@ -693,40 +704,157 @@ | ||
``` | ||
<a name="initInitializerBuilder..buildInitializer"></a> | ||
<a name="reuseSpecialProps"></a> | ||
### initInitializerBuilder~buildInitializer(dependencies) ⇒ <code>Promise.<String></code> | ||
Create a JavaScript module that initialize | ||
a set of dependencies with hardcoded | ||
import/awaits. | ||
## reuseSpecialProps(from, to, [amend]) ⇒ <code>function</code> | ||
Apply special props to the given initializer from another one | ||
and optionally amend with new special props | ||
**Kind**: inner method of [<code>initInitializerBuilder</code>](#initInitializerBuilder) | ||
**Returns**: <code>Promise.<String></code> - The JavaScript module content | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - The newly built initializer | ||
| Param | Type | Default | Description | | ||
| --- | --- | --- | --- | | ||
| from | <code>function</code> | | The initializer in which to pick the props | | ||
| to | <code>function</code> | | The initializer from which to build the new one | | ||
| [amend] | <code>Object</code> | <code>{}</code> | Some properties to override | | ||
<a name="constant"></a> | ||
## constant(name, value) ⇒ <code>function</code> | ||
Decorator that creates an initializer for a constant value | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - Returns a new constant initializer | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| dependencies | <code>Array.<String></code> | The main dependencies | | ||
| name | <code>String</code> | The constant's name. | | ||
| value | <code>any</code> | The constant's value | | ||
**Example** | ||
```js | ||
import initInitializerBuilder from 'knifecycle/dist/build'; | ||
import Knifecycle, { constant, service } from 'knifecycle'; | ||
const buildInitializer = await initInitializerBuilder({ | ||
$autoload: async () => {}, | ||
}); | ||
const { printAnswer } = new Knifecycle() | ||
.register(constant('THE_NUMBER', value)) | ||
.register(constant('log', console.log.bind(console))) | ||
.register(service( | ||
async ({ THE_NUMBER, log }) => () => log(THE_NUMBER), | ||
'printAnswer', | ||
['THE_NUMBER', 'log'], | ||
)) | ||
.run(['printAnswer']); | ||
const content = await buildInitializer(['entryPoint']); | ||
printAnswer(); // 42 | ||
``` | ||
<a name="reuseSpecialProps"></a> | ||
<a name="service"></a> | ||
## reuseSpecialProps(from, to, [amend]) ⇒ <code>function</code> | ||
Apply special props to the given function from another one | ||
## service(serviceBuilder, [name], [dependencies], [singleton], [extra]) ⇒ <code>function</code> | ||
Decorator that creates an initializer from a service builder | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - The newly built function | ||
**Returns**: <code>function</code> - Returns a new initializer | ||
| Param | Type | Default | Description | | ||
| --- | --- | --- | --- | | ||
| from | <code>function</code> | | The initialization function in which to pick the props | | ||
| to | <code>function</code> | | The initialization function from which to build the new one | | ||
| [amend] | <code>Object</code> | <code>{}</code> | Some properties to override | | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| serviceBuilder | <code>function</code> | An async function to build the service | | ||
| [name] | <code>String</code> | The service's name | | ||
| [dependencies] | <code>Array.<String></code> | The service's injected dependencies | | ||
| [singleton] | <code>Boolean</code> | Whether the service is a singleton or not | | ||
| [extra] | <code>any</code> | Eventual extra informations | | ||
**Example** | ||
```js | ||
import Knifecycle, { constant, service } from 'knifecycle'; | ||
const { printAnswer } = new Knifecycle() | ||
.register(constant('THE_NUMBER', value)) | ||
.register(constant('log', console.log.bind(console))) | ||
.register(service( | ||
async ({ THE_NUMBER, log }) => () => log(THE_NUMBER), | ||
'printAnswer', | ||
['THE_NUMBER', 'log'], | ||
true | ||
)) | ||
.run(['printAnswer']); | ||
printAnswer(); // 42 | ||
``` | ||
<a name="autoService"></a> | ||
## autoService(serviceBuilder) ⇒ <code>function</code> | ||
Decorator that creates an initializer from a service | ||
builder by automatically detecting its name | ||
and dependencies | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - Returns a new initializer | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| serviceBuilder | <code>function</code> | An async function to build the service | | ||
<a name="provider"></a> | ||
## provider(providerBuilder, [name], [dependencies], [singleton], [extra]) ⇒ <code>function</code> | ||
Decorator that creates an initializer for a provider | ||
builder | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - Returns a new provider initializer | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| providerBuilder | <code>function</code> | An async function to build the service provider | | ||
| [name] | <code>String</code> | The service's name | | ||
| [dependencies] | <code>Array.<String></code> | The service's dependencies | | ||
| [singleton] | <code>Boolean</code> | Whether the service is a singleton or not | | ||
| [extra] | <code>any</code> | Eventual extra informations | | ||
**Example** | ||
```js | ||
import Knifecycle, { provider } from 'knifecycle' | ||
import fs from 'fs'; | ||
const $ = new Knifecycle(); | ||
$.register(provider(configProvider, 'config')); | ||
async function configProvider() { | ||
return new Promise((resolve, reject) { | ||
fs.readFile('config.js', function(err, data) { | ||
let config; | ||
if(err) { | ||
reject(err); | ||
return; | ||
} | ||
try { | ||
config = JSON.parse(data.toString); | ||
} catch (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve({ | ||
service: config, | ||
}); | ||
}); | ||
}); | ||
} | ||
``` | ||
<a name="autoProvider"></a> | ||
## autoProvider(providerBuilder) ⇒ <code>function</code> | ||
Decorator that creates an initializer from a provider | ||
builder by automatically detecting its name | ||
and dependencies | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - Returns a new provider initializer | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| providerBuilder | <code>function</code> | An async function to build the service provider | | ||
<a name="wrapInitializer"></a> | ||
@@ -756,3 +884,3 @@ | ||
| --- | --- | --- | | ||
| dependencies | <code>Array.<String></code> | List of dependencies declarations to declare which services the initializer needs to resolve its own service | | ||
| dependencies | <code>Array.<String></code> | List of dependencies declarations to declare which services the initializer needs to provide its own service | | ||
| initializer | <code>function</code> | The initializer to tweak | | ||
@@ -885,6 +1013,6 @@ | ||
``` | ||
<a name="options"></a> | ||
<a name="singleton"></a> | ||
## options(options, initializer, [merge]) ⇒ <code>function</code> | ||
Decorator to amend an initializer options. | ||
## singleton(initializer, [isSingleton]) ⇒ <code>function</code> | ||
Decorator to set an initializer singleton option. | ||
@@ -896,10 +1024,8 @@ **Kind**: global function | ||
| --- | --- | --- | --- | | ||
| options | <code>Object</code> | | Options to set to the initializer | | ||
| options.singleton | <code>Object</code> | | Define the initializer service as a singleton (one instance for several runs) | | ||
| initializer | <code>function</code> | | The initializer to tweak | | ||
| [merge] | <code>function</code> | <code>true</code> | Whether options should be merged or not | | ||
| [isSingleton] | <code>boolean</code> | <code>true</code> | Define the initializer singleton option (one instance for several runs if true) | | ||
**Example** | ||
```js | ||
import Knifecycle, { inject, options } from 'knifecycle'; | ||
import Knifecycle, { inject, singleton } from 'knifecycle'; | ||
import myServiceInitializer from './service'; | ||
@@ -910,3 +1036,3 @@ | ||
inject(['ENV'], | ||
options({ singleton: true}, myServiceInitializer) | ||
singleton(myServiceInitializer) | ||
), | ||
@@ -1009,135 +1135,2 @@ 'myService', | ||
``` | ||
<a name="constant"></a> | ||
## constant(name, initializer) ⇒ <code>function</code> | ||
Decorator that creates an initializer for a constant value | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - Returns a new initializer | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| name | <code>String</code> | The constant's name. | | ||
| initializer | <code>any</code> | The constant's value | | ||
**Example** | ||
```js | ||
import Knifecycle, { constant, service } from 'knifecycle'; | ||
const { printAnswer } = new Knifecycle() | ||
.register(constant('THE_NUMBER', value)) | ||
.register(constant('log', console.log.bind(console))) | ||
.register(service( | ||
async ({ THE_NUMBER, log }) => () => log(THE_NUMBER), | ||
'printAnswer', | ||
['THE_NUMBER', 'log'], | ||
)) | ||
.run(['printAnswer']); | ||
printAnswer(); // 42 | ||
``` | ||
<a name="service"></a> | ||
## service(builder, [name], [dependencies], [options]) ⇒ <code>function</code> | ||
Decorator that creates an initializer for a service | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - Returns a new initializer | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| builder | <code>function</code> | An initializer returning the service promise | | ||
| [name] | <code>String</code> | The service's name | | ||
| [dependencies] | <code>Array.<String></code> | The service's dependencies | | ||
| [options] | <code>Object</code> | Options attached to the built initializer | | ||
**Example** | ||
```js | ||
import Knifecycle, { constant, service } from 'knifecycle'; | ||
const { printAnswer } = new Knifecycle() | ||
.register(constant('THE_NUMBER', value)) | ||
.register(constant('log', console.log.bind(console))) | ||
.register(service( | ||
async ({ THE_NUMBER, log }) => () => log(THE_NUMBER), | ||
'printAnswer', | ||
['THE_NUMBER', 'log'], | ||
{ singleton: true } | ||
)) | ||
.run(['printAnswer']); | ||
printAnswer(); // 42 | ||
``` | ||
<a name="autoService"></a> | ||
## autoService(initializer) ⇒ <code>function</code> | ||
Decorator that auto creates a service | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - Returns a new initializer | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| initializer | <code>function</code> | An initializer returning the service promise | | ||
<a name="provider"></a> | ||
## provider(builder, [name], [dependencies], [options]) ⇒ <code>function</code> | ||
Decorator that creates an initializer for a provider | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - Returns a new initializer | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| builder | <code>function</code> | A builder returning the provider promise | | ||
| [name] | <code>String</code> | The service's name | | ||
| [dependencies] | <code>Array.<String></code> | The service's dependencies | | ||
| [options] | <code>Object</code> | Options attached to the built initializer | | ||
**Example** | ||
```js | ||
import Knifecycle, { provider } from 'knifecycle' | ||
import fs from 'fs'; | ||
const $ = new Knifecycle(); | ||
$.register(provider(configProvider, 'config')); | ||
async function configProvider() { | ||
return new Promise((resolve, reject) { | ||
fs.readFile('config.js', function(err, data) { | ||
let config; | ||
if(err) { | ||
reject(err); | ||
return; | ||
} | ||
try { | ||
config = JSON.parse(data.toString); | ||
} catch (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve({ | ||
service: config, | ||
}); | ||
}); | ||
}); | ||
} | ||
``` | ||
<a name="autoProvider"></a> | ||
## autoProvider(initializer) ⇒ <code>function</code> | ||
Decorator that auto creates a provider | ||
**Kind**: global function | ||
**Returns**: <code>function</code> - Returns a new initializer | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| initializer | <code>function</code> | An initializer returning the provider promise | | ||
<a name="handler"></a> | ||
@@ -1144,0 +1137,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1124642
11963
102
2
2
4
3
34
44
1
1231
1
+ Addedtype-fest@^0.19.0
+ Addedtype-fest@0.19.0(transitive)