Skip to content

Commit

Permalink
Implement async_hooks manually
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed May 4, 2018
1 parent b94f821 commit 9c57045
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 110 deletions.
2 changes: 1 addition & 1 deletion docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ Print debugging info about your Jest config.
Attempt to collect and print open handles preventing Jest from exiting cleanly.
Use this in cases where you need to use `--forceExit` in order for Jest to exit
to potentially track down the reason. Implemented using
[`why-is-node-running`](https://github.com/mafintosh/why-is-node-running), so it
[`async_hooks`](https://nodejs.org/api/async_hooks.html), so it
only works in Node 8 and newer.

### `--env=<environment>`
Expand Down
5 changes: 1 addition & 4 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -963,10 +963,7 @@ structure as the first argument and return it:
"numPassedTests": number,
"numFailedTests": number,
"numPendingTests": number,
"openHandles": Array<{
title: string,
entries: Array<{file: string, line: string}>,
}>,
"openHandles": Array<Error>,
"testResults": [{
"numFailingTests": number,
"numPassingTests": number,
Expand Down
3 changes: 0 additions & 3 deletions packages/jest-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@
"which": "^1.2.12",
"yargs": "^11.0.0"
},
"optionalDependencies": {
"why-is-node-running": "^2.0.2"
},
"bin": {
"jest": "./bin/jest.js"
},
Expand Down
19 changes: 2 additions & 17 deletions packages/jest-cli/src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import type {GlobalConfig, Path, ProjectConfig} from 'types/Config';
import {Console, clearLine, createDirectory} from 'jest-util';
import {validateCLIOptions} from 'jest-validate';
import {readConfig, deprecationEntries} from 'jest-config';
import {formatStackTrace} from 'jest-message-util';
import {version as VERSION} from '../../package.json';
import * as args from './args';
import chalk from 'chalk';
import createContext from '../lib/create_context';
import exit from 'exit';
import getChangedFilesPromise from '../get_changed_files_promise';
import {formatHandleErrors} from '../get_node_handles';
import fs from 'fs';
import handleDeprecationWarnings from '../lib/handle_deprecation_warnings';
import logDebugMessages from '../lib/log_debug_messages';
Expand Down Expand Up @@ -106,27 +106,12 @@ export const runCLI = async (
const {openHandles} = results;

if (openHandles && openHandles.length) {
const handles = openHandles
.map(({title, entries}) => ({
// Fake column to make it a valid stack trace
stack: entries.map(({file}) => `at ${file}:0`).join('\n'),
title,
}))
.map(
({title, stack}) =>
title +
'\n' +
// First config should be fine
formatStackTrace(stack, configs[0], {noStackTrace: false}),
)
.join('\n\n');

const openHandlesString = pluralize('open handle', openHandles.length, 's');

const message =
chalk.red(
`\nJest has detected the following ${openHandlesString} potentially keeping Jest from exiting:\n\n`,
) + handles;
) + formatHandleErrors(openHandles, configs[0]).join('\n\n');

console.error(message);
}
Expand Down
50 changes: 0 additions & 50 deletions packages/jest-cli/src/format_why_node_running.js

This file was deleted.

83 changes: 83 additions & 0 deletions packages/jest-cli/src/get_node_handles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {ProjectConfig} from 'types/Config';

// This works for nodes without the async_hooks as the require's lazy
// $FlowFixMe: Node core module
import asyncHooks from 'async_hooks';

import {
formatStackTrace,
separateMessageFromStack,
} from '../../jest-message-util/build';

// Inspired by https://github.com/mafintosh/why-is-node-running/blob/master/index.js
// Extracted as we want to format the result ourselves
export default function collectHandles(): () => Array<Error> {
const activeHandles: Map<string, Error> = new Map();

function initHook(asyncId, type) {
if (type === 'TIMERWRAP') return;
const error = new Error(type);

if (Error.captureStackTrace) {
Error.captureStackTrace(error, initHook);
}

if (error.stack.includes('Runtime.requireModule')) {
activeHandles.set(asyncId, error);
}
}

let hook;

try {
hook = asyncHooks.createHook({
destroy(asyncId) {
activeHandles.delete(asyncId);
},
init: initHook,
});

hook.enable();
} catch (e) {
const nodeMajor = Number(process.versions.node.split('.')[0]);
if (e.code === 'MODULE_NOT_FOUND' && nodeMajor < 8) {
throw new Error(
'You can only use --detectOpenHandles on Node 8 and newer.',
);
} else {
throw e;
}
}

return () => {
hook.disable();

const result = Array.from(activeHandles.values());

activeHandles.clear();

return result;
};
}

export function formatHandleErrors(
errors: Array<Error>,
config: ProjectConfig,
): Array<string> {
return errors.map(err => {
const {message, stack} = separateMessageFromStack(err.stack);

return (
message + '\n\n' + formatStackTrace(stack, config, {noStackTrace: false})
);
});
}
25 changes: 7 additions & 18 deletions packages/jest-cli/src/run_jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import TestSequencer from './test_sequencer';
import {makeEmptyAggregatedTestResult} from './test_result_helpers';
import FailedTestsCache from './failed_tests_cache';
import JestHooks, {type JestHookEmitter} from './jest_hooks';
import formatWhyRunning from './format_why_node_running';
import collectNodeHandles from './get_node_handles';

const setConfig = (contexts, newConfig) =>
contexts.forEach(
Expand Down Expand Up @@ -75,11 +75,11 @@ const processResults = (runResults, options) => {
onComplete,
outputStream,
testResultsProcessor,
whyRunning,
collectHandles,
} = options;

if (whyRunning) {
runResults.openHandles = formatWhyRunning(whyRunning);
if (collectHandles) {
runResults.openHandles = collectHandles();
} else {
runResults.openHandles = [];
}
Expand Down Expand Up @@ -254,21 +254,10 @@ export default (async function runJest({
// paths when printing.
setConfig(contexts, {cwd: process.cwd()});

let whyRunning;
let collectHandles;

if (globalConfig.detectOpenHandles) {
try {
whyRunning = require('why-is-node-running');
} catch (e) {
const nodeMajor = Number(process.versions.node.split('.')[0]);
if (e.code === 'MODULE_NOT_FOUND' && nodeMajor < 8) {
throw new Error(
'You can only use --detectOpenHandles on Node 8 and newer.',
);
} else {
throw e;
}
}
collectHandles = collectNodeHandles();
}

if (globalConfig.globalSetup) {
Expand Down Expand Up @@ -308,11 +297,11 @@ export default (async function runJest({
await globalTeardown();
}
return processResults(results, {
collectHandles,
isJSON: globalConfig.json,
onComplete,
outputFile: globalConfig.outputFile,
outputStream,
testResultsProcessor: globalConfig.testResultsProcessor,
whyRunning,
});
});
9 changes: 2 additions & 7 deletions types/TestResult.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export type AggregatedResultWithoutCoverage = {
numRuntimeErrorTestSuites: number,
numTotalTests: number,
numTotalTestSuites: number,
openHandles: Array<OpenHandle>,
openHandles: Array<Error>,
snapshot: SnapshotSummary,
startTime: number,
success: boolean,
Expand All @@ -140,11 +140,6 @@ export type Suite = {|
tests: Array<AssertionResult>,
|};

export type OpenHandle = {|
title: string,
entries: Array<{file: string, line: string}>,
|};

export type TestResult = {|
console: ?ConsoleBuffer,
coverage?: RawCoverage,
Expand All @@ -155,7 +150,7 @@ export type TestResult = {|
numFailingTests: number,
numPassingTests: number,
numPendingTests: number,
openHandles: Array<OpenHandle>,
openHandles: Array<Error>,
perfStats: {|
end: Milliseconds,
start: Milliseconds,
Expand Down
10 changes: 0 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8790,10 +8790,6 @@ stack-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.1.tgz#d4f33ab54e8e38778b0ca5cfd3b3afb12db68620"

stackback@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"

stacktrace-parser@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.4.tgz#01397922e5f62ecf30845522c95c4fe1d25e7d4e"
Expand Down Expand Up @@ -9673,12 +9669,6 @@ which@^1.2.1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0:
dependencies:
isexe "^2.0.0"

why-is-node-running@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.0.2.tgz#faf352f095356c8c37a28bf645f874e5648c8d02"
dependencies:
stackback "0.0.2"

wide-align@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
Expand Down

0 comments on commit 9c57045

Please sign in to comment.