Skip to content

Commit

Permalink
support dispose for all instances
Browse files Browse the repository at this point in the history
  • Loading branch information
blake-mealey committed Sep 22, 2022
1 parent f738999 commit 479fbb3
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/__tests__/disposable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe("Disposable", () => {

it("returns false when dispose method takes too many args", () => {
const specialDisposable = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
dispose(_: any) {}
};

Expand Down
31 changes: 25 additions & 6 deletions src/__tests__/global-container.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -861,13 +861,29 @@ describe("dispose", () => {
it("disposes all child disposables", () => {
const container = globalContainer.createChildContainer();

container.registerInstance(Bar, new Bar());
container.register("ValueBar", {useValue: new Bar()});
container.register("FactoryBar", {
useFactory: () => new Bar()
});
container.register("ClassBar", Bar);
container.register("TokenBar", {useToken: "ValueBar"});

const foo = container.resolve(Foo);
const bar = container.resolve(Bar);
const valueBar = container.resolve<Bar>("ValueBar");
const factoryBar = container.resolve<Bar>("FactoryBar");
const classBar = container.resolve<Bar>("ClassBar");
const tokenBar = container.resolve<Bar>("TokenBar");

container.dispose();

expect(foo.disposed).toBeTruthy();
expect(bar.disposed).toBeTruthy();
expect(bar.dispose).toBeTruthy();
expect(valueBar.disposed).toBeTruthy();
expect(factoryBar.disposed).toBeTruthy();
expect(classBar.disposed).toBeTruthy();
expect(tokenBar.disposed).toBeTruthy();
});

it("disposes asynchronous disposables", async () => {
Expand All @@ -894,14 +910,17 @@ describe("dispose", () => {
expect(foo2.disposed).toBeTruthy();
});

it("doesn't dispose of instances created external to the container", () => {
const foo = new Foo();
it("disposes all instances that were resolved together", () => {
const container = globalContainer.createChildContainer();

container.registerInstance(Foo, foo);
container.resolve(Foo);
container.register("FooList", {useValue: new Foo()});
container.register("FooList", {useValue: new Bar()});

const fooList = container.resolveAll<Foo | Bar>("FooList");

container.dispose();

expect(foo.disposed).toBeFalsy();
expect(fooList[0].disposed).toBeTruthy();
expect(fooList[1].disposed).toBeTruthy();
});
});
11 changes: 11 additions & 0 deletions src/dependency-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,13 @@ class InternalDependencyContainer implements DependencyContainer {

const returnInstance = isSingleton || isContainerScoped;

let newResolution = true;
let resolved: T;

if (isValueProvider(registration.provider)) {
resolved = registration.provider.useValue;
} else if (isTokenProvider(registration.provider)) {
newResolution = returnInstance;
resolved = returnInstance
? registration.instance ||
(registration.instance = this.resolve(
Expand All @@ -324,6 +326,7 @@ class InternalDependencyContainer implements DependencyContainer {
))
: this.resolve(registration.provider.useToken, context);
} else if (isClassProvider(registration.provider)) {
newResolution = returnInstance;
resolved = returnInstance
? registration.instance ||
(registration.instance = this.construct(
Expand All @@ -334,6 +337,7 @@ class InternalDependencyContainer implements DependencyContainer {
} else if (isFactoryProvider(registration.provider)) {
resolved = registration.provider.useFactory(this);
} else {
newResolution = false;
resolved = this.construct(registration.provider, context);
}

Expand All @@ -342,6 +346,13 @@ class InternalDependencyContainer implements DependencyContainer {
context.scopedResolutions.set(registration, resolved);
}

// If this is a new resolution and the instance is disposable, add it to our set of disposables
if (newResolution && isDisposable(resolved)) {
if (!this.disposables.has(resolved)) {
this.disposables.add(resolved);
}
}

return resolved;
}

Expand Down
4 changes: 3 additions & 1 deletion src/types/disposable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ export default interface Disposable {
}

export function isDisposable(value: any): value is Disposable {
if (typeof value.dispose !== "function") return false;
if (typeof value !== "object" || typeof value.dispose !== "function") {
return false;
}

const disposeFun: Function = value.dispose;

Expand Down

0 comments on commit 479fbb3

Please sign in to comment.