Skip to content

Commit

Permalink
Merge pull request #8 from ramda/specialized-names
Browse files Browse the repository at this point in the history
Support for specialized functions
  • Loading branch information
haskellcamargo authored Nov 16, 2017
2 parents 8a7e469 + ffb3234 commit 673071b
Show file tree
Hide file tree
Showing 39 changed files with 630 additions and 4,057 deletions.
38 changes: 38 additions & 0 deletions ast-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';
const R = require('ramda');

// :: Node -> String
const getName = R.ifElse(
R.propEq('type', 'MemberExpression'),
R.path(['property', 'name']),
R.prop('name')
);

// :: String -> Node -> Boolean
const isRamdaMethod = name => R.either(
R.whereEq({
type: 'Identifier',
name
}),
R.where({
type: R.equals('MemberExpression'),
object: R.whereEq({ type: 'Identifier', name: 'R' }),
property: R.either(
R.whereEq({ type: 'Identifier', name }),
R.whereEq({ type: 'Literal', value: name })
)
})
);

// :: { name :: String, arguments :: [Node] -> Boolean }
// -> Object
// -> Boolean
const isCalling = pattern => R.where({
type: R.equals('CallExpression'),
callee: isRamdaMethod(pattern.name),
arguments: pattern.arguments || R.T
});

