From 60360d6dc8d5dfa80c8c355eca6d26681f02794b Mon Sep 17 00:00:00 2001 From: Gilad S Date: Sun, 26 Nov 2023 00:24:45 +0200 Subject: [PATCH] feat(minor): support `WeakRef` targets in `DisposeAggregator` and `AsyncDisposeAggregator` --- src/AsyncDisposeAggregator.ts | 12 +++++++++- src/DisposeAggregator.ts | 12 ++++++++-- test/AsyncDisposeAggregator.test.ts | 4 ++-- test/DisposeAggregator.test.ts | 4 ++-- test/tsconfig.json | 35 +++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 test/tsconfig.json diff --git a/src/AsyncDisposeAggregator.ts b/src/AsyncDisposeAggregator.ts index cb61b65..3a5b749 100644 --- a/src/AsyncDisposeAggregator.ts +++ b/src/AsyncDisposeAggregator.ts @@ -57,6 +57,7 @@ export class AsyncDisposeAggregator { /** * Disposes all the targets that have been added and clears the list of targets. + * You can wrap the target with a `WeakRef` to prevent this class from holding a strong reference to the target. * @returns {Promise} */ public async dispose(): Promise { @@ -75,6 +76,9 @@ export class AsyncDisposeAggregator { } /* c8 ignore stop */ } + if (typeof WeakRef !== "undefined" && disposeTarget instanceof WeakRef) + disposeTarget = disposeTarget.deref(); + if (disposeTarget == null) continue; else if ( @@ -117,4 +121,10 @@ type DisposeTarget = (() => void | Promise) | { [Symbol.dispose](): void } | { dispose(): void | Promise -}; +} | WeakRef<{ + [Symbol.asyncDispose](): void | Promise +} | { + [Symbol.dispose](): void +} | { + dispose(): void | Promise +}>; diff --git a/src/DisposeAggregator.ts b/src/DisposeAggregator.ts index b13a7bd..8da7513 100644 --- a/src/DisposeAggregator.ts +++ b/src/DisposeAggregator.ts @@ -45,12 +45,16 @@ export class DisposeAggregator { /** * Disposes all the targets that have been added and clears the list of targets. + * You can wrap the target with a `WeakRef` to prevent this class from holding a strong reference to the target. */ public dispose(): void { this._ensureNotDisposed(); while (this._targets.length > 0) { - const disposeTarget = this._targets.shift(); + let disposeTarget = this._targets.shift(); + + if (typeof WeakRef !== "undefined" && disposeTarget instanceof WeakRef) + disposeTarget = disposeTarget.deref(); if (disposeTarget == null) continue; @@ -84,4 +88,8 @@ type DisposeAggregatorTarget = (() => void) | { [Symbol.dispose](): void } | { dispose(): void -}; +} | WeakRef<{ + [Symbol.dispose](): void +} | { + dispose(): void +}>; diff --git a/test/AsyncDisposeAggregator.test.ts b/test/AsyncDisposeAggregator.test.ts index 8102495..7ef60d5 100644 --- a/test/AsyncDisposeAggregator.test.ts +++ b/test/AsyncDisposeAggregator.test.ts @@ -43,13 +43,13 @@ describe("AsyncDisposeAggregator", () => { expect(stub3.disposed).toBe(false); }); - test("Symbol.asyncDispose", async () => { + test("WeakRef, Symbol.asyncDispose", async () => { const disposeAggregator = new AsyncDisposeAggregator(); const stub = new DisposeStub(); expect(stub.disposed).toBe(false); - disposeAggregator.add(stub); + disposeAggregator.add(new WeakRef(stub)); expect(disposeAggregator.targetCount).toBe(1); await disposeAggregator[Symbol.asyncDispose](); diff --git a/test/DisposeAggregator.test.ts b/test/DisposeAggregator.test.ts index 90fcacc..277ca79 100644 --- a/test/DisposeAggregator.test.ts +++ b/test/DisposeAggregator.test.ts @@ -32,13 +32,13 @@ describe("DisposeAggregator", () => { expect(stub2.disposed).toBe(false); }); - test("Symbol.asyncDispose", () => { + test("WeakRef, Symbol.dispose", () => { const disposeAggregator = new DisposeAggregator(); const stub = new DisposeStub(); expect(stub.disposed).toBe(false); - disposeAggregator.add(stub); + disposeAggregator.add(new WeakRef(stub)); expect(disposeAggregator.targetCount).toBe(1); disposeAggregator[Symbol.dispose](); diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000..df02111 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "lib": ["es2022"], + "module": "es2022", + "target": "es2022", + "esModuleInterop": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitOverride": true, + "removeComments": false, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "moduleResolution": "node", + "resolveJsonModule": false, + "strictNullChecks": true, + "isolatedModules": true, + "noEmit": false, + "outDir": "./dist", + "strict": true, + "sourceMap": true, + "composite": false, + "declaration": true, + "stripInternal": true + }, + "files": [], + "include": [ + "." + ], + "ts-node": { + "esm": true + } +}