From 29b64a4638f78992b6e8961ff4175791010c0a42 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Thu, 21 Oct 2021 17:35:30 -0700 Subject: [PATCH] feature(@jsdoc/core): make the dependency provider more flexible You can now register classes and factory functions, either as singletons or not. --- packages/jsdoc-core/lib/dependencies.js | 36 +++++++++- .../jsdoc-core/test/specs/lib/dependencies.js | 66 ++++++++++++++++++- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/packages/jsdoc-core/lib/dependencies.js b/packages/jsdoc-core/lib/dependencies.js index a49a33c2..959edc6c 100644 --- a/packages/jsdoc-core/lib/dependencies.js +++ b/packages/jsdoc-core/lib/dependencies.js @@ -24,10 +24,44 @@ class Dependencies { registerClass(name, constructor, ...deps) { this._bottle.service(name, constructor, ...deps); + // Remove the cached provider. + this._bottle.middleware(name, (_, next) => { + this._bottle.resetProviders([name]); + next(); + }); + } + + registerFactory(name, factory, ...deps) { + const realFactory = () => { + const args = deps.map((dep) => this.get(dep)); + + return factory(...args); + }; + + this._bottle.serviceFactory(name, realFactory, ...deps); + // Remove the cached provider. + this._bottle.middleware(name, (_, next) => { + this._bottle.resetProviders([name]); + next(); + }); + } + + registerSingleton(name, constructor, ...deps) { + this._bottle.service(name, constructor, ...deps); + } + + registerSingletonFactory(name, factory, ...deps) { + const realFactory = () => { + const args = deps.map((dep) => this.get(dep)); + + return factory(...args); + }; + + this._bottle.factory(name, realFactory); } registerValue(name, value) { - this._bottle.constant(name, value); + this._bottle.value(name, value); } } diff --git a/packages/jsdoc-core/test/specs/lib/dependencies.js b/packages/jsdoc-core/test/specs/lib/dependencies.js index c24184d6..be888cc5 100644 --- a/packages/jsdoc-core/test/specs/lib/dependencies.js +++ b/packages/jsdoc-core/test/specs/lib/dependencies.js @@ -49,7 +49,7 @@ describe('@jsdoc/core/lib/dependencies', () => { expect(instance.bar).toBeInstanceOf(Bar); }); - it('returns the same instance every time', () => { + it('returns a new instance every time, for normal classes', () => { class Foo {} let instance1; @@ -59,10 +59,49 @@ describe('@jsdoc/core/lib/dependencies', () => { instance1 = container.get('Foo'); instance2 = container.get('Foo'); + expect(instance2).not.toBe(instance1); + }); + + it('returns a new result every time, for factories', () => { + const factory = () => new Set(); + let set1; + let set2; + + container.registerFactory('setFactory', factory); + set1 = container.get('setFactory'); + set2 = container.get('setFactory'); + + expect(set2).not.toBe(set1); + }); + + it('returns the same instance every time, for singletons', () => { + class Foo {} + + let instance1; + let instance2; + + container.registerSingleton('Foo', Foo); + instance1 = container.get('Foo'); + instance2 = container.get('Foo'); + expect(instance2).toBe(instance1); }); - it('returns static values', () => { + it('returns the same instance every time, for singleton factories', () => { + class Foo {} + + const factory = () => new Foo(); + let instance1; + let instance2; + + container.registerSingletonFactory('Foo', factory); + instance1 = container.get('Foo'); + instance2 = container.get('Foo'); + + expect(instance2).toBe(instance1); + }); + + it('returns values', () => { const value = new Set(); container.registerValue('foo', value); @@ -80,6 +119,29 @@ describe('@jsdoc/core/lib/dependencies', () => { }); }); + describe('registerFactory', () => { + // The tests for `get()` also test the behavior of this method more extensively. + it('accepts a name and function', () => { + expect(() => container.registerFactory('foo', () => null)).not.toThrow(); + }); + }); + + describe('registerSingleton', () => { + // The tests for `get()` also test the behavior of this method more extensively. + it('accepts a name and constructor', () => { + class Foo {} + + expect(() => container.registerSingleton('Foo', Foo)).not.toThrow(); + }); + }); + + describe('registerSingletonFactory', () => { + // The tests for `get()` also test the behavior of this method more extensively. + it('accepts a name and function', () => { + expect(() => container.registerSingletonFactory('foo', () => null)).not.toThrow(); + }); + }); + describe('registerValue', () => { // The tests for `get()` also test the behavior of this method more extensively. it('accepts a name and value', () => {