exports.isRamdaMethod = isRamdaMethod;
exports.isCalling = isCalling;
exports.getName = getName;
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-ramda",
"version": "2.0.0",
"version": "2.1.0",
"description": "ESLint rules for use with Ramda",
"license": "MIT",
"keywords": [
Expand All @@ -22,6 +22,7 @@
},
"dependencies": {
"create-eslint-index": "^1.0.0",
"ramda": "0.25.0",
"req-all": "^1.0.0"
},
"devDependencies": {
Expand All @@ -40,6 +41,7 @@
},
"files": [
"index.js",
"ast-helper.js",
"rules/",
"LICENSE",
"README.md"
Expand Down
37 changes: 20 additions & 17 deletions rules/any-pass-simplification.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
'use strict';
const R = require('ramda');
const isCalling = require('../ast-helper').isCalling;

const create = context => ({
CallExpression(node) {
if (node.callee.type === 'Identifier'
&& node.callee.name === 'anyPass'
&& node.arguments.length > 0
&& node.arguments[0].type === 'ArrayExpression') {
const elements = node.arguments[0].elements;
const negated = elements
.filter(element =>
element.type === 'CallExpression'
&& element.callee.type === 'Identifier'
&& element.callee.name === 'complement'
);
if (elements.length === negated.length) {
context.report({
node,
message: '`anyPass` containing only complement functions should be a complement of `allPass`'
});
}
const match = isCalling({
name: 'anyPass',
arguments: R.both(
R.propSatisfies(R.lt(0), 'length'),
R.where({
0: R.both(
R.propEq('type', 'ArrayExpression'),
R.propSatisfies(R.all(isCalling({ name: 'complement' })), 'elements')
)
})
)
});

if (match(node)) {
context.report({
node,
message: '`anyPass` containing only complement functions should be a complement of `allPass`'
});
}
}
});
Expand Down
25 changes: 15 additions & 10 deletions rules/both-simplification.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
'use strict';
const R = require('ramda');
const isCalling = require('../ast-helper').isCalling;

const create = context => ({
CallExpression(node) {
if (node.callee.type === 'Identifier'
&& node.callee.name === 'both'
&& node.arguments.length > 1
&& node.arguments[0].type === 'CallExpression'
&& node.arguments[1].type === 'CallExpression'
&& node.arguments[0].callee.type === 'Identifier'
&& node.arguments[1].callee.type === 'Identifier'
&& node.arguments[0].callee.name === 'complement'
&& node.arguments[1].callee.name === 'complement'
&& node.arguments) {
const match = isCalling({
name: 'both',
arguments: R.ifElse(
R.propSatisfies(R.lt(1), 'length'),
R.where({
0: isCalling({ name: 'complement' }),
1: isCalling({ name: 'complement' })
}),
R.F
)
});

if (match(node)) {
context.report({
node,
message: '`both(complement(_), complement(_))` should be simplified to `complement(either(_, _))`'
Expand Down
47 changes: 29 additions & 18 deletions rules/complement-simplification.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
'use strict';
const R = require('ramda');
const ast = require('../ast-helper');

const isCalling = ast.isCalling;
const isRamdaMethod = ast.isRamdaMethod;
const getName = ast.getName;

const names = {
or: 'and',
and: 'or',
T: 'F',
F: 'T'
};

const create = context => ({
CallExpression(node) {
if (node.callee.type === 'Identifier'
&& node.callee.name === 'complement'
&& node.arguments.length > 0
&& node.arguments[0].type === 'Identifier') {
const name = node.arguments[0].name;
const replacementTable = {
or: 'and',
and: 'or',
T: 'F',
F: 'T'
};
const match = isCalling({
name: 'complement',
arguments: R.both(
R.propSatisfies(R.lt(0), 'length'),
R.where({
0: R.anyPass(R.map(isRamdaMethod, R.keys(names)))
})
)
});

const expected = replacementTable[name];
if (expected) {
context.report({
node,
message: `\`complement(${name})\` should be simplified to \`${expected}\``
});
}
if (match(node)) {
const name = getName(node.arguments[0]);
const expected = names[name];
context.report({
node,
message: `\`complement(${name})\` should be simplified to \`${expected}\``
});
}
}
});
Expand Down
21 changes: 16 additions & 5 deletions rules/cond-simplification.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
'use strict';
const R = require('ramda');
const isCalling = require('../ast-helper').isCalling;

const create = context => ({
CallExpression(node) {
if (node.callee.type === 'Identifier'
&& node.callee.name === 'cond'
&& node.arguments.length > 0
&& node.arguments[0].type === 'ArrayExpression'
&& node.arguments[0].elements.length <= 2) {
const match = isCalling({
name: 'cond',
arguments: R.both(
R.propSatisfies(R.lt(0), 'length'),
R.where({
0: R.both(
R.propEq('type', 'ArrayExpression'),
R.pathSatisfies(R.gte(2), ['elements', 'length'])
)
})
)
});

if (match(node)) {
context.report({
node,
message: '`cond` with too few parameters should be `ifElse`, `either` or `both`'
Expand Down
25 changes: 15 additions & 10 deletions rules/either-simplification.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
'use strict';
const R = require('ramda');
const isCalling = require('../ast-helper').isCalling;

const create = context => ({
CallExpression(node) {
if (node.callee.type === 'Identifier'
&& node.callee.name === 'either'
&& node.arguments.length > 1
&& node.arguments[0].type === 'CallExpression'
&& node.arguments[1].type === 'CallExpression'
&& node.arguments[0].callee.type === 'Identifier'
&& node.arguments[1].callee.type === 'Identifier'
&& node.arguments[0].callee.name === 'complement'
&& node.arguments[1].callee.name === 'complement'
&& node.arguments) {
const match = isCalling({
name: 'either',
arguments: R.ifElse(
R.propSatisfies(R.lt(1), 'length'),
R.where({
0: isCalling({ name: 'complement' }),
1: isCalling({ name: 'complement' })
}),
R.F
)
});

if (match(node)) {
context.report({
node,
message: '`either(complement(_), complement(_))` should be simplified to `complement(both(_, _))`'
Expand Down
19 changes: 13 additions & 6 deletions rules/filter-simplification.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
'use strict';
const R = require('ramda');
const isCalling = require('../ast-helper').isCalling;

const create = context => ({
CallExpression(node) {
if (node.callee.type === 'Identifier'
&& node.callee.name === 'filter'
&& node.arguments.length > 0
&& node.arguments[0].type === 'CallExpression'
&& node.arguments[0].callee.type === 'Identifier'
&& node.arguments[0].callee.name === 'complement') {
const match = isCalling({
name: 'filter',
arguments: R.both(
R.propSatisfies(R.lt(0), 'length'),
R.propSatisfies(isCalling({
name: 'complement'
}), 0)
)
});

if (match(node)) {
context.report({
node,
message: '`filter(complement(_))` should be simplified to `reject(_)`'
Expand Down
47 changes: 31 additions & 16 deletions rules/if-else-simplification.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
'use strict';
const R = require('ramda');
const ast = require('../ast-helper');

const isCalling = ast.isCalling;
const isRamdaMethod = ast.isRamdaMethod;

const create = context => ({
CallExpression(node) {
if (node.callee.type === 'Identifier'
&& node.callee.name === 'ifElse'
&& node.arguments.length >= 2) {
const second = node.arguments[1].name;
const matchUnless = isCalling({
name: 'ifElse',
arguments: R.both(
R.propSatisfies(R.lte(2), 'length'),
R.propSatisfies(isRamdaMethod('identity'), 1)
)
});

const matchWhen = isCalling({
name: 'ifElse',
arguments: R.both(
R.propSatisfies(R.lte(3), 'length'),
R.propSatisfies(isRamdaMethod('identity'), 2)
)
});

if (second === 'identity') {
context.report({
node,
message: '`ifElse(_, identity, _)` should be simplified to `unless(_, _)`'
});
}
if (matchUnless(node)) {
context.report({
node,
message: '`ifElse(_, identity, _)` should be simplified to `unless(_, _)`'
});
}

if (node.arguments[2] && node.arguments[2].name === 'identity') {
context.report({
node,
message: '`ifElse(_, _, identity)` should be simplified to `when(_, _)`'
});
}
if (matchWhen(node)) {
context.report({
node,
message: '`ifElse(_, _, identity)` should be simplified to `when(_, _)`'
});
}
}
});
Expand Down
19 changes: 13 additions & 6 deletions rules/map-simplification.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
'use strict';
const R = require('ramda');
const isCalling = require('../ast-helper').isCalling;

const create = context => ({
CallExpression(node) {
if (node.callee.type === 'Identifier'
&& node.callee.name === 'map'
&& node.arguments.length > 0
&& node.arguments[0].type === 'CallExpression'
&& node.arguments[0].callee.type === 'Identifier'
&& node.arguments[0].callee.name === 'prop') {
const match = isCalling({
name: 'map',
arguments: R.both(
R.propSatisfies(R.lt(0), 'length'),
R.propSatisfies(isCalling({
name: 'prop'
}), 0)
)
});

if (match(node)) {
context.report({
node,
message: '`map(prop(_))` should be simplified to `pluck(_)`'
Expand Down
Loading

0 comments on commit 673071b

Please sign in to comment.