collectHandles.js 7.2 KB

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