Skip to content

Commit

Permalink
Support deprecations for Ember debug helpers now requiring an options…
Browse files Browse the repository at this point in the history
… argument

Refs #2
  • Loading branch information
minichate committed Dec 14, 2015
1 parent db8529b commit 873db77
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 0 deletions.
1 change: 1 addition & 0 deletions .integration_jscsrc
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
"disallowInitializerArity": true,
"disallowPrivateRegistryProperty": true,
"disallowAppInstanceContainer": true,
"disallowDebugMissingArguments": true,
"disallowPrototypeExtension": true
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ None. Ember 2.0 removed support for the above deprecations.

- `disallowAppInstanceContainer` will warn you if you use the private `app.container.lookup()` depreprecation in an initializer. See http://emberjs.com/deprecations/v2.x/#toc_ember-applicationinstance-container for details.

- `disallowDebugMissingArguments` will warn you if you use one of the `Ember.warn()`, `Ember.deprecate()`, `Ember.deprecateFunc()` or `Ember.computed.deprecatingAlias()` function without passing in an options object that at a minimum contains an `id` property. See http://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options for details.

### Other Ember best practices

- `disallowPrototypeExtension` will warn you if you are using `.property()`, `.observes()` or `observesBefore()`. See http://guides.emberjs.com/v1.10.0/configuring-ember/disabling-prototype-extensions/#toc_functions for details.
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ module.exports = function(configuration) {
configuration.registerRule(require('./rules/disallow-initializerarity'));
configuration.registerRule(require('./rules/disallow-privateregistryproperty'));
configuration.registerRule(require('./rules/disallow-appinstancecontainer'));
configuration.registerRule(require('./rules/disallow-debugmissingarguments'));
};
109 changes: 109 additions & 0 deletions lib/rules/disallow-debugmissingarguments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
var assert = require('assert');
var EmberCoreHelpers = require('../helpers/ember-core');

module.exports = function() {};

module.exports.prototype.getOptionName = function() {
return 'disallowDebugMissingArguments';
};

module.exports.prototype.configure = function(options) {
assert(
options === true || options === false,
this.getOptionName() + ' option requires a boolean value or should be removed'
);

this._callers = {
'deprecateFunc': {args: 3, idpos: 1, name: 'deprecateFunc'},
'deprecate': {args: 3, idpos: 2, name: 'deprecate'},
'warn': {args: 3, idpos: 2, name: 'warn'},
'deprecatingAlias': {args: 2, idpos: 1, name: 'computed.deprecatingAlias'},
};
};

