Skip to content

Commit dc149b5

Browse files
authored
Merge pull request #624 from bmish/eslint-plugin-unicorn
Add eslint-plugin-unicorn internally and fix recommended rules
2 parents c93198c + 9a6a4be commit dc149b5

32 files changed

+336
-149
lines changed

.eslintrc.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ module.exports = {
44
root: true,
55
parserOptions: {
66
ecmaVersion: 2017,
7+
sourceType: 'script'
78
},
89
plugins: [
910
'eslint-plugin',
1011
'filenames',
1112
'import',
1213
'node',
13-
'prettier'
14+
'prettier',
15+
'unicorn'
1416
],
1517
extends: [
1618
'eslint:recommended',
@@ -19,6 +21,7 @@ module.exports = {
1921
'plugin:import/errors',
2022
'plugin:import/warnings',
2123
'plugin:node/recommended',
24+
'plugin:unicorn/recommended',
2225
'prettier'
2326
],
2427
env: {
@@ -117,6 +120,9 @@ module.exports = {
117120
    'import/no-useless-path-segments''error',
118121
    'import/no-webpack-loader-syntax''error',
119122
'import/unambiguous': 'error',
123+
124+
// Unicorn rules:
125+
'unicorn/prevent-abbreviations': 'off'
120126
},
121127
overrides: [
122128
// test files

lib/rules/alias-model-in-controller.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ module.exports = {
4242
const parsedArgs = utils.parseArgs(property.value);
4343

4444
if (
45-
parsedCallee.length &&
46-
['alias', 'readOnly', 'reads'].indexOf(parsedCallee.pop()) > -1 &&
45+
parsedCallee.length > 0 &&
46+
['alias', 'readOnly', 'reads'].includes(parsedCallee.pop()) &&
4747
(parsedArgs[0] === 'model' || String(parsedArgs[0]).startsWith('model.'))
4848
) {
4949
aliasPresent = true;

lib/rules/avoid-leaking-state-in-ember-objects.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ module.exports = {
8787
const properties = ember.getModuleProperties(node);
8888

8989
properties
90-
.filter(property => property.key && ignoredProperties.indexOf(property.key.name) === -1)
90+
.filter(property => property.key && !ignoredProperties.includes(property.key.name))
9191
.forEach(property => {
9292
if (!isAllowed(property.value)) {
9393
report(property);

lib/rules/computed-property-getters.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ module.exports = {
4343

4444
const requireGetterOnlyWithASetterInComputedProperty = function(node) {
4545
const objectExpressions = node.arguments.filter(arg => types.isObjectExpression(arg));
46-
if (objectExpressions.length) {
46+
if (objectExpressions.length > 0) {
4747
const { properties } = objectExpressions[0];
4848
const getters = properties.filter(
4949
prop => prop.key && prop.key.name && prop.key.name === 'get'
@@ -62,7 +62,7 @@ module.exports = {
6262

6363
const preventGetterInComputedProperty = function(node) {
6464
const objectExpressions = node.arguments.filter(arg => types.isObjectExpression(arg));
65-
if (objectExpressions.length) {
65+
if (objectExpressions.length > 0) {
6666
const { properties } = objectExpressions[0];
6767
const getters = properties.filter(
6868
prop => prop.key && prop.key.name && prop.key.name === 'get'
@@ -80,7 +80,7 @@ module.exports = {
8080
const objectExpressions = node.arguments.filter(arg => types.isObjectExpression(arg));
8181
const functionExpressions = node.arguments.filter(arg => types.isFunctionExpression(arg));
8282

83-
if (objectExpressions.length) {
83+
if (objectExpressions.length > 0) {
8484
const { properties } = objectExpressions[0];
8585

8686
const getters = properties.filter(
@@ -89,14 +89,14 @@ module.exports = {
8989
if (getters.length === 0) {
9090
report(node, ALWAYS_GETTER_MESSAGE);
9191
}
92-
} else if (functionExpressions.length) {
92+
} else if (functionExpressions.length > 0) {
9393
report(node, ALWAYS_GETTER_MESSAGE);
9494
}
9595
};
9696

9797
return {
9898
CallExpression(node) {
99-
if (ember.isComputedProp(node) && node.arguments.length) {
99+
if (ember.isComputedProp(node) && node.arguments.length > 0) {
100100
if (requireGetters === 'always-with-setter') {
101101
requireGetterOnlyWithASetterInComputedProperty(node);
102102
}

lib/rules/jquery-ember-run.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ module.exports = {
4646
const callee = node.callee;
4747
const fnNodes = utils.findNodes(node.arguments, 'ArrowFunctionExpression');
4848

49-
if (isJqueryUsed(callee) && fnNodes.length) {
49+
if (isJqueryUsed(callee) && fnNodes.length > 0) {
5050
fnNodes.forEach(fnNode => {
5151
const fnBody = fnNode.body.body;
5252
const fnExpressions = utils.findNodes(fnBody, 'ExpressionStatement');

lib/rules/named-functions-in-promises.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,16 @@ module.exports = {
5858
}
5959
},
6060
};
61-
62-
function hasPromiseExpression(node) {
63-
const callee = node.callee;
64-
const promisesMethods = ['then', 'catch', 'finally'];
65-
66-
return (
67-
types.isCallExpression(callee.object) &&
68-
types.isIdentifier(callee.property) &&
69-
promisesMethods.indexOf(callee.property.name) > -1
70-
);
71-
}
7261
},
7362
};
63+
64+
function hasPromiseExpression(node) {
65+
const callee = node.callee;
66+
const promisesMethods = ['then', 'catch', 'finally'];
67+
68+
return (
69+
types.isCallExpression(callee.object) &&
70+
types.isIdentifier(callee.property) &&
71+
promisesMethods.includes(callee.property.name)
72+
);
73+
}

lib/rules/new-module-imports.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ module.exports = {
102102
const fullNames = getFullNames(fullName, node);
103103

104104
// find a matching expression starting at the end
105-
for (let i = 0; i < fullNames.length; i++) {
106-
fullName = fullNames[i];
105+
for (const element of fullNames) {
106+
fullName = element;
107107

108108
const key = fullName.replace(/^Ember\./, '');
109109
const match = GLOBALS[fullName];

lib/rules/no-actions-hash.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,6 @@ module.exports = {
2020
},
2121

2222
create: context => {
23-
function inClassWhichCanContainActions(context, node) {
24-
return (
25-
ember.isEmberComponent(context, node) ||
26-
ember.isEmberController(context, node) ||
27-
ember.isEmberRoute(context, node)
28-
);
29-
}
30-
3123
function reportActionsProp(properties) {
3224
const actionsProp = properties.find(property => ember.isActionsProp(property));
3325
if (actionsProp) {
@@ -49,3 +41,11 @@ module.exports = {
4941
};
5042
},
5143
};
44+
45+
function inClassWhichCanContainActions(context, node) {
46+
return (
47+
ember.isEmberComponent(context, node) ||
48+
ember.isEmberController(context, node) ||
49+
ember.isEmberRoute(context, node)
50+
);
51+
}

lib/rules/no-attrs-in-components.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ module.exports = {
2323
},
2424

2525
create(context) {
26-
function isThisAttrsExpression(node) {
27-
return types.isThisExpression(node.object) && node.property.name === 'attrs';
28-
}
29-
3026
return {
3127
MemberExpression(node) {
3228
if (isThisAttrsExpression(node)) {
@@ -36,3 +32,7 @@ module.exports = {
3632
};
3733
},
3834
};
35+
36+
function isThisAttrsExpression(node) {
37+
return types.isThisExpression(node.object) && node.property.name === 'attrs';
38+
}

lib/rules/no-empty-attrs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ module.exports = {
4040
);
4141

4242
isDSAttr.forEach(attr => {
43-
if (!attr.value.arguments.length) {
43+
if (attr.value.arguments.length === 0) {
4444
report(attr.value);
4545
}
4646
});

lib/rules/no-function-prototype-extensions.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ module.exports = {
2727

2828
const isFunctionPrototypeExtension = function(property) {
2929
return (
30-
types.isIdentifier(property) &&
31-
functionPrototypeExtensionNames.indexOf(property.name) !== -1
30+
types.isIdentifier(property) && functionPrototypeExtensionNames.includes(property.name)
3231
);
3332
};
3433

lib/rules/no-get.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function makeErrorMessageForGet(property, isImportedGet) {
1212
const ERROR_MESSAGE_GET_PROPERTIES =
1313
"Use `{ prop1: this.prop1, prop2: this.prop2, ... }` instead of Ember's `getProperties` function";
1414

15-
const VALID_JS_VARIABLE_NAME_REGEXP = RegExp('^[a-zA-Z_$][0-9a-zA-Z_$]*$');
15+
const VALID_JS_VARIABLE_NAME_REGEXP = new RegExp('^[a-zA-Z_$][0-9a-zA-Z_$]*$');
1616
function isValidJSVariableName(str) {
1717
return VALID_JS_VARIABLE_NAME_REGEXP.test(str);
1818
}

lib/rules/no-global-jquery.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ module.exports = {
4141
// Will exclude non-ember or non-jquery imports such as:
4242
// - `import Foo from 'bar';`
4343
// - `import { readOnly } from '@ember/object/computed';`
44-
if (validImportAlias.indexOf(node.source.value) !== -1) {
44+
if (validImportAlias.includes(node.source.value)) {
4545
const isJqueryImport = node.source.value === 'jquery';
4646
if (isJqueryImport) {
4747
hasJqueryImport = true;
@@ -107,17 +107,17 @@ module.exports = {
107107
}
108108
},
109109
};
110+
},
111+
};
110112

111-
function isNestedJQueryAssignment(props) {
112-
return props.filter(prop => prop.key.name === '$').length !== 0;
113-
}
113+
function isNestedJQueryAssignment(props) {
114+
return props.filter(prop => prop.key.name === '$').length !== 0;
115+
}
114116

115-
function isEmberIdentifier(init) {
116-
return init.name === 'Ember';
117-
}
117+
function isEmberIdentifier(init) {
118+
return init.name === 'Ember';
119+
}
118120

119-
function isEmberMemberExpression(init) {
120-
return init.object.name === 'Ember';
121-
}
122-
},
123-
};
121+
function isEmberMemberExpression(init) {
122+
return init.object.name === 'Ember';
123+
}

lib/rules/no-on-calls-in-components.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ module.exports = {
4848

4949
return (
5050
types.isCallExpression(value) &&
51-
lifecycleHooks.indexOf(args[0]) > -1 &&
51+
lifecycleHooks.includes(args[0]) &&
5252
((types.isIdentifier(callee) && callee.name === 'on') ||
5353
(types.isMemberExpression(callee) &&
5454
types.isIdentifier(callee.object) &&
@@ -70,7 +70,7 @@ module.exports = {
7070

7171
const propertiesWithOnCalls = ember.getModuleProperties(node).filter(isOnCall);
7272

73-
if (propertiesWithOnCalls.length) {
73+
if (propertiesWithOnCalls.length > 0) {
7474
propertiesWithOnCalls.forEach(property => {
7575
report(property.value);
7676
});

lib/rules/no-side-effects.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const ember = require('../utils/ember');
88
//------------------------------------------------------------------------------
99

1010
function isUnallowedMethod(name) {
11-
return ['set', 'setProperties'].indexOf(name) > -1;
11+
return ['set', 'setProperties'].includes(name);
1212
}
1313

1414
module.exports = {

lib/rules/no-unnecessary-route-path-option.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function pathMatchesRouteName(path, routeName) {
7676
return false;
7777
}
7878

79-
const pathWithoutOptionalLeadingSlash = path.substring(0, 1) === '/' ? path.substring(1) : path;
79+
const pathWithoutOptionalLeadingSlash = path.slice(0, 1) === '/' ? path.slice(1) : path;
8080
return pathWithoutOptionalLeadingSlash === routeName;
8181
}
8282

lib/rules/require-computed-property-dependencies.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ function getTraverser() {
55
try {
66
// eslint-disable-next-line node/no-unpublished-require
77
traverser = require('eslint/lib/shared/traverser'); // eslint >= 6
8-
} catch (e) {
8+
} catch (error) {
99
// eslint-disable-next-line node/no-unpublished-require, node/no-missing-require
1010
traverser = require('eslint/lib/util/traverser'); // eslint < 6
1111
}
@@ -402,7 +402,7 @@ module.exports = {
402402
)
403403
)
404404
.reduce((keys, key) => {
405-
if (keys.indexOf(key) < 0) {
405+
if (!keys.includes(key)) {
406406
keys.push(key);
407407
}
408408
return keys;
@@ -457,6 +457,10 @@ module.exports = {
457457
},
458458
};
459459

460+
function isBare(key) {
461+
return !key.includes('.') || key.endsWith('[]');
462+
}
463+
460464
/**
461465
* Collapse dependency keys with braces if possible.
462466
*
@@ -468,11 +472,7 @@ module.exports = {
468472
* @returns string
469473
*/
470474
function collapseKeys(keys) {
471-
const uniqueKeys = Array.from(new Set(keys));
472-
473-
function isBare(key) {
474-
return key.indexOf('.') === -1 || key.endsWith('[]');
475-
}
475+
const uniqueKeys = [...new Set(keys)];
476476

477477
const bareKeys = uniqueKeys.filter(isBare);
478478
const rest = uniqueKeys.filter(key => !isBare(key));
@@ -487,7 +487,7 @@ function collapseKeys(keys) {
487487
return mapByParent;
488488
}, new Map());
489489

490-
const joined = Array.from(mapByParent.keys()).map(parent => {
490+
const joined = [...mapByParent.keys()].map(parent => {
491491
const children = mapByParent.get(parent);
492492
if (children.length > 1) {
493493
return `${parent}.{${children.sort().join(',')}}`;
@@ -505,7 +505,7 @@ function collapseKeys(keys) {
505505
*/
506506
function expandKeys(keys) {
507507
// aka flat map
508-
return [].concat(...keys.map(expandKey));
508+
return [].concat(...keys.map(expandKey)); // eslint-disable-line unicorn/prefer-flat-map
509509
}
510510

511511
/**
@@ -522,9 +522,9 @@ function expandKey(key) {
522522
if (key.includes('{')) {
523523
// key = "foo.{bar,baz}"
524524
const keyParts = key.split('{'); // ["foo", "{bar,baz}"]
525-
const keyBeforeCurly = keyParts[0].substring(0, keyParts[0].length - 1); // "foo"
525+
const keyBeforeCurly = keyParts[0].slice(0, Math.max(0, keyParts[0].length - 1)); // "foo"
526526
const keyAfterCurly = keyParts[1]; // "{bar,baz}"
527-
const keyAfterCurlySplitByCommas = keyAfterCurly.replace(/\{|\}/g, '').split(','); // ["bar", "baz"]
527+
const keyAfterCurlySplitByCommas = keyAfterCurly.replace(/{|}/g, '').split(','); // ["bar", "baz"]
528528
const keyRecombined = [[keyBeforeCurly], keyAfterCurlySplitByCommas]; // [["foo"], ["baz", "baz"]]
529529
return keyRecombined
530530
.reduce(

lib/rules/route-path-style.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ function isWildcardSegment(segment) {
7878
return segment.includes('*');
7979
}
8080

81-
const KEBAB_CASE_REGEXP = /^[0-9a-z-]+$/;
81+
const KEBAB_CASE_REGEXP = /^[\da-z-]+$/; // eslint-disable-line unicorn/regex-shorthand
8282
function isKebabCase(str) {
8383
return str.match(KEBAB_CASE_REGEXP);
8484
}

lib/rules/routes-segments-snake-case.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module.exports = {
2424

2525
create(context) {
2626
const message = 'Use snake case in dynamic segments of routes';
27-
const routeSegmentRegex = /:([a-zA-Z0-9-_]+)/g;
27+
const routeSegmentRegex = /:([\w-]+)/g;
2828

2929
const report = function(node) {
3030
context.report(node, message);
@@ -60,7 +60,7 @@ module.exports = {
6060
routeOptions.properties.forEach(property => {
6161
const segmentNames = getSegmentNames(property);
6262

63-
if (segmentNames.length && segmentNames.filter(isNotSnakeCase).length) {
63+
if (segmentNames.length > 0 && segmentNames.filter(isNotSnakeCase).length > 0) {
6464
report(property.value);
6565
}
6666
});

0 commit comments

Comments
 (0)