atom-nuclide
Version:
A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.
125 lines (107 loc) • 4.65 kB
JavaScript
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.default = addPrepareStackTraceHook;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
var _commonsNodeSingleton2;
function _commonsNodeSingleton() {
return _commonsNodeSingleton2 = _interopRequireDefault(require('../../commons-node/singleton'));
}
var _commonsNodeString2;
function _commonsNodeString() {
return _commonsNodeString2 = require('../../commons-node/string');
}
var PREPARE_STACK_TRACE_HOOKED_KEY = '_nuclide_error_stack_trace_hooked';
var hookedPrepareStackTrace = undefined;
/**
* v8 provided a way to customize Error stacktrace generation by overwriting
* Error.prepareStackTrace (https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi).
* Here we added a hook to Error.prepareStackTrace to achieve following goals:
* 1) Whenever `error.stack` is called, error.stackTrace will be generated.
* 2) Other module's customization to Error.prepareStackTrace, no matter before or after the hook
* is added, will still work as expected.
* In this way, other module could still overwrite Error.prepareStackTrace to customize stacktrace.
* This is required as Atom's builtin coffeescript package need to show coffeescript stacktrace by
* customize Error.prepareStackTrace.
*/
function addPrepareStackTraceHook() {
(_commonsNodeSingleton2 || _commonsNodeSingleton()).default.get(PREPARE_STACK_TRACE_HOOKED_KEY, function () {
hookedPrepareStackTrace = createHookedPrepareStackTrace(Error.prepareStackTrace || defaultPrepareStackTrace);
// Hook Error.prepareStackTrace by leveraging get/set accessor. In this way, writing to
// Error.prepareStackTrace will put the new prepareStackTrace functions in a wrapper that
// calls the hook.
// $FlowIssue
Object.defineProperty(Error, 'prepareStackTrace', {
get: function get() {
return hookedPrepareStackTrace;
},
set: function set(newValue) {
hookedPrepareStackTrace = createHookedPrepareStackTrace(newValue || defaultPrepareStackTrace);
},
enumerable: false,
configurable: true
});
// TODO (chenshen) t8789330.
// Atom added getRawStack to Error.prototype to get Error's structured stacktrace
// (https://github.com/atom/grim/blob/master/src/grim.coffee#L43). However, this
// doesn't work well with our customization of stacktrace. So here we temporarily
// walk around this by following hack, until https://github.com/atom/atom/issues/9641
// get addressed.
/* $FlowFixMe */ // eslint-disable-next-line no-extend-native
Error.prototype.getRawStack = null;
return true;
});
}
/**
* Create a wrapper that calls to structuredStackTraceHook first, then return the result of
* prepareStackTrace.
*/
function createHookedPrepareStackTrace(prepareStackTrace) {
// If the prepareStackTrace is already been hooked, just return it.
if (prepareStackTrace.name === 'nuclideHookedPrepareStackTrace') {
return prepareStackTrace;
}
var hookedFunction = function nuclideHookedPrepareStackTrace(error, frames) {
structuredStackTraceHook(error, frames);
return prepareStackTrace(error, frames);
};
return hookedFunction;
}
function structuredStackTraceHook(error, frames) {
// $FlowFixMe
error.stackTrace = frames.map(function (frame) {
return {
functionName: frame.getFunctionName(),
methodName: frame.getMethodName(),
fileName: frame.getFileName(),
lineNumber: frame.getLineNumber(),
columnNumber: frame.getColumnNumber(),
evalOrigin: frame.getEvalOrigin(),
isTopLevel: frame.isToplevel(),
isEval: frame.isEval(),
isNative: frame.isNative(),
isConstructor: frame.isConstructor()
};
});
}
function defaultPrepareStackTrace(error, frames) {
var formattedStackTrace = error.message ? error.name + ': ' + error.message : '' + error.name;
frames.forEach(function (frame) {
formattedStackTrace += '\n at ' + (0, (_commonsNodeString2 || _commonsNodeString()).maybeToString)(frame.toString());
});
return formattedStackTrace;
}
var __test__ = {
createHookedPrepareStackTrace: createHookedPrepareStackTrace,
resetPrepareStackTraceHooked: function resetPrepareStackTraceHooked() {
(_commonsNodeSingleton2 || _commonsNodeSingleton()).default.clear(PREPARE_STACK_TRACE_HOOKED_KEY);
}
};
exports.__test__ = __test__;