New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

eslint-plugin-ember

Package Overview
Dependencies
Maintainers
5
Versions
190
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-ember - npm Package Compare versions

Comparing version 4.2.0 to 4.3.0

docs/rules/no-duplicate-dependent-keys.md

7

docs/rules/named-functions-in-promises.md

@@ -41,2 +41,9 @@ ## Use named functions defined on objects to handle promises

},
// GOOD if allowSimpleArrowFunction: true
updateUser(user) {
user.save()
.then(() => this._reloadUser())
.then(() => this._notifyAboutSuccess())
.catch(() => this._notifyAboutFailure());
},
},

@@ -43,0 +50,0 @@ _reloadUser(user) {

1

lib/recommended-rules.js

@@ -28,2 +28,3 @@ /*

"ember/order-in-routes": "error",
"ember/require-super-in-init": "off",
"ember/routes-segments-snake-case": "error",

@@ -30,0 +31,0 @@ "ember/use-brace-expansion": "error",

34

lib/rules/use-brace-expansion.js

@@ -31,13 +31,29 @@ 'use strict';

const properties = node.arguments
.filter(arg => utils.isLiteral(arg) && typeof arg.value === 'string')
const matchesBraces = x => !!x.match(/[{}]/g);
const hasBraces = arr => arr.some(matchesBraces);
const beforeBraces = arr => arr.slice(0, arr.indexOf(arr.find(matchesBraces)));
const arrayDeepEqual = (a, b) =>
a.length === b.length && a.reduce((acc, e, i) => acc && e === b[i], true);
const problem = node.arguments
.filter(arg => utils.isLiteral(arg) && typeof arg.value === 'string' && arg.value.indexOf('.') >= 0)
.map(e => e.value.split('.'))
.filter(e => e.length > 1)
.map(e => e[0])
.sort((a, b) => a > b);
.find((prop, i, props) => props.filter((e) => {
const propHasBraces = hasBraces(prop);
const eHasBraces = hasBraces(e);
for (let i = 0; i < properties.length - 1; i++) {
if (properties[i] === properties[i + 1]) {
report(node);
}
if (propHasBraces && eHasBraces) {
return arrayDeepEqual(beforeBraces(e), beforeBraces(prop));
} else if (!propHasBraces && !eHasBraces) {
return prop[0] === e[0];
}
const withBraces = propHasBraces ? prop : e;
const withoutBraces = propHasBraces ? e : prop;
const shareable = beforeBraces(withBraces);
return arrayDeepEqual(shareable, withoutBraces.slice(0, shareable.length));
}).length > 1);
if (problem) {
report(node);
}

@@ -44,0 +60,0 @@ },

@@ -14,2 +14,3 @@ 'use strict';

isEmberRoute,
isEmberMixin,

@@ -47,2 +48,5 @@ isSingleLineFn,

isControllerDefaultProp,
parseDependentKeys,
unwrapBraceExpressions,
hasDuplicateDependentKeys,
};

@@ -125,2 +129,5 @@

}
function isEmberMixin(node, filePath) {
return isEmberCoreModule(node, 'Mixin', filePath);
}

@@ -370,1 +377,66 @@ function isInjectedServiceProp(node) {

}
/**
* Checks whether a computed property has duplicate dependent keys.
*
* @param {CallExpression} callExp Given call expression
* @return {Boolean} Flag whether dependent keys present.
*/
function hasDuplicateDependentKeys(callExp) {
if (!isComputedProp(callExp)) return false;
const dependentKeys = parseDependentKeys(callExp);
const uniqueKeys = dependentKeys
.filter((val, index, self) => self.indexOf(val) === index);
return uniqueKeys.length !== dependentKeys.length;
}
/**
* Parses dependent keys from call expression and returns them in an array.
*
* It also unwraps the expressions, so that `model.{foo,bar}` becomes `model.foo, model.bar`.
*
* @param {CallExpression} callExp CallExpression to examine
* @return {Array} Array of unwrapped dependent keys
*/
function parseDependentKeys(callExp) {
// Check whether we have a MemberExpression, eg. computed(...).volatile()
const isMemberExpCallExp = !callExp.arguments.length &&
utils.isMemberExpression(callExp.callee) &&
utils.isCallExpression(callExp.callee.object);
const args = isMemberExpCallExp ? callExp.callee.object.arguments : callExp.arguments;
const dependentKeys = args
.filter(arg => utils.isLiteral(arg))
.map(literal => literal.value);
return unwrapBraceExpressions(dependentKeys);
}
/**
* Unwraps brace expressions.
*
* @param {Array} dependentKeys array of strings containing unprocessed dependent keys.
* @return {Array} Array of unwrapped dependent keys
*/
function unwrapBraceExpressions(dependentKeys) {
const braceExpressionRegexp = /{.+}/g;
const unwrappedExpressions = dependentKeys.map((key) => {
if (!braceExpressionRegexp.test(key)) return key;
const braceExpansionPart = key.match(braceExpressionRegexp)[0];
const prefix = key.replace(braceExpansionPart, '');
const properties = braceExpansionPart
.replace('{', '')
.replace('}', '')
.split(',');
return properties.map(property => `${prefix}${property}`);
});
return unwrappedExpressions
.reduce((acc, cur) => acc.concat(cur), []);
}
{
"name": "eslint-plugin-ember",
"version": "4.2.0",
"version": "4.3.0",
"description": "Eslint plugin for Ember.js apps",

@@ -56,3 +56,3 @@ "main": "lib/index.js",

"dependencies": {
"ember-rfc176-data": "^0.2.2",
"ember-rfc176-data": "^0.2.7",
"requireindex": "^1.1.0",

@@ -65,6 +65,3 @@ "snake-case": "^2.1.0"

]
},
"publishConfig": {
"tag": "latest"
}
}

