Skip to content

Commit 4bb7a2d

Browse files
lh0x00SimenB
authored andcommitted
Use weak-napi instead of weak in jest-leak-detector
1 parent ce47c6c commit 4bb7a2d

File tree

7 files changed

+98
-63
lines changed

7 files changed

+98
-63
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- `[expect]` Display expectedDiff more carefully in toBeCloseTo ([#8389](https://github.com/facebook/jest/pull/8389))
88
- `[jest-fake-timers]` `getTimerCount` will not include cancelled immediates ([#8764](https://github.com/facebook/jest/pull/8764))
9+
- `[jest-leak-detector]` [**BREAKING**] Use `weak-napi` instead of `weak` package ([#8686](https://github.com/facebook/jest/pull/8686))
910
- `[jest-snapshot]` Remove only the added newlines in multiline snapshots ([#8859](https://github.com/facebook/jest/pull/8859))
1011

1112
### Chore & Maintenance

packages/jest-leak-detector/README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@ Internally creates a weak reference to the object, and forces garbage collection
77
## Example
88

99
```javascript
10-
let reference = {};
10+
(async function() {
11+
let reference = {};
12+
let isLeaking;
1113

12-
const detector = new LeakDetector(reference);
14+
const detector = new LeakDetector(reference);
1315

14-
// Reference is held in memory.
15-
console.log(detector.isLeaking()); // true
16+
// Reference is held in memory.
17+
isLeaking = await detector.isLeaking();
18+
console.log(isLeaking); // true
1619

17-
// We destroy the only reference to the object.
18-
reference = null;
20+
// We destroy the only reference to the object.
21+
reference = null;
1922

20-
// Reference is gone.
21-
console.log(detector.isLeaking()); // false
23+
// Reference is gone.
24+
isLeaking = await detector.isLeaking();
25+
console.log(isLeaking); // false
26+
})();
2227
```

packages/jest-leak-detector/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
"types": "build/index.d.ts",
1212
"dependencies": {
1313
"jest-get-type": "^24.9.0",
14-
"pretty-format": "^24.9.0"
14+
"pretty-format": "^24.9.0",
15+
"weak-napi": "^1.0.3"
1516
},
1617
"devDependencies": {
17-
"@types/weak": "^1.0.0",
18-
"weak": "^1.0.1"
18+
"@types/weak-napi": "^1.0.0"
1919
},
2020
"engines": {
2121
"node": ">= 8"

packages/jest-leak-detector/src/__tests__/index.test.ts

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,39 +28,42 @@ it('complains if the value is a primitive', () => {
2828
expect(() => new LeakDetector(NaN)).toThrowErrorMatchingSnapshot();
2929
});
3030

31-
it('does not show the GC if hidden', () => {
31+
it('does not show the GC if hidden', async () => {
3232
const detector = new LeakDetector({});
3333

3434
// @ts-ignore: purposefully removed
3535
global.gc = undefined;
36-
detector.isLeaking();
36+
await detector.isLeaking();
3737
expect(global.gc).not.toBeDefined();
3838
});
3939

40-
it('does not hide the GC if visible', () => {
40+
it('does not hide the GC if visible', async () => {
4141
const detector = new LeakDetector({});
4242

4343
global.gc = () => {};
44-
detector.isLeaking();
44+
await detector.isLeaking();
4545
expect(global.gc).toBeDefined();
4646
});
4747

48-
it('correctly checks simple leaks', () => {
48+
it('correctly checks simple leaks', async () => {
4949
let reference: unknown = {};
50+
let isLeaking: boolean;
5051

5152
const detector = new LeakDetector(reference);
5253

5354
// Reference is still held in memory.
54-
expect(detector.isLeaking()).toBe(true);
55+
isLeaking = await detector.isLeaking();
56+
expect(isLeaking).toBe(true);
5557

5658
// We destroy the only reference to the object we had.
5759
reference = null;
5860

5961
// Reference should be gone.
60-
expect(detector.isLeaking()).toBe(false);
62+
isLeaking = await detector.isLeaking();
63+
expect(isLeaking).toBe(false);
6164
});
6265

63-
it('tests different objects', () => {
66+
it('tests different objects', async () => {
6467
const refs = [
6568
function() {},
6669
() => {},
@@ -73,12 +76,20 @@ it('tests different objects', () => {
7376

7477
const detectors = refs.map(ref => new LeakDetector(ref));
7578

76-
detectors.forEach(detector => expect(detector.isLeaking()).toBe(true));
77-
refs.forEach((_, i) => (refs[i] = null));
78-
detectors.forEach(detector => expect(detector.isLeaking()).toBe(false));
79+
let isLeaking: boolean;
80+
for (const i in detectors) {
81+
isLeaking = await detectors[i].isLeaking();
82+
expect(isLeaking).toBe(true);
83+
refs[i] = null;
84+
}
85+
86+
for (const i in detectors) {
87+
isLeaking = await detectors[i].isLeaking();
88+
expect(isLeaking).toBe(false);
89+
}
7990
});
8091

81-
it('correctly checks more complex leaks', () => {
92+
it('correctly checks more complex leaks', async () => {
8293
let ref1: any = {};
8394
let ref2: any = {};
8495

@@ -89,21 +100,30 @@ it('correctly checks more complex leaks', () => {
89100
const detector1 = new LeakDetector(ref1);
90101
const detector2 = new LeakDetector(ref2);
91102

103+
let isLeaking1: boolean;
104+
let isLeaking2: boolean;
105+
92106
// References are still held in memory.
93-
expect(detector1.isLeaking()).toBe(true);
94-
expect(detector2.isLeaking()).toBe(true);
107+
isLeaking1 = await detector1.isLeaking();
108+
expect(isLeaking1).toBe(true);
109+
isLeaking2 = await detector2.isLeaking();
110+
expect(isLeaking2).toBe(true);
95111

96112
// We destroy the reference to ref1.
97113
ref1 = null;
98114

99115
// It will still be referenced by ref2, so both references are still leaking.
100-
expect(detector1.isLeaking()).toBe(true);
101-
expect(detector2.isLeaking()).toBe(true);
116+
isLeaking1 = await detector1.isLeaking();
117+
expect(isLeaking1).toBe(true);
118+
isLeaking2 = await detector2.isLeaking();
119+
expect(isLeaking2).toBe(true);
102120

103121
// We destroy the reference to ref2.
104122
ref2 = null;
105123

106124
// Now both references should be gone (yay mark & sweep!).
107-
expect(detector1.isLeaking()).toBe(false);
108-
expect(detector2.isLeaking()).toBe(false);
125+
isLeaking1 = await detector1.isLeaking();
126+
expect(isLeaking1).toBe(false);
127+
isLeaking2 = await detector2.isLeaking();
128+
expect(isLeaking2).toBe(false);
109129
});

packages/jest-leak-detector/src/index.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import {setFlagsFromString} from 'v8';
99
import {runInNewContext} from 'vm';
10+
import weak from 'weak-napi';
1011
import prettyFormat from 'pretty-format';
1112
import {isPrimitive} from 'jest-get-type';
1213

@@ -23,33 +24,19 @@ export default class {
2324
);
2425
}
2526

26-
let weak;
27-
28-
try {
29-
// eslint-disable-next-line import/no-extraneous-dependencies
30-
weak = require('weak');
31-
} catch (err) {
32-
if (!err || err.code !== 'MODULE_NOT_FOUND') {
33-
throw err;
34-
}
35-
36-
throw new Error(
37-
'The leaking detection mechanism requires the "weak" package to be installed and work. ' +
38-
'Please install it as a dependency on your main project',
39-
);
40-
}
41-
42-
weak(value, () => (this._isReferenceBeingHeld = false));
27+
weak(value as object, () => (this._isReferenceBeingHeld = false));
4328
this._isReferenceBeingHeld = true;
4429

4530
// Ensure value is not leaked by the closure created by the "weak" callback.
4631
value = null;
4732
}
4833

49-
isLeaking(): boolean {
34+
isLeaking(): Promise<boolean> {
5035
this._runGarbageCollector();
5136

52-
return this._isReferenceBeingHeld;
37+
return new Promise(resolve =>
38+
setImmediate(() => resolve(this._isReferenceBeingHeld)),
39+
);
5340
}
5441

5542
private _runGarbageCollector() {

packages/jest-runner/src/runTest.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,8 @@ export default async function runTest(
297297
);
298298

299299
if (leakDetector) {
300-
// We wanna allow a tiny but time to pass to allow last-minute cleanup
301-
await new Promise(resolve => setTimeout(resolve, 100));
302-
303300
// Resolve leak detector, outside the "runTestInternal" closure.
304-
result.leaks = leakDetector.isLeaking();
301+
result.leaks = await leakDetector.isLeaking();
305302
} else {
306303
result.leaks = false;
307304
}

yarn.lock

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2539,10 +2539,10 @@
25392539
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.5.tgz#9da44ed75571999b65c37b60c9b2b88db54c585d"
25402540
integrity sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==
25412541

2542-
"@types/weak@^1.0.0":
2542+
"@types/weak-napi@^1.0.0":
25432543
version "1.0.0"
2544-
resolved "https://registry.yarnpkg.com/@types/weak/-/weak-1.0.0.tgz#7b3bf891c4b53e2b8a144b7e41f4b0f6e78c2ba8"
2545-
integrity sha512-6WXZpeAac3vj5+OfQvlqYEtc88oOgvkcxbrnmBw53Da6gA+MGztL+Hns3BpnyUevgz+4DxsJblgAew1A/tkcng==
2544+
resolved "https://registry.yarnpkg.com/@types/weak-napi/-/weak-napi-1.0.0.tgz#b0977c0737cb62d028c4eda76f4e295bb3ae3c49"
2545+
integrity sha512-viW/kPA1gpeoNdUge025WqmThQ2lnnHzZWZJM5KlH8w9E5YehOh3GnDjW5w/sAEC91VOlePEiFSQmbnX7VVyLw==
25462546
dependencies:
25472547
"@types/node" "*"
25482548

@@ -3616,7 +3616,7 @@ binary-extensions@^2.0.0:
36163616
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
36173617
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
36183618

3619-
bindings@^1.2.1:
3619+
bindings@^1.3.0:
36203620
version "1.5.0"
36213621
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
36223622
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
@@ -6924,6 +6924,18 @@ get-stream@^4.0.0, get-stream@^4.1.0:
69246924
dependencies:
69256925
pump "^3.0.0"
69266926

6927+
get-symbol-from-current-process-h@^1.0.1:
6928+
version "1.0.1"
6929+
resolved "https://registry.yarnpkg.com/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.1.tgz#7e4809087e7d2f3a78a785b36f787e2183ba4c5d"
6930+
integrity sha512-QvP1+tCDjgTiu+akjdEYd8eK8MFYy6nRCRNjfiCeQB9RJEHQZpN+WE+CVqPRNqjIVMwSqd0WiD008B+b7iIdaA==
6931+
6932+
get-uv-event-loop-napi-h@^1.0.2:
6933+
version "1.0.5"
6934+
resolved "https://registry.yarnpkg.com/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.5.tgz#1904a1dc1fa6df7487c9e8eaf87302bcc9e33e47"
6935+
integrity sha512-uWDHId313vRTyqeLhlLWJS0CJOP8QXY5en/9Pv14dnPvAlRfKBfD6h2EDtoy7jxxOIWB9QgzYK16VCN3pCZOBg==
6936+
dependencies:
6937+
get-symbol-from-current-process-h "^1.0.1"
6938+
69276939
get-value@^2.0.3, get-value@^2.0.6:
69286940
version "2.0.6"
69296941
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -9850,7 +9862,7 @@ mute-stream@~0.0.4:
98509862
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
98519863
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
98529864

9853-
nan@^2.0.5, nan@^2.12.1:
9865+
nan@^2.12.1:
98549866
version "2.14.0"
98559867
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
98569868
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
@@ -9912,6 +9924,11 @@ nice-try@^1.0.4:
99129924
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
99139925
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
99149926

9927+
node-addon-api@^1.1.0:
9928+
version "1.6.3"
9929+
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.3.tgz#3998d4593e2dca2ea82114670a4eb003386a9fe1"
9930+
integrity sha512-FXWH6mqjWgU8ewuahp4spec8LkroFZK2NicOv6bNwZC3kcwZUI8LeZdG80UzTSLLhK4T7MsgNwlYDVRlDdfTDg==
9931+
99159932
node-environment-flags@1.0.5:
99169933
version "1.0.5"
99179934
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a"
@@ -12531,6 +12548,13 @@ set-value@^2.0.0, set-value@^2.0.1:
1253112548
is-plain-object "^2.0.3"
1253212549
split-string "^3.0.1"
1253312550

12551+
setimmediate-napi@^1.0.3:
12552+
version "1.0.3"
12553+
resolved "https://registry.yarnpkg.com/setimmediate-napi/-/setimmediate-napi-1.0.3.tgz#f5ef99da0d9b7a1036dd375b35a687cfb483c172"
12554+
integrity sha512-ah02BktAAJJ1eHANtD93ZdvKZrCXJwSHXww5arS1YcihOlpJlwsVkns4BXh6sRJNAyWTLl6TkjVx8CjKV9qwcQ==
12555+
dependencies:
12556+
get-uv-event-loop-napi-h "^1.0.2"
12557+
1253412558
setimmediate@^1.0.4, setimmediate@^1.0.5:
1253512559
version "1.0.5"
1253612560
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
@@ -14046,13 +14070,14 @@ wcwidth@^1.0.0:
1404614070
dependencies:
1404714071
defaults "^1.0.3"
1404814072

14049-
weak@^1.0.1:
14050-
version "1.0.1"
14051-
resolved "https://registry.yarnpkg.com/weak/-/weak-1.0.1.tgz#ab99aab30706959aa0200cb8cf545bb9cb33b99e"
14052-
integrity sha1-q5mqswcGlZqgIAy4z1RbucszuZ4=
14073+
weak-napi@^1.0.3:
14074+
version "1.0.3"
14075+
resolved "https://registry.yarnpkg.com/weak-napi/-/weak-napi-1.0.3.tgz#ff4dfa818db1c509ba4166530b42414ef74cbba6"
14076+
integrity sha512-cyqeMaYA5qI7RoZKAKvIHwEROEKDNxK7jXj3u56nF2rGBh+HFyhYmBb1/wAN4RqzRmkYKVVKQyqHpBoJjqtGUA==
1405314077
dependencies:
14054-
bindings "^1.2.1"
14055-
nan "^2.0.5"
14078+
bindings "^1.3.0"
14079+
node-addon-api "^1.1.0"
14080+
setimmediate-napi "^1.0.3"
1405614081

1405714082
webidl-conversions@^4.0.2:
1405814083
version "4.0.2"

0 commit comments

Comments
 (0)