UNPKG

7.68 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', {
4 value: true
5});
6exports.default = collectHandles;
7exports.formatHandleErrors = formatHandleErrors;
8function asyncHooks() {
9 const data = _interopRequireWildcard(require('async_hooks'));
10 asyncHooks = function () {
11 return data;
12 };
13 return data;
14}
15function _util() {
16 const data = require('util');
17 _util = function () {
18 return data;
19 };
20 return data;
21}
22function v8() {
23 const data = _interopRequireWildcard(require('v8'));
24 v8 = function () {
25 return data;
26 };
27 return data;
28}
29function vm() {
30 const data = _interopRequireWildcard(require('vm'));
31 vm = function () {
32 return data;
33 };
34 return data;
35}
36function _stripAnsi() {
37 const data = _interopRequireDefault(require('strip-ansi'));
38 _stripAnsi = function () {
39 return data;
40 };
41 return data;
42}
43function _jestMessageUtil() {
44 const data = require('jest-message-util');
45 _jestMessageUtil = function () {
46 return data;
47 };
48 return data;
49}
50function _jestUtil() {
51 const data = require('jest-util');
52 _jestUtil = function () {
53 return data;
54 };
55 return data;
56}
57function _interopRequireDefault(obj) {
58 return obj && obj.__esModule ? obj : {default: obj};
59}
60function _getRequireWildcardCache(nodeInterop) {
61 if (typeof WeakMap !== 'function') return null;
62 var cacheBabelInterop = new WeakMap();
63 var cacheNodeInterop = new WeakMap();
64 return (_getRequireWildcardCache = function (nodeInterop) {
65 return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
66 })(nodeInterop);
67}
68function _interopRequireWildcard(obj, nodeInterop) {
69 if (!nodeInterop && obj && obj.__esModule) {
70 return obj;
71 }
72 if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
73 return {default: obj};
74 }
75 var cache = _getRequireWildcardCache(nodeInterop);
76 if (cache && cache.has(obj)) {
77 return cache.get(obj);
78 }
79 var newObj = {};
80 var hasPropertyDescriptor =
81 Object.defineProperty && Object.getOwnPropertyDescriptor;
82 for (var key in obj) {
83 if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) {
84 var desc = hasPropertyDescriptor
85 ? Object.getOwnPropertyDescriptor(obj, key)
86 : null;
87 if (desc && (desc.get || desc.set)) {
88 Object.defineProperty(newObj, key, desc);
89 } else {
90 newObj[key] = obj[key];
91 }
92 }
93 }
94 newObj.default = obj;
95 if (cache) {
96 cache.set(obj, newObj);
97 }
98 return newObj;
99}
100/**
101 * Copyright (c) Meta Platforms, Inc. and affiliates.
102 *
103 * This source code is licensed under the MIT license found in the
104 * LICENSE file in the root directory of this source tree.
105 */
106
107/* eslint-disable local/ban-types-eventually */
108
109function stackIsFromUser(stack) {
110 // Either the test file, or something required by it
111 if (stack.includes('Runtime.requireModule')) {
112 return true;
113 }
114
115 // jest-jasmine it or describe call
116 if (stack.includes('asyncJestTest') || stack.includes('asyncJestLifecycle')) {
117 return true;
118 }
119
120 // An async function call from within circus
121 if (stack.includes('callAsyncCircusFn')) {
122 // jest-circus it or describe call
123 return (
124 stack.includes('_callCircusTest') || stack.includes('_callCircusHook')
125 );
126 }
127 return false;
128}
129const alwaysActive = () => true;
130
131// @ts-expect-error: doesn't exist in v12 typings
132const hasWeakRef = typeof WeakRef === 'function';
133const asyncSleep = (0, _util().promisify)(setTimeout);
134let gcFunc = globalThis.gc;
135function runGC() {
136 if (!gcFunc) {
137 v8().setFlagsFromString('--expose-gc');
138 gcFunc = vm().runInNewContext('gc');
139 v8().setFlagsFromString('--no-expose-gc');
140 if (!gcFunc) {
141 throw new Error(
142 'Cannot find `global.gc` function. Please run node with `--expose-gc` and report this issue in jest repo.'
143 );
144 }
145 }
146 gcFunc();
147}
148
149// Inspired by https://github.com/mafintosh/why-is-node-running/blob/master/index.js
150// Extracted as we want to format the result ourselves
151function collectHandles() {
152 const activeHandles = new Map();
153 const hook = asyncHooks().createHook({
154 destroy(asyncId) {
155 activeHandles.delete(asyncId);
156 },
157 init: function initHook(asyncId, type, triggerAsyncId, resource) {
158 // Skip resources that should not generally prevent the process from
159 // exiting, not last a meaningfully long time, or otherwise shouldn't be
160 // tracked.
161 if (
162 type === 'PROMISE' ||
163 type === 'TIMERWRAP' ||
164 type === 'ELDHISTOGRAM' ||
165 type === 'PerformanceObserver' ||
166 type === 'RANDOMBYTESREQUEST' ||
167 type === 'DNSCHANNEL' ||
168 type === 'ZLIB' ||
169 type === 'SIGNREQUEST'
170 ) {
171 return;
172 }
173 const error = new (_jestUtil().ErrorWithStack)(type, initHook, 100);
174 let fromUser = stackIsFromUser(error.stack || '');
175
176 // If the async resource was not directly created by user code, but was
177 // triggered by another async resource from user code, track it and use
178 // the original triggering resource's stack.
179 if (!fromUser) {
180 const triggeringHandle = activeHandles.get(triggerAsyncId);
181 if (triggeringHandle) {
182 fromUser = true;
183 error.stack = triggeringHandle.error.stack;
184 }
185 }
186 if (fromUser) {
187 let isActive;
188
189 // Handle that supports hasRef
190 if ('hasRef' in resource) {
191 if (hasWeakRef) {
192 // @ts-expect-error: doesn't exist in v12 typings
193 const ref = new WeakRef(resource);
194 isActive = () => {
195 return ref.deref()?.hasRef() ?? false;
196 };
197 } else {
198 isActive = resource.hasRef.bind(resource);
199 }
200 } else {
201 // Handle that doesn't support hasRef
202 isActive = alwaysActive;
203 }
204 activeHandles.set(asyncId, {
205 error,
206 isActive
207 });
208 }
209 }
210 });
211 hook.enable();
212 return async () => {
213 // Wait briefly for any async resources that have been queued for
214 // destruction to actually be destroyed.
215 // For example, Node.js TCP Servers are not destroyed until *after* their
216 // `close` callback runs. If someone finishes a test from the `close`
217 // callback, we will not yet have seen the resource be destroyed here.
218 await asyncSleep(100);
219 if (activeHandles.size > 0) {
220 // For some special objects such as `TLSWRAP`.
221 // Ref: https://github.com/jestjs/jest/issues/11665
222 runGC();
223 await asyncSleep(0);
224 }
225 hook.disable();
226
227 // Get errors for every async resource still referenced at this moment
228 const result = Array.from(activeHandles.values())
229 .filter(({isActive}) => isActive())
230 .map(({error}) => error);
231 activeHandles.clear();
232 return result;
233 };
234}
235function formatHandleErrors(errors, config) {
236 const stacks = new Set();
237 return (
238 errors
239 .map(err =>
240 (0, _jestMessageUtil().formatExecError)(
241 err,
242 config,
243 {
244 noStackTrace: false
245 },
246 undefined,
247 true
248 )
249 )
250 // E.g. timeouts might give multiple traces to the same line of code
251 // This hairy filtering tries to remove entries with duplicate stack traces
252 .filter(handle => {
253 const ansiFree = (0, _stripAnsi().default)(handle);
254 const match = ansiFree.match(/\s+at(.*)/);
255 if (!match || match.length < 2) {
256 return true;
257 }
258 const stack = ansiFree.substr(ansiFree.indexOf(match[1])).trim();
259 if (stacks.has(stack)) {
260 return false;
261 }
262 stacks.add(stack);
263 return true;
264 })
265 );
266}