diff --git a/lib/utils/ember.js b/lib/utils/ember.js index 8f140ac019..2276a70802 100644 --- a/lib/utils/ember.js +++ b/lib/utils/ember.js @@ -188,10 +188,20 @@ function isMirageConfig(fileName) { return path.normalize(fileName).endsWith(path.join('mirage', 'config.js')); } +// jQuery has an `extend` function and we want to avoid mistaking it for an extended object. +// TODO: ideally, this would check the actual name that jQuery is imported under, but there's a lot of plumbing needed for that. +const COMMON_JQUERY_NAMES = new Set(['$', 'jQuery']); + function isExtendObject(node) { + // Check for: + // * foo.extend(); + // * foo['extend'](); return ( - node.callee.property && - (node.callee.property.name === 'extend' || node.callee.property.value === 'extend') + node.type === 'CallExpression' && + node.callee.type === 'MemberExpression' && + ((node.callee.property.type === 'Identifier' && node.callee.property.name === 'extend') || + (node.callee.property.type === 'Literal' && node.callee.property.value === 'extend')) && + !(node.callee.object.type === 'Identifier' && COMMON_JQUERY_NAMES.has(node.callee.object.name)) ); } diff --git a/tests/lib/utils/ember-test.js b/tests/lib/utils/ember-test.js index a389049e0b..4f6e679a9d 100644 --- a/tests/lib/utils/ember-test.js +++ b/tests/lib/utils/ember-test.js @@ -556,6 +556,48 @@ describe('isEmberService', () => { }); }); +describe('isExtendObject', () => { + it('should detect using extend function name', () => { + const node = parse('foo.extend()'); + expect(emberUtils.isExtendObject(node)).toBeTruthy(); + }); + + it('should detect using extend string name', () => { + const node = parse('foo["extend"]()'); + expect(emberUtils.isExtendObject(node)).toBeTruthy(); + }); + + it('should detect using nested object', () => { + const node = parse('foo.bar.extend()'); + expect(emberUtils.isExtendObject(node)).toBeTruthy(); + }); + + it('should not detect a potential jQuery usage with `$`', () => { + const node = parse('$.extend()'); + expect(emberUtils.isExtendObject(node)).toBeFalsy(); + }); + + it('should not detect a potential jQuery usage with `jQuery`', () => { + const node = parse('jQuery.extend()'); + expect(emberUtils.isExtendObject(node)).toBeFalsy(); + }); + + it('should not detect with non-extend name', () => { + const node = parse('foo.notExtend()'); + expect(emberUtils.isExtendObject(node)).toBeFalsy(); + }); + + it('should not detect with no object', () => { + const node = parse('extend()'); + expect(emberUtils.isExtendObject(node)).toBeFalsy(); + }); + + it('should not detect with wrong function', () => { + const node = parse('extend.foo()'); + expect(emberUtils.isExtendObject(node)).toBeFalsy(); + }); +}); + describe('isEmberArrayProxy', () => { it('should detect using old module style', () => { const context = new FauxContext('Ember.ArrayProxy.extend()');