Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGFIX beta] Refactor ember-debug to support better prod stripping. #15224

Merged
merged 1 commit into from
May 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 140 additions & 125 deletions packages/ember-debug/lib/deprecate.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*global __fail__*/
import { DEBUG } from 'ember-env-flags';

import EmberError from './error';
import Logger from 'ember-console';
Expand All @@ -7,6 +8,11 @@ import { ENV } from 'ember-environment';

import { registerHandler as genericRegisterHandler, invoke } from './handlers';

/**
@module ember
@submodule ember-debug
*/

/**
Allows for runtime registration of handler functions that override the default deprecation behavior.
Deprecations are invoked by calls to [Ember.deprecate](http://emberjs.com/api/classes/Ember.html#method_deprecate).
Expand Down Expand Up @@ -42,150 +48,159 @@ import { registerHandler as genericRegisterHandler, invoke } from './handlers';
@param handler {Function} A function to handle deprecation calls.
@since 2.1.0
*/
export function registerHandler(handler) {
genericRegisterHandler('deprecate', handler);
}
let registerHandler = () => {};
let missingOptionsDeprecation, missingOptionsIdDeprecation, missingOptionsUntilDeprecation, deprecate;

function formatMessage(_message, options) {
let message = _message;

if (options && options.id) {
message = message + ` [deprecation id: ${options.id}]`;
}

if (options && options.url) {
message += ` See ${options.url} for more details.`;
if (DEBUG) {
registerHandler = function registerHandler(handler) {
genericRegisterHandler('deprecate', handler);
}

return message;
}

registerHandler(function logDeprecationToConsole(message, options) {
let updatedMessage = formatMessage(message, options);
let formatMessage = function formatMessage(_message, options) {
let message = _message;

Logger.warn(`DEPRECATION: ${updatedMessage}`);
});
if (options && options.id) {
message = message + ` [deprecation id: ${options.id}]`;
}

let captureErrorForStack;
if (options && options.url) {
message += ` See ${options.url} for more details.`;
}

if (new Error().stack) {
captureErrorForStack = () => new Error();
} else {
captureErrorForStack = () => {
try { __fail__.fail(); } catch (e) { return e; }
};
}
return message;
}

registerHandler(function logDeprecationStackTrace(message, options, next) {
if (ENV.LOG_STACKTRACE_ON_DEPRECATION) {
let stackStr = '';
let error = captureErrorForStack();
let stack;

if (error.stack) {
if (error['arguments']) {
// Chrome
stack = error.stack.replace(/^\s+at\s+/gm, '').
replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2').
replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n');
stack.shift();
} else {
// Firefox
stack = error.stack.replace(/(?:\n@:0)?\s+$/m, '').
replace(/^\(/gm, '{anonymous}(').split('\n');
}
registerHandler(function logDeprecationToConsole(message, options) {
let updatedMessage = formatMessage(message, options);

stackStr = `\n ${stack.slice(2).join('\n ')}`;
}
Logger.warn(`DEPRECATION: ${updatedMessage}`);
});

let updatedMessage = formatMessage(message, options);
let captureErrorForStack;

Logger.warn(`DEPRECATION: ${updatedMessage}${stackStr}`);
if (new Error().stack) {
captureErrorForStack = () => new Error();
} else {
next(...arguments);
captureErrorForStack = () => {
try { __fail__.fail(); } catch (e) { return e; }
};
}
});

registerHandler(function raiseOnDeprecation(message, options, next) {
if (ENV.RAISE_ON_DEPRECATION) {
let updatedMessage = formatMessage(message);
registerHandler(function logDeprecationStackTrace(message, options, next) {
if (ENV.LOG_STACKTRACE_ON_DEPRECATION) {
let stackStr = '';
let error = captureErrorForStack();
let stack;

if (error.stack) {
if (error['arguments']) {
// Chrome
stack = error.stack.replace(/^\s+at\s+/gm, '').
replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2').
replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n');
stack.shift();
} else {
// Firefox
stack = error.stack.replace(/(?:\n@:0)?\s+$/m, '').
replace(/^\(/gm, '{anonymous}(').split('\n');
}

stackStr = `\n ${stack.slice(2).join('\n ')}`;
}

let updatedMessage = formatMessage(message, options);

throw new EmberError(updatedMessage);
} else {
next(...arguments);
}
});
Logger.warn(`DEPRECATION: ${updatedMessage}${stackStr}`);
} else {
next(...arguments);
}
});

export let missingOptionsDeprecation = 'When calling `Ember.deprecate` you ' +
'must provide an `options` hash as the third parameter. ' +
'`options` should include `id` and `until` properties.';
export let missingOptionsIdDeprecation = 'When calling `Ember.deprecate` you must provide `id` in options.';
export let missingOptionsUntilDeprecation = 'When calling `Ember.deprecate` you must provide `until` in options.';
registerHandler(function raiseOnDeprecation(message, options, next) {
if (ENV.RAISE_ON_DEPRECATION) {
let updatedMessage = formatMessage(message);

/**
@module ember
@submodule ember-debug
*/
throw new EmberError(updatedMessage);
} else {
next(...arguments);
}
});

/**
Display a deprecation warning with the provided message and a stack trace
(Chrome and Firefox only).

* In a production build, this method is defined as an empty function (NOP).
Uses of this method in Ember itself are stripped from the ember.prod.js build.

@method deprecate
@param {String} message A description of the deprecation.
@param {Boolean} test A boolean. If falsy, the deprecation will be displayed.
@param {Object} options
@param {String} options.id A unique id for this deprecation. The id can be
used by Ember debugging tools to change the behavior (raise, log or silence)
for that specific deprecation. The id should be namespaced by dots, e.g.
"view.helper.select".
@param {string} options.until The version of Ember when this deprecation
warning will be removed.
@param {String} [options.url] An optional url to the transition guide on the
emberjs.com website.
@for Ember
@public
@since 1.0.0
*/
export default function deprecate(message, test, options) {
if (!options || (!options.id && !options.until)) {
deprecate(
missingOptionsDeprecation,
false,
{
id: 'ember-debug.deprecate-options-missing',
until: '3.0.0',
url: 'http://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
}
);
}
missingOptionsDeprecation = 'When calling `Ember.deprecate` you ' +
'must provide an `options` hash as the third parameter. ' +
'`options` should include `id` and `until` properties.';
missingOptionsIdDeprecation = 'When calling `Ember.deprecate` you must provide `id` in options.';
missingOptionsUntilDeprecation = 'When calling `Ember.deprecate` you must provide `until` in options.';

/**
Display a deprecation warning with the provided message and a stack trace
(Chrome and Firefox only).

* In a production build, this method is defined as an empty function (NOP).
Uses of this method in Ember itself are stripped from the ember.prod.js build.

@method deprecate
@param {String} message A description of the deprecation.
@param {Boolean} test A boolean. If falsy, the deprecation will be displayed.
@param {Object} options
@param {String} options.id A unique id for this deprecation. The id can be
used by Ember debugging tools to change the behavior (raise, log or silence)
for that specific deprecation. The id should be namespaced by dots, e.g.
"view.helper.select".
@param {string} options.until The version of Ember when this deprecation
warning will be removed.
@param {String} [options.url] An optional url to the transition guide on the
emberjs.com website.
@for Ember
@public
@since 1.0.0
*/
deprecate = function deprecate(message, test, options) {
if (!options || (!options.id && !options.until)) {
deprecate(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this cause a stack overflow?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a similar thought, but no because we are passing options and options.id and options.until. If we remove the guarding if (and move those conditions to the deprecate's predicate) it would be a stack overflow.

missingOptionsDeprecation,
false,
{
id: 'ember-debug.deprecate-options-missing',
until: '3.0.0',
url: 'http://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
}
);
}

if (options && !options.id) {
deprecate(
missingOptionsIdDeprecation,
false,
{
id: 'ember-debug.deprecate-id-missing',
until: '3.0.0',
url: 'http://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
}
);
}
if (options && !options.id) {
deprecate(
missingOptionsIdDeprecation,
false,
{
id: 'ember-debug.deprecate-id-missing',
until: '3.0.0',
url: 'http://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
}
);
}

if (options && !options.until) {
deprecate(
missingOptionsUntilDeprecation,
options && options.until,
{
id: 'ember-debug.deprecate-until-missing',
until: '3.0.0',
url: 'http://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
}
);
if (options && !options.until) {
deprecate(
missingOptionsUntilDeprecation,
options && options.until,
{
id: 'ember-debug.deprecate-until-missing',
until: '3.0.0',
url: 'http://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
}
);
}

invoke('deprecate', ...arguments);
}
}

export default deprecate;

invoke('deprecate', ...arguments);
export {
registerHandler,
missingOptionsDeprecation,
missingOptionsIdDeprecation,
missingOptionsUntilDeprecation
}
34 changes: 23 additions & 11 deletions packages/ember-debug/lib/handlers.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import { DEBUG } from 'ember-env-flags';

export let HANDLERS = { };

export function registerHandler(type, callback) {
let nextHandler = HANDLERS[type] || (() => { });
let registerHandler = () => {};
let invoke = () => {};

HANDLERS[type] = (message, options) => {
callback(message, options, nextHandler);
};
}
if (DEBUG) {
registerHandler = function registerHandler(type, callback) {
let nextHandler = HANDLERS[type] || (() => { });

HANDLERS[type] = (message, options) => {
callback(message, options, nextHandler);
};
}

export function invoke(type, message, test, options) {
if (test) { return; }
invoke = function invoke(type, message, test, options) {
if (test) { return; }

let handlerForType = HANDLERS[type];
let handlerForType = HANDLERS[type];

if (handlerForType) {
handlerForType(message, options);
if (handlerForType) {
handlerForType(message, options);
}
}
}

export {
registerHandler,
invoke
}
Loading