@@ -40,4 +40,4 @@ # eslint-plugin-ember

Possible configurations:
- [plugin:ember/base](https://github.com/ember-cli/eslint-plugin-ember/blob/master/config/base.js) - contains only recommended settings for custom rules defined in this plugin.
- [plugin:ember/recommended](https://github.com/ember-cli/eslint-plugin-ember/blob/master/config/recommended.js) - extends base configuration with extra rules' settings provided by eslint
- [plugin:ember/base](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/base.js) - contains only recommended settings for custom rules defined in this plugin.
- [plugin:ember/recommended](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/recommended.js) - extends base configuration with extra rules' settings provided by eslint

@@ -106,2 +106,3 @@ #### Use plain plugin:

| :white_check_mark: | [no-side-effects](./docs/rules/no-side-effects.md) | Warns about unexpected side effects in computed properties |
| | [require-super-in-init](./docs/rules/require-super-in-init.md) | Enforces super calls in init hooks |
| :white_check_mark: | [use-brace-expansion](./docs/rules/use-brace-expansion.md) | Enforces usage of brace expansion |

@@ -108,0 +109,0 @@ | :white_check_mark: :wrench: | [use-ember-get-and-set](./docs/rules/use-ember-get-and-set.md) | Enforces usage of Ember.get and Ember.set |

@@ -45,3 +45,3 @@ // ------------------------------------------------------------------------------

code: 'var Router = Ember.Router.extend({});',
errors: [{ message: 'Use import Router from \'@ember/routing/router\'; instead of using Ember.Router' }],
errors: [{ message: 'Use import EmberRouter from \'@ember/routing/router\'; instead of using Ember.Router' }],
},

@@ -54,5 +54,5 @@ {

code: 'new Ember.RSVP.Promise();',
errors: [{ message: 'Use import RSVP from \'rsvp\'; instead of using Ember.RSVP' }],
errors: [{ message: 'Use import { Promise } from \'rsvp\'; instead of using Ember.RSVP.Promise' }],
},
],
});

@@ -19,2 +19,5 @@ // ------------------------------------------------------------------------------

{ code: '{ test: computed("a.{test,test2}", "b", function() {}) }' },
{ code: '{ test: computed("a.{test,test2}", "c", "b", function() {}) }' },
{ code: '{ test: computed("model.a.{test,test2}", "model.b.{test3,test4}", function() {}) }' },
{ code: '{ test: computed("foo.bar.{name,place}", "foo.qux.[]", "foo.baz.{thing,@each.stuff}", function() {}) }' },
{ code: "{ test: Ember.computed.filterBy('a', 'b', false) }" },

@@ -24,2 +27,8 @@ ],

