eslint-plugin-ember-best-practices
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -14,2 +14,3 @@ Burnashov Evgeny <sham@sveak.com> | ||
Mikael Riska <mikael.riska@ecraft.com> | ||
Nathaniel Furniss <nfurniss@linkedin.com> | ||
Nathaniel Furniss <nlfurniss@gmail.com> | ||
@@ -16,0 +17,0 @@ Quinn C. Hoyer <qhoyer@linkedin.com> |
1.1.0 / 2017-10-03 | ||
================== | ||
* Fixes issues with imports (#91) | ||
* Fixes require-ember-lifeline rule to scope it to only ember file types (#93) | ||
* Merge pull request #89 from nlfurniss/dry-parserOptions | ||
1.0.0 / 2017-09-13 | ||
@@ -3,0 +10,0 @@ ================== |
@@ -5,2 +5,3 @@ /** | ||
const { get } = require('../utils/get'); | ||
const { getCaller, cleanCaller } = require('../utils/caller'); | ||
@@ -11,4 +12,19 @@ const isCPGetter = require('../utils/computed-property'); | ||
let SIDE_EFFECTS = ['this.send', 'this.sendAction', 'this.sendEvent', 'Em.sendEvent', 'Ember.sendEvent', 'this.trigger', 'this.set', 'Ember.set', 'Em.set', 'this.setProperties', 'Ember.setProperties', 'Em.setProperties']; | ||
function sideEffectsForEmber(importName) { | ||
return [ | ||
`${importName}.sendEvent`, | ||
`${importName}.set`, | ||
`${importName}.setProperties` | ||
]; | ||
} | ||
const SIDE_EFFECTS = Object.freeze([].concat( | ||
'this.send', | ||
'this.sendAction', | ||
'this.trigger', | ||
sideEffectsForEmber('this'), | ||
sideEffectsForEmber('Em'), | ||
sideEffectsForEmber('Ember') | ||
)); | ||
const MESSAGE = 'Do not send events or actions in Computed Properties. This will cause data flow issues in the application, where the accessing of a property causes some side-effect. You should only send actions on behalf of user initiated events. Please see the following guide for more information: https://github.com/ember-best-practices/eslint-plugin-ember-best-practices/blob/master/guides/rules/no-side-effect-cp.md'; | ||
@@ -26,12 +42,13 @@ | ||
create(context) { | ||
let inCPGettter = false; | ||
let bindings = []; | ||
let emberImportBinding; | ||
let inCPGetter = false; | ||
let importedSideEffectBindings = []; | ||
let emberImportBinding, importedCPBinding; | ||
let sideEffects = SIDE_EFFECTS.slice(); | ||
return { | ||
ImportDeclaration(node) { | ||
bindings = collectImportBindings(node, { | ||
importedSideEffectBindings = importedSideEffectBindings.concat(collectImportBindings(node, { | ||
'@ember/object': ['set', 'setProperties'], | ||
'@ember/object/events': ['sendEvent'] | ||
}); | ||
})); | ||
}, | ||
@@ -41,3 +58,3 @@ | ||
if (emberImportBinding) { | ||
SIDE_EFFECTS = SIDE_EFFECTS.concat(collectObjectPatternBindings(node, { | ||
sideEffects = sideEffects.concat(collectObjectPatternBindings(node, { | ||
[emberImportBinding]: ['set', 'setProperties', 'sendEvent'] | ||
@@ -49,13 +66,21 @@ })); | ||
ImportDefaultSpecifier(node) { | ||
emberImportBinding = getEmberImportBinding(node); | ||
let localEmberImportBinding = getEmberImportBinding(node); | ||
if (localEmberImportBinding) { | ||
emberImportBinding = localEmberImportBinding; | ||
sideEffects = sideEffects.concat(sideEffectsForEmber(emberImportBinding)); | ||
} | ||
if (get(node, 'parent.source.value') === '@ember/computed') { | ||
importedCPBinding = node.local.name; | ||
} | ||
}, | ||
FunctionExpression(node) { | ||
if (isCPGetter(node)) { | ||
inCPGettter = true; | ||
if (isCPGetter(node, emberImportBinding, importedCPBinding)) { | ||
inCPGetter = node; | ||
} | ||
}, | ||
'FunctionExpression:exit'(node) { | ||
if (isCPGetter(node)) { | ||
inCPGettter = false; | ||
if (node === inCPGetter) { | ||
inCPGetter = null; | ||
} | ||
@@ -65,5 +90,5 @@ }, | ||
CallExpression(node) { | ||
if (inCPGettter) { | ||
if (inCPGetter) { | ||
let caller = cleanCaller(getCaller(node)); | ||
let hasSideEffect = SIDE_EFFECTS.includes(caller) || bindings.includes(caller); | ||
let hasSideEffect = sideEffects.includes(caller) || importedSideEffectBindings.includes(caller); | ||
if (hasSideEffect) { | ||
@@ -70,0 +95,0 @@ context.report(node, MESSAGE); |
@@ -9,2 +9,3 @@ /** | ||
const LINTABLE_FILE_PATTERNS = /\/components|routes|controllers|services\//i; | ||
let DISALLOWED_OBJECTS = ['Ember.run', 'run']; | ||
@@ -32,2 +33,6 @@ let RUN_METHODS = ['later', 'next', 'debounce', 'throttle']; | ||
function isLintableFile(context) { | ||
return LINTABLE_FILE_PATTERNS.test(context.eslint.getFilename()); | ||
} | ||
module.exports = { | ||
@@ -66,2 +71,6 @@ docs: { | ||
if (!isLintableFile(context)) { | ||
return; | ||
} | ||
if (disallowedCalls.includes(caller)) { | ||
@@ -68,0 +77,0 @@ context.report(node, getMessage(caller)); |
const { get } = require('./get'); | ||
const { getCaller } = require('./caller'); | ||
function isCPDesc(node, method) { | ||
function isCPDesc(node, importedEmberName, importedCPName) { | ||
if (node.type !== 'FunctionExpression') { | ||
@@ -9,3 +9,3 @@ return false; | ||
if (get(node, 'parent.key.name') !== method) { | ||
if (get(node, 'parent.key.name') !== 'get') { | ||
return false; | ||
@@ -26,3 +26,3 @@ } | ||
let callee = greatGrandParent.callee; | ||
if (greatGrandParent.type === 'Identifier' && callee.name === 'computed') { | ||
if (greatGrandParent.type === 'Identifier' && (callee.name === 'computed' || callee.name === importedCPName)) { | ||
return true; | ||
@@ -32,3 +32,3 @@ } | ||
let caller = getCaller(callee); | ||
return caller === 'Ember.computed' || caller === 'Em.computed'; | ||
return caller === 'Ember.computed' || caller === 'Em.computed' || caller === `${importedEmberName}.computed`; | ||
} | ||
@@ -46,3 +46,3 @@ return false; // don't know how you could get here | ||
function isCPAccessor(node) { | ||
function isCPAccessor(node, importedEmberName, importedCPName) { | ||
if (node.type !== 'FunctionExpression') { | ||
@@ -58,3 +58,3 @@ return false; | ||
let callee = parent.callee; | ||
if (callee.type === 'Identifier' && callee.name === 'computed') { | ||
if (callee.type === 'Identifier' && (callee.name === 'computed' || callee.name === importedCPName)) { | ||
return true; | ||
@@ -65,3 +65,3 @@ } | ||
let caller = getCaller(callee); | ||
return caller === 'Ember.computed' || caller === 'Em.computed'; | ||
return caller === 'Ember.computed' || caller === 'Em.computed' || caller === `${importedEmberName}.computed`; | ||
} | ||
@@ -71,5 +71,7 @@ return false; | ||
module.exports = function isCPGetter(node) { | ||
return isCPDesc(node, 'get') || isCPAccessor(node) || isPrototypeExtCP(node); | ||
module.exports = function isCPGetter(node, importedEmberName, importedCPName) { | ||
return isCPDesc(node, importedEmberName, importedCPName) | ||
|| isCPAccessor(node, importedEmberName, importedCPName) | ||
|| isPrototypeExtCP(node); | ||
}; | ||
{ | ||
"name": "eslint-plugin-ember-best-practices", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Eslint rules for linting for anti-patterns in Ember applications.", | ||
@@ -32,3 +32,3 @@ "main": "lib/index.js", | ||
"mocha-eslint": "^3.0.1", | ||
"nyc": "^10.0.0" | ||
"nyc": "^11.2.1" | ||
}, | ||
@@ -35,0 +35,0 @@ "dependencies": { |
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
227574
54
1229
0