From 51fa61964f0b8f4d06db93a014b49b21ec53ea71 Mon Sep 17 00:00:00 2001
From: enrilzhou <49245495+enrilzhou@users.noreply.github.com>
Date: Fri, 1 Jul 2022 05:13:59 +0800
Subject: [PATCH] feat(jest-leak-detector): use FinalizationRegistry when it
exists to get rid of external dependency (#12973)
---
CHANGELOG.md | 2 ++
packages/jest-leak-detector/src/index.ts | 39 ++++++++++++++++--------
2 files changed, 29 insertions(+), 12 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ec5b8b40551..680ec6a667ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
### Features
+- `[jest-leak-detector]` Use native `FinalizationRegistry` when it exists to get rid of external C dependency ([#12973](https://github.com/facebook/jest/pull/12973))
+
### Fixes
- `[jest-changed-files]` Fix a lock-up after repeated invocations ([#12757](https://github.com/facebook/jest/issues/12757))
diff --git a/packages/jest-leak-detector/src/index.ts b/packages/jest-leak-detector/src/index.ts
index 33a5e8cefe97..f315fb592e11 100644
--- a/packages/jest-leak-detector/src/index.ts
+++ b/packages/jest-leak-detector/src/index.ts
@@ -4,6 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
+///
import {promisify} from 'util';
import {setFlagsFromString} from 'v8';
@@ -15,6 +16,7 @@ const tick = promisify(setImmediate);
export default class LeakDetector {
private _isReferenceBeingHeld: boolean;
+ private _finalizationRegistry?: FinalizationRegistry;
constructor(value: unknown) {
if (isPrimitive(value)) {
@@ -26,23 +28,36 @@ export default class LeakDetector {
);
}
- let weak: typeof import('weak-napi');
+ // TODO: Remove the `if` and `weak-napi` when we drop node 12, as v14 supports FinalizationRegistry
+ if (globalThis.FinalizationRegistry) {
+ // When `_finalizationRegistry` is GCed the callback we set will no longer be called,
+ // so we need to assign it to `this` to keep it referenced
+ this._finalizationRegistry = new FinalizationRegistry(() => {
+ this._isReferenceBeingHeld = false;
+ });
- try {
- // eslint-disable-next-line import/no-extraneous-dependencies
- weak = require('weak-napi');
- } catch (err: any) {
- if (!err || err.code !== 'MODULE_NOT_FOUND') {
- throw err;
+ this._finalizationRegistry.register(value as object, undefined);
+ } else {
+ let weak: typeof import('weak-napi');
+
+ try {
+ // eslint-disable-next-line import/no-extraneous-dependencies
+ weak = require('weak-napi');
+ } catch (err: any) {
+ if (!err || err.code !== 'MODULE_NOT_FOUND') {
+ throw err;
+ }
+
+ throw new Error(
+ 'The leaking detection mechanism requires newer version of node that supports ' +
+ 'FinalizationRegistry, update your node or install the "weak-napi" package' +
+ 'which support current node version as a dependency on your main project.',
+ );
}
- throw new Error(
- 'The leaking detection mechanism requires the "weak-napi" package to be installed and work. ' +
- 'Please install it as a dependency on your main project',
- );
+ weak(value as object, () => (this._isReferenceBeingHeld = false));
}
- weak(value as object, () => (this._isReferenceBeingHeld = false));
this._isReferenceBeingHeld = true;
// Ensure value is not leaked by the closure created by the "weak" callback.