{
code: '{ test: computed("foo.{name,place}", "foo.[]", "foo.{thing,@each.stuff}", function() {}) }',
errors: [{
message: 'Use brace expansion',
}],
},
{
code: '{ test: computed("a.test", "a.test2", function() {}) }',

@@ -31,2 +40,20 @@ errors: [{

{
code: '{ test: computed("a.{same,level}", "a.{not,nested}", function() {}) }',
errors: [{
message: 'Use brace expansion',
}],
},
{
code: '{ test: computed("a.b.c.d", "a.b.deep.props", function() {}) }',
errors: [{
message: 'Use brace expansion',
}],
},
{
code: '{ test: computed("a.{test,test2}", "c", "a.test3.test4", function() {}) }',
errors: [{
message: 'Use brace expansion',
}],
},
{
code: '{ test: computed("a.{test,test2}", "a.test3", function() {}) }',

@@ -33,0 +60,0 @@ errors: [{

@@ -446,2 +446,117 @@ 'use strict';

describe('parseDependentKeys', () => {
it('should parse dependent keys from callexpression', () => {
const node = parse("computed('model.{foo,bar}', 'model.bar')");
expect(emberUtils.parseDependentKeys(node)).toEqual([
'model.foo', 'model.bar', 'model.bar',
]);
});
it('should work when no dependent keys present', () => {
const node = parse('computed(function() {})');
expect(emberUtils.parseDependentKeys(node)).toEqual([]);
});
it('should handle dependent keys and function arguments', () => {
const node = parse("computed('model.{foo,bar}', 'model.bar', function() {})");
expect(emberUtils.parseDependentKeys(node)).toEqual([
'model.foo', 'model.bar', 'model.bar',
]);
});
it('should handle dependent keys and function arguments in MemberExpression', () => {
const node = parse(`
computed('model.{foo,bar}', 'model.bar', function() {
}).volatile();
`);
expect(emberUtils.parseDependentKeys(node)).toEqual([
'model.foo', 'model.bar', 'model.bar',
]);
});
});
describe('unwrapBraceExpressions', () => {
it('should unwrap simple dependent keys', () => {
expect(emberUtils.unwrapBraceExpressions([
'model.foo', 'model.bar'
])).toEqual(['model.foo', 'model.bar']);
});
it('should unwrap dependent keys with braces', () => {
expect(emberUtils.unwrapBraceExpressions([
'model.{foo,bar}', 'model.bar'
])).toEqual(['model.foo', 'model.bar', 'model.bar']);
});
it('should unwrap more complex dependent keys', () => {
expect(emberUtils.unwrapBraceExpressions([
'model.{foo,bar}', 'model.bar', 'data.{foo,baz,qux}'
])).toEqual([
'model.foo', 'model.bar', 'model.bar', 'data.foo', 'data.baz', 'data.qux',
]);
});
it('should unwrap multi-level keys', () => {
expect(emberUtils.unwrapBraceExpressions([
'model.bar.{foo,qux}', 'model.bar.baz'
])).toEqual([
'model.bar.foo', 'model.bar.qux', 'model.bar.baz'
]);
});
it('should unwrap @each with extensions', () => {
expect(emberUtils.unwrapBraceExpressions([
'collection.@each.{foo,bar}', 'collection.@each.qux'
])).toEqual([
'collection.@each.foo', 'collection.@each.bar', 'collection.@each.qux'
]);
});
it('should unwrap complicated mixed dependent keys', () => {
expect(emberUtils.unwrapBraceExpressions([
'a.b.c.{d.@each.qwe.zxc,f,g.[]}'
])).toEqual([
'a.b.c.d.@each.qwe.zxc', 'a.b.c.f', 'a.b.c.g.[]',
]);
});
it('should unwrap complicated mixed repeated dependent keys', () => {
expect(emberUtils.unwrapBraceExpressions([
'a.b.{d.@each.qux,f,d.@each.foo}'
])).toEqual([
'a.b.d.@each.qux', 'a.b.f', 'a.b.d.@each.foo',
]);
});
});
describe('hasDuplicateDependentKeys', () => {
it('reports duplicate dependent keys in computed calls', () => {
let node = parse("computed('model.{foo,bar}', 'model.bar')");
expect(emberUtils.hasDuplicateDependentKeys(node)).toBeTruthy();
node = parse("Ember.computed('model.{foo,bar}', 'model.bar')");
expect(emberUtils.hasDuplicateDependentKeys(node)).toBeTruthy();
});
it('ignores not repeated dependentKeys', () => {
let node = parse("computed('model.{foo,bar}', 'model.qux')");
expect(emberUtils.hasDuplicateDependentKeys(node)).not.toBeTruthy();
node = parse("Ember.computed('model.{foo,bar}', 'model.qux')");
expect(emberUtils.hasDuplicateDependentKeys(node)).not.toBeTruthy();
node = parse("computed('model.{foo,bar}', 'model.qux').volatile()");
expect(emberUtils.hasDuplicateDependentKeys(node)).not.toBeTruthy();
});
it('ignores non-computed calls', () => {
const node = parse("foo('model.{foo,bar}', 'model.bar')");
expect(emberUtils.hasDuplicateDependentKeys(node)).not.toBeTruthy();
});
it('reports duplicate dependent keys in computed calls with MemberExp', () => {
let node = parse("Ember.computed('model.{foo,bar}', 'model.bar').volatile()");
expect(emberUtils.hasDuplicateDependentKeys(node)).toBeTruthy();
node = parse("computed('model.{foo,bar}', 'model.bar').volatile()");
expect(emberUtils.hasDuplicateDependentKeys(node)).toBeTruthy();
});
});
describe('getEmberImportAliasName', () => {

@@ -448,0 +563,0 @@ it('should get the proper name of default import', () => {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc