eslint-plugin-ember
Advanced tools
Comparing version 5.0.3 to 5.1.0
@@ -0,1 +1,24 @@ | ||
# Changelog | ||
## v5.1.0 (2018-03-11) | ||
#### :rocket: Enhancement | ||
* [#172](https://github.com/ember-cli/eslint-plugin-ember/pull/172) Add `--fix` support to `order-in-*` rules. ([@PrzemoRevolve](https://github.com/PrzemoRevolve)) | ||
#### :bug: Bug Fix | ||
* [#233](https://github.com/ember-cli/eslint-plugin-ember/pull/233) Fix init order in controllers and routes. ([@ro0gr](https://github.com/ro0gr)) | ||
* [#198](https://github.com/ember-cli/eslint-plugin-ember/pull/198) Add new scenarios for `require-super-in-init` rule. ([@clcuevas](https://github.com/clcuevas)) | ||
* [#205](https://github.com/ember-cli/eslint-plugin-ember/pull/205) add willInsertElement component lifecycle hook. ([@hakubo](https://github.com/hakubo)) | ||
#### Committers: 8 | ||
- Claudia Cuevas ([clcuevas](https://github.com/clcuevas)) | ||
- Jakub Olek ([hakubo](https://github.com/hakubo)) | ||
- Jason Williams ([jaswilli](https://github.com/jaswilli)) | ||
- Przemysław Nowak ([PrzemoRevolve](https://github.com/PrzemoRevolve)) | ||
- Ricardo Mendes ([locks](https://github.com/locks)) | ||
- Ruslan Grabovoy ([ro0gr](https://github.com/ro0gr)) | ||
- Sylvain MINA ([sly7-7](https://github.com/sly7-7)) | ||
- [verim1990](https://github.com/verim1990) | ||
## v5.0.3 (2017-12-21) | ||
@@ -2,0 +25,0 @@ |
@@ -5,3 +5,3 @@ ## Do not use Ember's `function` prototype extensions | ||
Use computed property syntax, observer syntax or module hooks instead of `.property()`, `.observe()` or `.on()` in Ember modules. | ||
Use computed property syntax, observer syntax or module hooks instead of `.property()`, `.observes()` or `.on()` in Ember modules. | ||
@@ -12,3 +12,3 @@ ```javascript | ||
abc: function() { /* custom logic */ }.property('xyz'), | ||
def: function() { /* custom logic */ }.observe('xyz'), | ||
def: function() { /* custom logic */ }.observes('xyz'), | ||
ghi: function() { /* custom logic */ }.on('didInsertElement'), | ||
@@ -15,0 +15,0 @@ |
@@ -16,8 +16,12 @@ ## Don't introduce side-effects in computed properties | ||
export default Component.extend({ | ||
users: [ | ||
{ name: 'Foo', age: 15 }, | ||
{ name: 'Bar', age: 16 }, | ||
{ name: 'Baz', age: 15 } | ||
], | ||
init() { | ||
this.users = [ | ||
{ name: 'Foo', age: 15 }, | ||
{ name: 'Bar', age: 16 }, | ||
{ name: 'Baz', age: 15 } | ||
]; | ||
this._super(...arguments); | ||
}, | ||
// GOOD: | ||
@@ -24,0 +28,0 @@ fifteen: filterBy('users', 'age', 15), |
@@ -19,2 +19,3 @@ ## Organize your components | ||
'willRender', | ||
'willInsertElement', | ||
'didInsertElement', | ||
@@ -45,2 +46,3 @@ 'didRender', | ||
'willRender', | ||
'willInsertElement', | ||
'didInsertElement', | ||
@@ -47,0 +49,0 @@ 'didRender', |
@@ -27,3 +27,3 @@ ## Use `Ember.get` and `Ember.set` | ||
```javascript | ||
// Bad | ||
// Not recommended | ||
this.get('fooProperty'); | ||
@@ -36,5 +36,4 @@ this.set('fooProperty', 'bar'); | ||
// Good | ||
const { | ||
// Recommended | ||
import { | ||
get, | ||
@@ -45,4 +44,6 @@ set, | ||
setProperties | ||
} = Ember; | ||
} from '@ember/object'; | ||
// ... | ||
get(this, 'fooProperty'); | ||
@@ -49,0 +50,0 @@ set(this, 'fooProperty', 'bar'); |
@@ -22,3 +22,3 @@ 'use strict'; | ||
const functionPrototypeExtensionNames = ['property', 'observe', 'on']; | ||
const functionPrototypeExtensionNames = ['property', 'observes', 'on']; | ||
@@ -25,0 +25,0 @@ const isFunctionPrototypeExtension = function (property) { |
@@ -40,2 +40,9 @@ 'use strict'; | ||
if ( | ||
utils.isIdentifier(fnCallee) && | ||
fnCallee.name === 'set') { | ||
report(fnExpression); | ||
return; | ||
} | ||
if ( | ||
utils.isMemberExpression(fnCallee) && | ||
@@ -42,0 +49,0 @@ utils.isThisExpression(fnCallee.object) && |
@@ -19,2 +19,3 @@ 'use strict'; | ||
'willRender', | ||
'willInsertElement', | ||
'didInsertElement', | ||
@@ -43,3 +44,3 @@ 'didRender', | ||
}, | ||
fixable: null // or "code" or "whitespace" | ||
fixable: 'code' // or "code" or "whitespace" | ||
}, | ||
@@ -58,2 +59,3 @@ | ||
'willRender', | ||
'willInsertElement', | ||
'didInsertElement', | ||
@@ -60,0 +62,0 @@ 'didRender', |
@@ -15,2 +15,3 @@ 'use strict'; | ||
'property', | ||
'init', | ||
'single-line-function', | ||
@@ -34,3 +35,3 @@ 'multi-line-function', | ||
}, | ||
fixable: null, // or "code" or "whitespace" | ||
fixable: 'code', // or "code" or "whitespace" | ||
}, | ||
@@ -37,0 +38,0 @@ |
@@ -28,3 +28,3 @@ 'use strict'; | ||
}, | ||
fixable: null, // or "code" or "whitespace" | ||
fixable: 'code', // or "code" or "whitespace" | ||
}, | ||
@@ -31,0 +31,0 @@ |
@@ -13,2 +13,3 @@ 'use strict'; | ||
'property', | ||
'init', | ||
'single-line-function', | ||
@@ -41,3 +42,3 @@ 'multi-line-function', | ||
}, | ||
fixable: null // or "code" or "whitespace" | ||
fixable: 'code' // or "code" or "whitespace" | ||
}, | ||
@@ -44,0 +45,0 @@ |
@@ -6,2 +6,54 @@ 'use strict'; | ||
/** | ||
* Locates nodes with either an ExpressionStatement or ReturnStatement | ||
* given name. | ||
* @param {Array} nodeBody Array of nodes. | ||
* @returns {Array} Array of nodes with given names. | ||
*/ | ||
function findStmtNodes(nodeBody) { | ||
const nodes = []; | ||
const fnExpressions = utils.findNodes(nodeBody, 'ExpressionStatement'); | ||
const returnStatement = utils.findNodes(nodeBody, 'ReturnStatement'); | ||
if (fnExpressions.length !== 0) { | ||
fnExpressions.forEach((item) => { | ||
nodes.push(item); | ||
}); | ||
} | ||
if (returnStatement.length !== 0) { | ||
returnStatement.forEach((item) => { | ||
nodes.push(item); | ||
}); | ||
} | ||
return nodes; | ||
} | ||
/** | ||
* Checks whether a node has the '_super' property. | ||
* @param {Array} nodes An array of nodes. | ||
* @returns {Boolean} | ||
*/ | ||
function checkForSuper(nodes) { | ||
if (nodes.length === 0) return false; | ||
return nodes.some((n) => { | ||
if (utils.isCallExpression(n.expression)) { | ||
const fnCallee = n.expression.callee; | ||
return utils.isMemberExpression(fnCallee) && | ||
utils.isThisExpression(fnCallee.object) && | ||
utils.isIdentifier(fnCallee.property) && | ||
fnCallee.property.name === '_super'; | ||
} else if (utils.isReturnStatement(n)) { | ||
if (!n.argument || !utils.isCallExpression(n.argument)) return false; | ||
const fnCallee = n.argument.callee; | ||
return fnCallee.property.name === '_super'; | ||
} | ||
return false; | ||
}); | ||
} | ||
//---------------------------------------------- | ||
@@ -35,3 +87,4 @@ // General rule - Call _super in init lifecycle hooks | ||
!ember.isEmberRoute(node, filePath) && | ||
!ember.isEmberMixin(node, filePath)) return; | ||
!ember.isEmberMixin(node, filePath) && | ||
!ember.isEmberService(node, filePath)) return; | ||
@@ -42,13 +95,4 @@ const initProperty = ember.getModuleProperties(node).find(property => property.key.name === 'init'); | ||
const initPropertyBody = initProperty.value.body.body; | ||
const fnExpressions = utils.findNodes(initPropertyBody, 'ExpressionStatement'); | ||
const hasSuper = fnExpressions.some((fnExpression) => { | ||
if (!utils.isCallExpression(fnExpression.expression)) return false; | ||
const fnCallee = fnExpression.expression.callee; | ||
return utils.isMemberExpression(fnCallee) && | ||
utils.isThisExpression(fnCallee.object) && | ||
utils.isIdentifier(fnCallee.property) && | ||
fnCallee.property.name === '_super'; | ||
}); | ||
const nodes = findStmtNodes(initPropertyBody); | ||
const hasSuper = checkForSuper(nodes); | ||
if (!hasSuper) { report(initProperty); } | ||
@@ -55,0 +99,0 @@ } |
@@ -13,4 +13,5 @@ 'use strict'; | ||
isEmberController, | ||
isEmberMixin, | ||
isEmberRoute, | ||
isEmberMixin, | ||
isEmberService, | ||
@@ -36,12 +37,7 @@ isSingleLineFn, | ||
isComponentLifecycleHookName, | ||
isComponentLifecycleHook, | ||
isComponentCustomFunction, | ||
isRoute, | ||
isRouteCustomFunction, | ||
isRouteProperty, | ||
isRouteDefaultProp, | ||
isControllerProperty, | ||
isControllerDefaultProp, | ||
@@ -148,7 +144,12 @@ parseDependentKeys, | ||
function isEmberMixin(node, filePath) { | ||
return isEmberCoreModule(node, 'Mixin', filePath); | ||
} | ||
function isEmberRoute(node, filePath) { | ||
return isEmberCoreModule(node, 'Route', filePath); | ||
} | ||
function isEmberMixin(node, filePath) { | ||
return isEmberCoreModule(node, 'Mixin', filePath); | ||
function isEmberService(node, filePath) { | ||
return isEmberCoreModule(node, 'Service', filePath); | ||
} | ||
@@ -219,5 +220,5 @@ | ||
return [ | ||
'init', | ||
'didReceiveAttrs', | ||
'willRender', | ||
'willInsertElement', | ||
'didInsertElement', | ||
@@ -239,7 +240,2 @@ 'didRender', | ||
function isComponentCustomFunction(property) { | ||
return isFunctionExpression(property.value) && | ||
!isComponentLifecycleHookName(property.key.name); | ||
} | ||
function isRoute(node) { | ||
@@ -267,10 +263,4 @@ return utils.isMemberExpression(node.callee) && | ||
function isRouteCustomFunction(property) { | ||
return isFunctionExpression(property.value) && | ||
!isRouteLifecycleHookName(property.key.name); | ||
} | ||
function isRouteProperty(name) { | ||
return [ | ||
'init', | ||
'actions', | ||
@@ -296,3 +286,2 @@ 'concatenatedProperties', | ||
return [ | ||
'init', | ||
'actions', | ||
@@ -299,0 +288,0 @@ 'concatenatedProperties', |
@@ -46,2 +46,3 @@ 'use strict'; | ||
willDestroyElement: 'lifecycle hook', | ||
willInsertElement: 'lifecycle hook', | ||
willRender: 'lifecycle hook', | ||
@@ -60,2 +61,6 @@ willUpdate: 'lifecycle hook' | ||
if (node.key.name === 'init') { | ||
return 'init'; | ||
} | ||
if (parentType === 'component') { | ||
@@ -189,4 +194,56 @@ if (ember.isComponentLifecycleHook(node)) { | ||
context.report(property, | ||
`The ${typeName} should be above the ${nextTypeName} on line ${nextSourceLine}`); | ||
context.report({ | ||
node: property, | ||
message: `The ${typeName} should be above the ${nextTypeName} on line ${nextSourceLine}`, | ||
fix: (fixer) => { | ||
// for capturing the moved property and EOL character to insert ',' in between | ||
const propertyWithEOL = /(.+)(\s+)$/; | ||
const sourceCode = context.getSourceCode(); | ||
const foundProperty = firstPropertyOfNextType.node; | ||
let nextToken = property; | ||
let optionalComma = ''; | ||
let isLastProperty = false; | ||
// including EOL character(s) | ||
do { | ||
const previousToken = nextToken; | ||
nextToken = sourceCode.getTokenAfter(nextToken, { includeComments: true }); | ||
if (!nextToken) { | ||
nextToken = previousToken; | ||
} | ||
// adding a trailing comma when it's the last property defined | ||
if (nextToken.value === '}') { | ||
isLastProperty = true; | ||
if (previousToken.value !== ',') { | ||
optionalComma = ','; | ||
} | ||
} | ||
} while (nextToken.value === ','); | ||
// additional whitespace is needed only when it's the last property | ||
const whitespaceCount = isLastProperty ? property.loc.start.column : 0; | ||
const propertyOffset = getCommentOffsetBefore(property, sourceCode); | ||
const foundPropertyOffset = getCommentOffsetBefore(foundProperty, sourceCode); | ||
const offset = nextToken.start - property.end; | ||
const textBetween = property.start - propertyOffset - foundProperty.end - whitespaceCount; | ||
const replaceTextRange = [foundProperty.start - foundPropertyOffset, nextToken.start]; | ||
const movedProperty = sourceCode.getText(property, propertyOffset, offset); | ||
const restText = sourceCode.getText(foundProperty, foundPropertyOffset, textBetween); | ||
// adding the optional comma between the property and newline | ||
const replaceWithComma = `$1${optionalComma}$2`; | ||
const movedPropertyWithComma = movedProperty.replace(propertyWithEOL, replaceWithComma); | ||
const optionalWhitespace = (new Array(whitespaceCount + 1)).join(' '); | ||
const outputText = movedPropertyWithComma + optionalWhitespace + restText; | ||
return fixer.replaceTextRange(replaceTextRange, outputText); | ||
} | ||
}); | ||
} else { | ||
@@ -202,2 +259,19 @@ maxOrder = order; | ||
function getCommentOffsetBefore(property, sourceCode) { | ||
const commentBlockRegExp = /^\/\*(.|\s)*\*\/$/m; | ||
const commentLineRegExp = /^\/\/.*$/; | ||
const previousToken = sourceCode.getTokenBefore(property, { includeComments: true }); | ||
const previousTokenText = sourceCode.getText(previousToken); | ||
// including comments above the moved property | ||
const isLineComment = previousToken.type === 'Line' && commentLineRegExp.test(previousTokenText); | ||
const isBlockComment = previousToken.type === 'Block' && commentBlockRegExp.test(previousTokenText); | ||
if (isLineComment || isBlockComment) { | ||
return property.start - previousToken.start; | ||
} | ||
return 0; | ||
} | ||
function addBackwardsPosition(order, newPosition, targetPosition) { | ||
@@ -204,0 +278,0 @@ const positionOrder = order.slice(); |
@@ -31,2 +31,3 @@ 'use strict'; | ||
isBinaryExpression, | ||
isReturnStatement | ||
}; | ||
@@ -201,2 +202,12 @@ | ||
/** | ||
* Check whether or not a node is a ReturnStatement | ||
* | ||
* @param {Object} node The node to check. | ||
* @return {Boolean} Whether or not the node is a ReturnStatement. | ||
*/ | ||
function isReturnStatement(node) { | ||
return node !== undefined && node.type && node.type === 'ReturnStatement'; | ||
} | ||
/** | ||
* Check whether or not a node is an ThisExpression. | ||
@@ -203,0 +214,0 @@ * |
{ | ||
"name": "eslint-plugin-ember", | ||
"version": "5.0.3", | ||
"version": "5.1.0", | ||
"description": "Eslint plugin for Ember.js apps", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -124,6 +124,6 @@ # eslint-plugin-ember | ||
|:---|:--------|:------------| | ||
| | [order-in-components](./docs/rules/order-in-components.md) | Enforces proper order of properties in components | | ||
| | [order-in-controllers](./docs/rules/order-in-controllers.md) | Enforces proper order of properties in controllers | | ||
| | [order-in-models](./docs/rules/order-in-models.md) | Enforces proper order of properties in models | | ||
| | [order-in-routes](./docs/rules/order-in-routes.md) | Enforces proper order of properties in routes | | ||
| :wrench: | [order-in-components](./docs/rules/order-in-components.md) | Enforces proper order of properties in components | | ||
| :wrench: | [order-in-controllers](./docs/rules/order-in-controllers.md) | Enforces proper order of properties in controllers | | ||
| :wrench: | [order-in-models](./docs/rules/order-in-models.md) | Enforces proper order of properties in models | | ||
| :wrench: | [order-in-routes](./docs/rules/order-in-routes.md) | Enforces proper order of properties in routes | | ||
| :white_check_mark: | [use-brace-expansion](./docs/rules/use-brace-expansion.md) | Enforces usage of brace expansion | | ||
@@ -130,0 +130,0 @@ |
@@ -85,3 +85,3 @@ // ------------------------------------------------------------------------------ | ||
{ | ||
code: 'export default Controller.extend({test: function() {}.observe("abc")});', | ||
code: 'export default Controller.extend({test: function() {}.observes("abc")});', | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
@@ -88,0 +88,0 @@ errors: [{ |
@@ -27,4 +27,10 @@ // ------------------------------------------------------------------------------ | ||
}], | ||
}, { | ||
code: 'prop: computed("test", function() {set(this, "testAmount", test.length); return "";})', | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'Don\'t introduce side-effects in computed properties', | ||
}], | ||
}, | ||
], | ||
}); |
@@ -174,2 +174,4 @@ // ------------------------------------------------------------------------------ | ||
}, | ||
willInsertElement() { | ||
}, | ||
didInsertElement() { | ||
@@ -754,4 +756,27 @@ }, | ||
}] | ||
}, | ||
{ | ||
code: `export default Component.extend({ | ||
levelOfHappiness: computed("attitude", "health", () => { | ||
}), | ||
vehicle: alias("car"), | ||
actions: {} | ||
});`, | ||
output: `export default Component.extend({ | ||
vehicle: alias("car"), | ||
levelOfHappiness: computed("attitude", "health", () => { | ||
}), | ||
actions: {} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "vehicle" single-line function should be above the "levelOfHappiness" multi-line function on line 2', | ||
line: 5 | ||
}] | ||
} | ||
] | ||
}); |
@@ -112,2 +112,3 @@ // ------------------------------------------------------------------------------ | ||
foo: service(), | ||
someProp: null, | ||
init() { | ||
@@ -303,3 +304,3 @@ this._super(...arguments); | ||
errors: [{ | ||
message: 'The inherited "init" property should be above the actions hash on line 4', | ||
message: 'The "init" lifecycle hook should be above the actions hash on line 4', | ||
line: 7 | ||
@@ -320,3 +321,3 @@ }] | ||
errors: [{ | ||
message: 'The inherited "init" property should be above the "customFoo" empty method on line 4', | ||
message: 'The "init" lifecycle hook should be above the "customFoo" empty method on line 4', | ||
line: 5 | ||
@@ -336,7 +337,57 @@ }] | ||
errors: [{ | ||
message: 'The "foo" service injection should be above the inherited "init" property on line 3', | ||
message: 'The "foo" service injection should be above the "init" lifecycle hook on line 3', | ||
line: 6 | ||
}] | ||
}, | ||
{ | ||
code: ` | ||
export default Controller.extend({ | ||
init() { | ||
this._super(...arguments); | ||
}, | ||
someProp: null | ||
}); | ||
`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "someProp" property should be above the "init" lifecycle hook on line 3', | ||
line: 6 | ||
}] | ||
}, | ||
{ | ||
code: | ||
// whitespace is preserved inside `` and it's breaking the test | ||
`export default Controller.extend({ | ||
queryParams: [], | ||
currentUser: service(), | ||
});`, | ||
output: | ||
`export default Controller.extend({ | ||
currentUser: service(), | ||
queryParams: [], | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "currentUser" service injection should be above the "queryParams" property on line 2', | ||
line: 3, | ||
}], | ||
}, | ||
{ | ||
code: `export default Controller.extend({ | ||
test: "asd", | ||
queryParams: [], | ||
actions: {} | ||
});`, | ||
output: `export default Controller.extend({ | ||
queryParams: [], | ||
test: "asd", | ||
actions: {} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "queryParams" property should be above the "test" property on line 2', | ||
line: 3, | ||
}], | ||
} | ||
], | ||
}); |
@@ -86,3 +86,3 @@ // ------------------------------------------------------------------------------ | ||
b: belongsTo("c", { async: false }), | ||
convertA(paramA) { | ||
convertA(paramA) { | ||
} | ||
@@ -94,3 +94,3 @@ });`, | ||
code: `export default DS.Model.extend({ | ||
convertA(paramA) { | ||
convertA(paramA) { | ||
}, | ||
@@ -220,4 +220,22 @@ a: attr("string"), | ||
}], | ||
}, | ||
}, { | ||
code: `export default Model.extend({ | ||
behaviors: hasMany("behaviour"), | ||
shape: attr("string"), | ||
mood: computed("health", "hunger", function() { | ||
}) | ||
});`, | ||
output: `export default Model.extend({ | ||
shape: attr("string"), | ||
behaviors: hasMany("behaviour"), | ||
mood: computed("health", "hunger", function() { | ||
}) | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "shape" attribute should be above the "behaviors" relationship on line 2', | ||
line: 3, | ||
}], | ||
} | ||
], | ||
}); |
@@ -401,3 +401,3 @@ // ------------------------------------------------------------------------------ | ||
errors: [{ | ||
message: 'The inherited "init" property should be above the actions hash on line 4', | ||
message: 'The "init" lifecycle hook should be above the actions hash on line 4', | ||
line: 5 | ||
@@ -418,3 +418,3 @@ }] | ||
errors: [{ | ||
message: 'The inherited "init" property should be above the "customFoo" empty method on line 4', | ||
message: 'The "init" lifecycle hook should be above the "customFoo" empty method on line 4', | ||
line: 5 | ||
@@ -434,7 +434,189 @@ }] | ||
errors: [{ | ||
message: 'The "foo" service injection should be above the inherited "init" property on line 3', | ||
message: 'The "foo" service injection should be above the "init" lifecycle hook on line 3', | ||
line: 6 | ||
}] | ||
}, | ||
{ | ||
code: ` | ||
export default Route.extend({ | ||
init() { | ||
this._super(...arguments); | ||
}, | ||
someProp: null | ||
}); | ||
`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "someProp" property should be above the "init" lifecycle hook on line 3', | ||
line: 6 | ||
}] | ||
}, | ||
{ | ||
code: `export default Route.extend({ | ||
queryParams: {}, | ||
currentUser: service(), | ||
customProp: "test", | ||
beforeModel() {}, | ||
model() {}, | ||
vehicle: alias("car"), | ||
actions: {}, | ||
_customAction() {} | ||
});`, | ||
output: `export default Route.extend({ | ||
currentUser: service(), | ||
queryParams: {}, | ||
customProp: "test", | ||
vehicle: alias("car"), | ||
beforeModel() {}, | ||
model() {}, | ||
actions: {}, | ||
_customAction() {} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "currentUser" service injection should be above the inherited "queryParams" property on line 2', | ||
line: 3 | ||
}, { | ||
message: 'The "vehicle" single-line function should be above the "beforeModel" lifecycle hook on line 5', | ||
line: 7 | ||
}] | ||
}, | ||
{ | ||
code: `export default Route.extend({ | ||
customProp: "test", | ||
// queryParams line comment | ||
queryParams: {}, | ||
model() {}, | ||
/** | ||
* actions block comment | ||
*/ | ||
actions: {}, | ||
_customAction() {} | ||
});`, | ||
output: `export default Route.extend({ | ||
// queryParams line comment | ||
queryParams: {}, | ||
customProp: "test", | ||
model() {}, | ||
/** | ||
* actions block comment | ||
*/ | ||
actions: {}, | ||
_customAction() {} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The inherited "queryParams" property should be above the "customProp" property on line 2', | ||
line: 4 | ||
}] | ||
}, | ||
{ | ||
code: `export default Route.extend({ | ||
customProp: "test", | ||
model() {}, | ||
/** | ||
* beforeModel block comment | ||
*/ | ||
beforeModel() {}, | ||
/** | ||
* actions block comment | ||
*/ | ||
actions: {}, | ||
_customAction() {} | ||
});`, | ||
output: `export default Route.extend({ | ||
customProp: "test", | ||
/** | ||
* beforeModel block comment | ||
*/ | ||
beforeModel() {}, | ||
model() {}, | ||
/** | ||
* actions block comment | ||
*/ | ||
actions: {}, | ||
_customAction() {} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "beforeModel" lifecycle hook should be above the "model" hook on line 3', | ||
line: 7 | ||
}] | ||
}, | ||
{ | ||
code: | ||
// whitespace is preserved inside `` and it's breaking the test | ||
`export default Route.extend({ | ||
customProp: "test", | ||
/** | ||
* actions block comment | ||
*/ | ||
actions: {}, | ||
/** | ||
* beforeModel block comment | ||
*/ | ||
beforeModel() {} | ||
});`, | ||
output: | ||
`export default Route.extend({ | ||
customProp: "test", | ||
/** | ||
* beforeModel block comment | ||
*/ | ||
beforeModel() {}, | ||
/** | ||
* actions block comment | ||
*/ | ||
actions: {}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "beforeModel" lifecycle hook should be above the actions hash on line 6', | ||
line: 10 | ||
}] | ||
}, | ||
{ | ||
code: | ||
// whitespace is preserved inside `` and it's breaking the test | ||
`export default Route.extend({ | ||
model() {}, | ||
test: "asd" | ||
});`, | ||
output: | ||
`export default Route.extend({ | ||
test: "asd", | ||
model() {}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "test" property should be above the "model" hook on line 2', | ||
line: 3 | ||
}] | ||
}, | ||
{ | ||
code: `export default Route.extend({ | ||
model() {}, | ||
test: "asd", | ||
actions: {} | ||
});`, | ||
output: `export default Route.extend({ | ||
test: "asd", | ||
model() {}, | ||
actions: {} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'The "test" property should be above the "model" hook on line 3', | ||
line: 5 | ||
}] | ||
} | ||
] | ||
}); |
@@ -18,2 +18,82 @@ // ------------------------------------------------------------------------------ | ||
{ | ||
code: `export default Component.extend({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: `export default Route.extend({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: `export default Controller.extend({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: `export default Mixin.extend({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: `export default Service.extend({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: `export default Component({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: `export default Route({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: `export default Controller({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: `export default Mixin({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: `export default Service({ | ||
init() { | ||
return this._super(...arguments); | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' } | ||
}, | ||
{ | ||
code: 'export default Component.extend();', | ||
@@ -35,2 +115,6 @@ parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
{ | ||
code: 'export default Service.extend();', | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
}, | ||
{ | ||
code: `export default Component({ | ||
@@ -68,2 +152,10 @@ init() { | ||
{ | ||
code: `export default Service({ | ||
init() { | ||
this._super(...arguments); | ||
}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
}, | ||
{ | ||
code: `export default Component({ | ||
@@ -76,2 +168,34 @@ init: function() { | ||
}, | ||
{ | ||
code: `export default Route({ | ||
init: function() { | ||
this._super(...arguments); | ||
}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
}, | ||
{ | ||
code: `export default Controller({ | ||
init: function() { | ||
this._super(...arguments); | ||
}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
}, | ||
{ | ||
code: `export default Mixin({ | ||
init: function() { | ||
this._super(...arguments); | ||
}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
}, | ||
{ | ||
code: `export default Service({ | ||
init: function() { | ||
this._super(...arguments); | ||
}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
} | ||
], | ||
@@ -183,3 +307,209 @@ invalid: [ | ||
}, | ||
{ | ||
code: `export default Service.extend({ | ||
init() {}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }], | ||
}, | ||
{ | ||
code: `export default Service.extend({ | ||
init() { | ||
this.set('prop', 'value'); | ||
}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }], | ||
}, | ||
{ | ||
code: `export default Service.extend({ | ||
init() { | ||
this.set('prop', 'value'); | ||
this.set('prop2', 'value2'); | ||
}, | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }], | ||
}, | ||
{ | ||
code: `export default Component.extend({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Route.extend({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Controller.extend({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Mixin.extend({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Service.extend({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Component({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Route({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Controller({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Mixin({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Service({ | ||
init() { | ||
return; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Component.extend({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Route.extend({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Controller.extend({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Mixin.extend({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Service.extend({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Component({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Route({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Controller({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Mixin({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
}, | ||
{ | ||
code: `export default Service({ | ||
init() { | ||
return 'meh'; | ||
} | ||
});`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ message, line: 2 }] | ||
} | ||
], | ||
}); |
@@ -225,2 +225,35 @@ 'use strict'; | ||
describe('isEmberService', () => { | ||
it('should check if it\'s an Ember Service', () => { | ||
let node; | ||
node = parse('Ember.Service.extend()'); | ||
expect( | ||
emberUtils.isEmberService(node), | ||
'it should detect Service when using Ember.Service' | ||
).toBeTruthy(); | ||
node = parse('Service.extend()'); | ||
expect( | ||
emberUtils.isEmberService(node), | ||
'it should detect Service when using local module Service' | ||
).toBeTruthy(); | ||
}); | ||
it('should check if it\'s an Ember Service even if it uses custom name', () => { | ||
const node = parse('CustomService.extend()'); | ||
const filePath = 'example-app/services/path/to/some-feature.js'; | ||
expect( | ||
emberUtils.isEmberService(node), | ||
'it shouldn\'t detect Service when no file path is proviced' | ||
).toBeFalsy(); | ||
expect( | ||
emberUtils.isEmberService(node, filePath), | ||
'it should detect Service when file path is provided' | ||
).toBeTruthy(); | ||
}); | ||
}); | ||
describe('isInjectedServiceProp', () => { | ||
@@ -227,0 +260,0 @@ let node; |
@@ -137,2 +137,10 @@ 'use strict'; | ||
describe('isReturnStatement', () => { | ||
const node = babelEslint.parse('return').body[0]; | ||
it('should check if node is a return statement', () => { | ||
expect(utils.isReturnStatement(node)).toBeTruthy(); | ||
}); | ||
}); | ||
describe('isThisExpression', () => { | ||
@@ -139,0 +147,0 @@ const node = parse('this'); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
460379
8563