module.exports.prototype.check = function(file, errors) {
var _this = this;

var ember = new EmberCoreHelpers(file);

var processNode = function(node) {
var foundId = false;
var details = _this._callers[node.callee.property.name];

if (node.arguments.length < details.args) {
errors.add(
'Ember.' + details.name +
' with only ' + node.arguments.length + ' arguments is deprecated in Ember 2.1. ' +
'Ensure that the correct argument is an object containing an ' +
'`id` property',
node.loc.start
);
} else if (node.arguments[details.idpos].type === 'ObjectExpression') {
var options = node.arguments[details.idpos];

options.properties.forEach(function(slot) {
if (slot.key.name === 'id') {
foundId = true;
}
});

if (!foundId) {
errors.add(
'Ember.' + details.name +
' with only ' + node.arguments.length + ' arguments is deprecated in Ember 2.1. ' +
'Ensure that the correct argument is an object containing an ' +
'`id` property',
node.loc.start
);
}
} else if (node.arguments[details.idpos].type === 'Identifier') {
file.iterateNodesByType(['Identifier'], function(node) {
if (node.name !== 'options') {
return;
}

if (!node.parentNode.init) {
return;
}

node.parentNode.init.properties.forEach(function(property) {
if (property.key.name === 'id') {
foundId = true;
}
});

});

if (!foundId) {
errors.add(
'Ember.' + details.name +
' with only ' + node.arguments.length + ' arguments is deprecated in Ember 2.1. ' +
'Ensure that the correct argument is an object containing an ' +
'`id` property',
node.loc.start
);
}
}
};

['deprecateFunc', 'deprecate', 'warn'].forEach(function(key) {
ember.findEmberFunction(key).forEach(processNode);
});

file.iterateNodesByType(['CallExpression'], function(node) {
if (!node.callee.property || node.callee.property.name !== 'deprecatingAlias') {
return;
}

if (!node.callee.object || !node.callee.object.property) {
return;
}

if (node.callee.object.property.name !== 'computed') {
return;
}

processNode(node);
});

};
226 changes: 226 additions & 0 deletions test/lib/rules/disallow-debugmissingarguments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
describe('lib/rules/disallow-debugmissingarguments', function () {
var checker = global.checker({
plugins: ['./lib/index']
});

describe('not configured', function() {

it('should report with undefined', function() {
global.expect(function() {
checker.configure({disallowDebugMissingArguments: undefined});
}).to.throws(/requires a boolean value/i);
});

it('should report with an object', function() {
global.expect(function() {
checker.configure({disallowDebugMissingArguments: {}});
}).to.throws(/requires a boolean value/i);
});

});

describe('with true for deprecateFunc', function() {
checker.rules({disallowDebugMissingArguments: true});

checker.cases([
/* jshint ignore:start */
{
it: 'should not report',
code: function() {
Ember.deprecateFunc(
'Function#observesImmediately is deprecated. Use Function#observes instead',
{ id: 'ember-runtime.ext-function', until: '3.0.0' },
FunctionPrototype._observesImmediately
);
}
}, {
it: 'should not report other functions',
code: function() {
Ember.foobar(
'Function#observesImmediately is deprecated. Use Function#observes instead',
{ id: 'ember-runtime.ext-function', until: '3.0.0' },
FunctionPrototype._observesImmediately
);
}
}, {
it: 'should not report options declared previously',
code: function() {
var options = { id: 'ember-runtime.ext-function', until: '3.0.0' };
Ember.deprecateFunc(
'Function#observesImmediately is deprecated. Use Function#observes instead',
options,
FunctionPrototype._observesImmediately
);
}
}, {
it: 'should report deprecated use',
errors: 1,
code: function() {
Ember.deprecateFunc(
'Function#observesImmediately is deprecated. Use Function#observes instead',
FunctionPrototype._observesImmediately
);
}
}, {
it: 'should report options declared previously with no id key',
errors: 1,
code: function() {
var options = { until: '3.0.0' };
Ember.deprecateFunc(
'Function#observesImmediately is deprecated. Use Function#observes instead',
options,
FunctionPrototype._observesImmediately
);
}
}, {
it: 'should report options with no id key',
errors: 1,
code: function() {
Ember.deprecateFunc(
'Function#observesImmediately is deprecated. Use Function#observes instead',
{ until: '3.0.0' },
FunctionPrototype._observesImmediately
);
}
}
/* jshint ignore:end */
]);
});

describe('with true for deprecate', function() {
checker.rules({disallowDebugMissingArguments: true});

checker.cases([
/* jshint ignore:start */
{
it: 'should not report',
code: function() {
Ember.deprecate('Should not throw', false, { id: 'test', until: 'forever' });
}
}, {
it: 'should not report options with id key',
code: function() {
Ember.deprecate(
'Should not throw',
false,
{ until: '3.0.0', id: 'foobar' }
);
}
}, {
it: 'should report',
errors: 1,
code: function() {
Ember.deprecate('Should not throw', false);
}
}, {
it: 'should report options with no id key',
errors: 1,
code: function() {
Ember.deprecate(
'Should not throw',
false,
{ until: '3.0.0' }
);
}
}
/* jshint ignore:end */
]);
});

describe('with true for warn', function() {
checker.rules({disallowDebugMissingArguments: true});

checker.cases([
/* jshint ignore:start */
{
it: 'should not report',
code: function() {
Ember.warn('Should not throw', false, { id: 'test', until: 'forever' });
}
}, {
it: 'should not report Ember.Logger.warn',
code: function() {
Ember.Logger.warn('Should not throw');
}
}, {
it: 'should not report options with id key',
code: function() {
Ember.warn(
'Should not throw',
false,
{ until: '3.0.0', id: 'foobar' }
);
}
}, {
it: 'should report',
errors: 1,
code: function() {
Ember.warn('Should not throw', false);
}
}, {
it: 'should report options with no id key',
errors: 1,
code: function() {
Ember.warn(
'Should not throw',
false,
{ until: '3.0.0' }
);
}
}
/* jshint ignore:end */
]);
});

describe('with true for computed.deprecatingAlias', function() {
checker.rules({disallowDebugMissingArguments: true});

checker.cases([
/* jshint ignore:start */
{
it: 'should not report',
code: function() {
Ember.Object.extend({
bar: "baz",
foo: Ember.computed.deprecatingAlias('bar', { until: '3.0.0', id: 'foobar' })
});
}
}, {
it: 'should not report incorrect attachment',
code: function() {
Ember.Object.extend({
bar: "baz",
foo: Ember.foo.deprecatingAlias('bar', { until: '3.0.0', id: 'foobar' })
});
}
}, {
it: 'should not report incorrect attachment',
code: function() {
Ember.Object.extend({
bar: "baz",
foo: Ember.deprecatingAlias('bar', { until: '3.0.0', id: 'foobar' })
});
}
}, {
it: 'should report missing options',
errors: 1,
code: function() {
Ember.Object.extend({
bar: "baz",
foo: Ember.computed.deprecatingAlias('bar')
});
}
}, {
it: 'should report missing id attribute',
errors: 1,
code: function() {
Ember.Object.extend({
bar: "baz",
foo: Ember.computed.deprecatingAlias('bar', { until: '3.0.0' })
});
}
}
/* jshint ignore:end */
]);
});
});

0 comments on commit 873db77

Please sign in to comment.