├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── docs └── rules │ ├── no-arguments.md │ ├── no-class.md │ ├── no-delete.md │ ├── no-events.md │ ├── no-get-set.md │ ├── no-let.md │ ├── no-loops.md │ ├── no-mutating-assign.md │ ├── no-mutating-methods.md │ ├── no-mutation.md │ ├── no-nil.md │ ├── no-proxy.md │ ├── no-rest-parameters.md │ ├── no-this.md │ ├── no-throw.md │ ├── no-unused-expression.md │ └── no-valueof-field.md ├── index.js ├── license ├── package.json ├── readme.md ├── rule-description.js ├── rules ├── no-arguments.js ├── no-class.js ├── no-delete.js ├── no-events.js ├── no-get-set.js ├── no-let.js ├── no-loops.js ├── no-mutating-assign.js ├── no-mutating-methods.js ├── no-mutation.js ├── no-nil.js ├── no-proxy.js ├── no-rest-parameters.js ├── no-this.js ├── no-throw.js ├── no-unused-expression.js └── no-valueof-field.js └── test ├── no-arguments.js ├── no-class.js ├── no-delete.js ├── no-events.js ├── no-get-set.js ├── no-let.js ├── no-loops.js ├── no-mutating-assign.js ├── no-mutating-methods.js ├── no-mutation.js ├── no-nil.js ├── no-proxy.js ├── no-rest-parameters.js ├── no-this.js ├── no-throw.js ├── no-unused-expression.js ├── no-valueof-field.js └── package.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | - '4' 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). 5 | 6 | ## [Unreleased] 7 | - none 8 | 9 | ## [2.3.0] - 2017-01-01 10 | ### Added 11 | - Added `allowUseStrict` option to [`no-unused-expression`] to allow `'use strict';` statements. 12 | - Use [`eslint-ast-utils`] to replace some logic. 13 | 14 | ### Fixed 15 | - Allow properties named `arguments` in [`no-arguments`]. 16 | - Allow function expressions in [`no-mutating-assign`]. 17 | 18 | ## [2.2.0] - 2016-08-31 19 | - Added [`no-valueof-field`] rule 20 | 21 | ## [2.1.0] - 2016-08-26 22 | ### Added 23 | - Added [`no-arguments`] rule 24 | - Added [`no-rest-parameters`] rule 25 | 26 | ## [2.0.0] - 2016-08-24 27 | ### Removed 28 | - **Breaking**: Removed support for Node < v4 29 | 30 | ### Added 31 | - Added [`no-proxy`] rule 32 | - Added [`no-mutating-methods`] rule 33 | - Added error reports when using `object.__defineSetter__()` or `object.__defineGetter__()` in [`no-get-set`] rule 34 | 35 | ### Fixed 36 | - Fixed error in [`no-mutating-assign`] report message 37 | 38 | ## [1.3.0] - 2016-06-22 39 | ### Added 40 | - Added `allowThis` option to [`no-mutation`] 41 | 42 | ## [1.2.0] - 2016-06-21 43 | ### Added 44 | - Added `exceptions` option to [`no-mutation`] 45 | - Added [`no-events`] rule 46 | - Added [`no-get-set`] rule 47 | 48 | ## [1.1.0] - 2016-06-16 49 | ### Added 50 | - Added [`no-delete`] rule 51 | - Added [`no-nil`] rule 52 | - Added [`no-unused-expression`] rule 53 | 54 | ## 1.0.0 - 2016-06-15 55 | ### Added 56 | - Added [`no-class`] rule 57 | - Added [`no-let`] rule 58 | - Added [`no-loops`] rule 59 | - Added [`no-mutating-assign`] rule 60 | - Added [`no-mutation`] rule 61 | - Added [`no-this`] rule 62 | - Added [`no-throw`] rule 63 | 64 | [Unreleased]: https://github.com/jfmengels/eslint-plugin-fp/compare/v2.3.0...master 65 | [2.3.0]: https://github.com/jfmengels/eslint-plugin-fp/compare/v2.2.0...v2.3.0 66 | [2.2.0]: https://github.com/jfmengels/eslint-plugin-fp/compare/v2.1.0...v2.2.0 67 | [2.1.0]: https://github.com/jfmengels/eslint-plugin-fp/compare/v2.0.0...v2.1.0 68 | [2.0.0]: https://github.com/jfmengels/eslint-plugin-fp/compare/v1.3.0...v2.0.0 69 | [1.3.0]: https://github.com/jfmengels/eslint-plugin-fp/compare/v1.2.0...v1.3.0 70 | [1.2.0]: https://github.com/jfmengels/eslint-plugin-fp/compare/v1.1.0...v1.2.0 71 | [1.1.0]: https://github.com/jfmengels/eslint-plugin-fp/compare/v1.0.0...v1.1.0 72 | 73 | [`eslint-ast-utils`]: https://github.com/jfmengels/eslint-ast-utils 74 | 75 | [`no-arguments`]: ./docs/rules/no-arguments.md 76 | [`no-class`]: ./docs/rules/no-class.md 77 | [`no-delete`]: ./docs/rules/no-delete.md 78 | [`no-events`]: ./docs/rules/no-events.md 79 | [`no-get-set`]: ./docs/rules/no-get-set.md 80 | [`no-let`]: ./docs/rules/no-let.md 81 | [`no-loops`]: ./docs/rules/no-loops.md 82 | [`no-mutating-assign`]: ./docs/rules/no-mutating-assign.md 83 | [`no-mutating-methods`]: ./docs/rules/no-mutating-methods.md 84 | [`no-mutation`]: ./docs/rules/no-mutation.md 85 | [`no-nil`]: ./docs/rules/no-nil.md 86 | [`no-proxy`]: ./docs/rules/no-proxy.md 87 | [`no-rest-parameters`]: ./docs/rules/no-rest-parameters.md 88 | [`no-this`]: ./docs/rules/no-this.md 89 | [`no-throw`]: ./docs/rules/no-throw.md 90 | [`no-unused-expression`]: ./docs/rules/no-unused-expression.md 91 | [`no-valueof-field`]: ./docs/rules/no-valueof-field.md 92 | -------------------------------------------------------------------------------- /docs/rules/no-arguments.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of `arguments` 2 | 3 | [`arguments`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/arguments) is a special variable made implicitly available in functions, which is an object containing the arguments passed to the function call. This is often used to allow any number of parameters to be passed to a function. 4 | 5 | Functional programming works better with known and explicit parameters. Also, having an undefined number of parameters does not work well with currying. 6 | 7 | ### Fail 8 | 9 | ```js 10 | function sum() { 11 | const numbers = Array.prototype.slice.call(arguments); 12 | return numbers.reduce((a, b) => a + b); 13 | } 14 | 15 | sum(1, 2, 3); 16 | ``` 17 | 18 | ### Pass 19 | 20 | ```js 21 | function sum(numbers) { 22 | return numbers.reduce((a, b) => a + b); 23 | } 24 | 25 | sum([1, 2, 3]); 26 | 27 | var args = node.arguments; 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/rules/no-class.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of `class` 2 | 3 | Classes are nice tools to use when programming with the object-oriented paradigm, as they hold internal state and give access to methods on the instances. In functional programming, having stateful objects is more harmful than helpful, and should be replaced by the use of pure functions. 4 | 5 | ### Fail 6 | 7 | ```js 8 | class Polygon { 9 | constructor(height, width) { 10 | this.height = height; 11 | this.width = width; 12 | } 13 | } 14 | ``` 15 | 16 | ### Pass 17 | 18 | ```js 19 | function polygon(height, width) { 20 | return { 21 | height: height, 22 | width: width 23 | }; 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/rules/no-delete.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of `delete` 2 | 3 | `delete` is an operator to remove fields from an object or elements from an array. This purposely mutates data, which is not wanted when doing functional programming. 4 | 5 | ### Fail 6 | 7 | ```js 8 | delete foo; 9 | delete foo.bar; 10 | delete foo[bar]; 11 | ``` 12 | 13 | ### Pass 14 | 15 | ```js 16 | var _ = require('lodash/fp'); 17 | 18 | var fooWithoutBar = _.omit('bar', foo); 19 | var fooWithoutField = _.omit(bar, foo); 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/rules/no-events.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of the `events` module 2 | 3 | The use of `EventEmitter` with the `events` module provided by Node.js promotes implicit side-effects by emitting and listening to events. Instead of `events`, you should prefer activating the wanted effects by calling the functions you wish to use explicitly. 4 | 5 | The rule will simply warn whenever the `events` module is imported. 6 | 7 | ### Fail 8 | 9 | ```js 10 | import EventEmitter from 'events'; 11 | 12 | const EventEmitter = require('events') 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/rules/no-get-set.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of getters and setters 2 | 3 | The use of JavaScript property getter and setters introduces side-effects and confusion. 4 | 5 | ### Fail 6 | 7 | ```js 8 | const person = { 9 | name: 'Some Name', 10 | get age() { 11 | return this._age; 12 | }, 13 | set age(n) { 14 | if (n < 0) { 15 | this._age = 0; 16 | } else if (n > 100) { 17 | this._age = 100; 18 | } else { 19 | this._age = n; 20 | } 21 | }: 20 22 | }; 23 | 24 | person.__defineGetter__('name', function() { 25 | return this.name || 'John Doe'; 26 | }); 27 | 28 | person.__defineSetter__('name', function(name) { 29 | this.name = name.trim(); 30 | }); 31 | ``` 32 | 33 | ### Pass 34 | 35 | ```js 36 | const person = { 37 | name: 'Some Name', 38 | age: 20 39 | }; 40 | 41 | function clamp(n, min, max) { 42 | if (n <= min) { 43 | return min; 44 | } 45 | if (n >= max) { 46 | return max; 47 | } 48 | return n; 49 | } 50 | 51 | function setAge(age, person) { 52 | return Object.assign({}, person, {age: clamp(age, 0, 100)}); 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/rules/no-let.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of `let` 2 | 3 | If you want to program as if your variables are immutable, part of the answer is to not allow your variables to be reassigned. By not allowing the use of `let` and `var`, variables that you declared may not be reassigned. 4 | 5 | This rule does not forbid the use the use of `var`, but you should forbid that by turning on the [`no-var` core rule](http://eslint.org/docs/rules/no-var). 6 | 7 | ### Fail 8 | 9 | ```js 10 | let a = 1; 11 | let b = 2, 12 | c = 3; 13 | let d; 14 | ``` 15 | 16 | ### Pass 17 | 18 | ```js 19 | const a = 1; 20 | const b = 2, 21 | c = 3; 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/rules/no-loops.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of loops 2 | 3 | Loops, such as `for` or `while` loops, work well when using a procedural paradigm. In functional programming, recursion or implementation agnostic operations like `map`, `filter` and `reduce` are preferred. 4 | 5 | ### Fail 6 | 7 | ```js 8 | const result = []; 9 | const elements = [1, 2, 3]; 10 | 11 | for (let i = 0; i < elements.length; i++) { 12 | if (elements[i] > 2) { 13 | result.push(elements[i]); 14 | } 15 | } 16 | 17 | for (element in elements) { 18 | result.push(element * 10); 19 | } 20 | 21 | for (element of elements) { 22 | result.push(element * 10); 23 | } 24 | 25 | while (n < 100) { 26 | result.push(n); 27 | n *= 2; 28 | } 29 | 30 | do { 31 | result.push(n); 32 | n *= 2; 33 | } while (n < 100); 34 | ``` 35 | 36 | ### Pass 37 | 38 | ```js 39 | const elements = [1, 2, 3]; 40 | 41 | const result = elements.filter(element => element > 2); 42 | 43 | const result = elements.map(element => element * 10); 44 | 45 | function doubleThemAll(n) { 46 | if (n >= 100) { 47 | return []; 48 | } 49 | return [n].concat(doubleThemAll(n * 2)); 50 | } 51 | const result = doubleThemAll(n); 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/rules/no-mutating-assign.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of [`Object.assign()`] with a variable as first argument 2 | 3 | [`Object.assign()`] is a method that mutates its first argument. In order to use this method as a non-mutating method, the first element may not be a variable (even if declared using `const`), and should therefore be a static value, such as an object expression. 4 | 5 | ### Fail 6 | 7 | ```js 8 | var a = {foo: 1, bar: 2}; 9 | var b = {bar: 3}; 10 | Object.assign(a, b); 11 | ``` 12 | 13 | ### Pass 14 | 15 | ```js 16 | var a = {foo: 1, bar: 2}; 17 | var b = {bar: 3}; 18 | Object.assign({}, a, b); 19 | Object.assign({foo: 1, bar: 2}, b); 20 | Object.assign(function foo() {}, {propTypes: {}}); 21 | ``` 22 | 23 | [`Object.assign()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign 24 | -------------------------------------------------------------------------------- /docs/rules/no-mutating-methods.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of mutating methods 2 | 3 | If you want to program as if your variables are immutable, you should not use mutating methods of core JavaScript objects, such as `Array.prototype.push`. Due to the lack of static information in JavaScript, it is not possible to know whether a method of an object is indeed the mutating core method or a function with the same name. Therefore, this rule simply reports the use of methods with the following names: `copyWithin`, `pop`, `push`, `reverse`, `shift`, `sort`, `splice`, `unshift`. 4 | 5 | It will also report the use of `Object.defineProperties`, `Object.defineProperty`, `Object.setPrototypeOf`. 6 | 7 | ## Options 8 | 9 | This rule supports the following options: 10 | 11 | `allowedObjects`: Array of names of objects whose methods can be used with no restrictions. This can be useful when using libraries like [`lodash/fp`](https://github.com/lodash/lodash/wiki/FP-Guide) or [`Ramda`](http://ramdajs.com), whose methods will not mutate the arguments. Defaults to an empty array. 12 | If set to `true`, then this rule will not report when assigning to or to a (sub-) property of `exports` or `module.exports`. Note that this will not report when reassigning or overwriting previous exports. 13 | 14 | You can set the options like this: 15 | 16 | ```js 17 | "fp/no-mutating-methods": ["error", { 18 | "allowedObjects": ['_', 'R', 'fp'] 19 | }] 20 | ``` 21 | 22 | ### Fail 23 | 24 | ```js 25 | probableArray.copyWithin(a); 26 | probableArray.pop(); 27 | probableArray.push(value); 28 | probableArray.reverse(); 29 | probableArray.shift(); 30 | probableArray.sort(); 31 | probableArray.splice(0, 1, value); 32 | probableArray.unshift(value); 33 | probableArray.unwatch(); 34 | probableArray.watch(fn); 35 | 36 | Object.defineProperties(object, props); 37 | Object.defineProperty(object, 'prop', descriptor); 38 | Object.setPrototypeOf(object, prototype); 39 | ``` 40 | 41 | ### Pass 42 | 43 | ```js 44 | probableArray.foo(); 45 | probableArray.concat(otherArray); 46 | 47 | Object.keys(object); 48 | 49 | /* eslint fp/no-mutating-methods: ["error", {"allowedObjects": ["_"]}] */ 50 | var _ = require('lodash/fp'); 51 | _.sort(a); 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/rules/no-mutation.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of mutating operators 2 | 3 | If you want to program as if your variables are immutable, part of the answer is to not mutation by re-assigning to a variable or assigning to a property, and not use update operators like `++` or `--`. 4 | 5 | ## Options 6 | 7 | This rule supports the following options: 8 | 9 | - `commonjs`: If set to `true`, then this rule will not report when assigning to or to a (sub-) property of `exports` or `module.exports`. Note that this will not report when reassigning or overwriting previous exports. 10 | 11 | - `allowThis`: If set to `true`, then this rule will not report when assigning to or to a (sub-) property of `this`. 12 | 13 | - `exceptions`: List of objects that describe exceptions to the rule. Each exception should have either or both `object` and `property` field set. 14 | 15 | You can set the options like this: 16 | 17 | ```js 18 | "fp/no-mutation": ["error", { 19 | "commonjs": true, 20 | "allowThis": true, 21 | "exceptions": [ 22 | {"object": "foo", "property": "bar"} 23 | ] 24 | }] 25 | ``` 26 | 27 | ### Fail 28 | 29 | ```js 30 | a = 0; 31 | 32 | a += 10; 33 | a -= 10; 34 | a *= 10; 35 | a /= 10; 36 | a %= 10; 37 | 38 | --a; 39 | ++a; 40 | a--; 41 | a++; 42 | 43 | function foo(a) { 44 | a = a || {}; 45 | } 46 | 47 | exports = {}; 48 | exports.foo = {}; 49 | module.exports = {}; 50 | module.exports.foo = {}; 51 | 52 | function foo(a) { 53 | this.a = a || {}; 54 | } 55 | ``` 56 | 57 | ### Pass 58 | 59 | ```js 60 | var a = 0; 61 | let b = 1; 62 | const c = 2; 63 | 64 | function foo(a={}) {} 65 | 66 | /* eslint fp/no-mutation: ["error", {"commonjs": true}] */ 67 | exports = {}; 68 | exports.foo = {}; 69 | module.exports = {}; 70 | module.exports.foo = {}; 71 | 72 | /* eslint fp/no-mutation: ["error", {"exceptions": [{"property": "propTypes"}]}] */ 73 | function Component(props) { 74 | // ... 75 | } 76 | 77 | Component.propTypes = { 78 | // ... 79 | }; 80 | 81 | /* eslint fp/no-mutation: ["error", {"allowThis": true}] */ 82 | function foo(a) { 83 | this.a = a || {}; 84 | } 85 | ``` 86 | -------------------------------------------------------------------------------- /docs/rules/no-nil.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of `null` and `undefined` 2 | 3 | Accessing properties or calling methods of a variable with value `null` or `undefined` is source of a lot of errors. The use of those values is hard to ignore in JavaScript, but avoiding their explicit use in your code may help restrain the number of occurrences of this kind of error. 4 | 5 | This rule: 6 | - Prohibits the use of `null` and `undefined` in any way, except in a comparison that directly compares with either value 7 | - Enforces that all variables are initialized, as they would otherwise evaluate to `undefined` 8 | - Enforces that every function returns a value, that is not either value 9 | 10 | ### Fail 11 | 12 | ```js 13 | let a; // Equivalent to `let a = undefined;` 14 | let a = null; 15 | 16 | // No return statement, so it will return `undefined` 17 | function foo() {} 18 | function foo() { 19 | a + b; 20 | } 21 | 22 | function foo() { 23 | return null; 24 | } 25 | 26 | function foo(a=null) { 27 | return a; 28 | } 29 | 30 | function foo() { 31 | if (bar) { 32 | return; 33 | } 34 | return 1; 35 | } 36 | ``` 37 | 38 | ### Pass 39 | 40 | ```js 41 | const a = 1; 42 | 43 | if (value === null || value === undefined) {} 44 | 45 | function foo(a=1) { 46 | return a; 47 | } 48 | 49 | function foo() { 50 | if (bar) { 51 | return 2; 52 | } 53 | return 1; 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/rules/no-proxy.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of `Proxy` 2 | 3 | Proxies add a hidden layer of side-effects when accessing properties of objects or elements of arrays. 4 | 5 | ### Fail 6 | 7 | ```js 8 | const handler = { 9 | get(target, key) { 10 | return Math.min(target[key], 0); 11 | } 12 | }; 13 | const object = new Proxy(variable, handler); 14 | object.a; 15 | ``` 16 | 17 | ### Pass 18 | 19 | ```js 20 | function positiveProperty(target, key) { 21 | return Math.min(target[key], 0); 22 | } 23 | positiveProperty(object, 'a'); 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/rules/no-rest-parameters.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of rest parameters 2 | 3 | [Rest parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters) can be used to allow any number of parameters to be passed to a function. 4 | 5 | Functional programming works better with known and explicit parameters. Having an undefined number of parameters does not work well with currying. 6 | 7 | ### Fail 8 | 9 | ```js 10 | function sum(...numbers) { 11 | return numbers.reduce((a, b) => a + b); 12 | } 13 | 14 | sum(1, 2, 3); 15 | ``` 16 | 17 | ### Pass 18 | 19 | ```js 20 | function sum(numbers) { 21 | return numbers.reduce((a, b) => a + b); 22 | } 23 | 24 | sum([1, 2, 3]); 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/rules/no-this.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of `this` 2 | 3 | When doing functional programming, you want to avoid having stateful objects and instead use simple JavaScript objects. 4 | 5 | ### Fail 6 | 7 | ```js 8 | const object = { 9 | numbers: [1, 2, 3], 10 | sum: function() { 11 | return this.numbers.reduce((a, b) => a + b, 0); 12 | } 13 | } 14 | 15 | object.sum(); 16 | ``` 17 | 18 | ### Pass 19 | 20 | ```js 21 | function sum(numbers) { 22 | return numbers.reduce((a, b) => a + b); 23 | } 24 | 25 | sum([1, 2, 3]); 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/rules/no-throw.md: -------------------------------------------------------------------------------- 1 | # Forbid the use of `throw` 2 | 3 | Instead of throwing an error, return an object containing the description of the error, or use a tool like `Either` that you can find in some functional libraries. 4 | 5 | ### Fail 6 | 7 | ```js 8 | function throwAnError() { 9 | throw new Error('some error message'); 10 | } 11 | ``` 12 | 13 | ### Pass 14 | 15 | ```js 16 | function returnAnError() { 17 | return Either.Left(new Error('some error message')); 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/rules/no-unused-expression.md: -------------------------------------------------------------------------------- 1 | # Enforce that an expression gets used 2 | 3 | In functional programming, methods do not mutate any values or cause side-effects, and it is therefore useless to call a method without using its result. The result should be assigned to a variable, passed as a parameter of another function, etc. Unused literals are reported too as they represent dead code. 4 | 5 | ## Options 6 | 7 | This rule supports the following options: 8 | 9 | - `allowUseStrict`: If set to `true`, will allow `'use strict';` statements. 10 | 11 | You can set the options like this: 12 | 13 | ```js 14 | "fp/no-unused-expression": ["error", {"allowUseStrict": true }] 15 | ``` 16 | 17 | ### Fail 18 | 19 | ```js 20 | 1 + 2; 21 | 22 | foo(); 23 | 24 | Object.assign(a, b); 25 | 26 | function foo(a, b) { 27 | a + b; 28 | } 29 | 30 | 'use strict'; 31 | ``` 32 | 33 | ### Pass 34 | 35 | ```js 36 | const sum = 1 + 2; 37 | 38 | const result = foo(); 39 | 40 | const result = foo(Object.assign(a, b)); 41 | 42 | function foo(a, b) { 43 | return a + b; 44 | } 45 | 46 | const foo = (a, b) => a + b; 47 | 48 | /* eslint fp/no-unused-expression: ["error", {"allowUseStrict": true }] */ 49 | 'use strict'; 50 | ``` 51 | -------------------------------------------------------------------------------- /docs/rules/no-valueof-field.md: -------------------------------------------------------------------------------- 1 | # Forbid the creation of `valueOf` fields 2 | 3 | The [`valueOf` field](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf) in objects is used to implicitly convert an object to a primitive value. 4 | Having this field overridden can have implicit unwanted results, or introduce side-effects in code that looks pure, as demonstrated in [this article](http://staltz.com/is-your-javascript-function-actually-pure.html). 5 | 6 | Here's an example of a use of `valueOf`: 7 | 8 | ```js 9 | const object1 = { 10 | value: 15, 11 | valueOf: function() { return this.value; } 12 | }; 13 | const object2 = { 14 | value: 25, 15 | valueOf: function() { return this.value; } 16 | }; 17 | object1 + object2 18 | // => 40 19 | ``` 20 | 21 | And its more explicit version without the use of `valueOf`: 22 | 23 | ```js 24 | const addValueOf = (a, b) => a.value + b.value; 25 | 26 | const object1 = { 27 | value: 15 28 | }; 29 | const object2 = { 30 | value: 25 31 | }; 32 | 33 | addValueOf(object1, object2) 34 | // => 40 35 | ``` 36 | 37 | This rule reports any explicit assignment or creation of a `valueOf` field in an object. 38 | 39 | ### Fail 40 | 41 | ```js 42 | const object = { valueOf: Math.random }; 43 | const object = { valueOf }; 44 | const object = { ["valueOf"]: Math.random }; 45 | object.valueOf = Math.random; 46 | object.prototype.valueOf = Math.random; 47 | ``` 48 | 49 | ### Pass 50 | 51 | ```js 52 | const object = { value: 2 }; 53 | object.value = 2; 54 | ``` 55 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const reqAll = require('req-all'); 4 | const createIndex = require('create-eslint-index'); 5 | 6 | const rules = reqAll('rules', {camelize: false}); 7 | 8 | const externalRecommendedRules = { 9 | 'no-var': 'error' 10 | }; 11 | 12 | const internalRecommendedRules = createIndex.createConfig({ 13 | plugin: 'fp', 14 | field: 'meta.docs.recommended' 15 | }, rules); 16 | 17 | module.exports = { 18 | rules, 19 | configs: { 20 | recommended: { 21 | rules: Object.assign({}, internalRecommendedRules, externalRecommendedRules) 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Jeroen Engels (github.com/jfmengels) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-fp", 3 | "version": "2.3.0", 4 | "description": "ESLint rules for functional programming", 5 | "license": "MIT", 6 | "repository": "jfmengels/eslint-plugin-fp", 7 | "author": { 8 | "name": "Jeroen Engels", 9 | "email": "jfm.engels@gmail.com", 10 | "url": "github.com/jfmengels" 11 | }, 12 | "engines": { 13 | "node": ">=4.0.0" 14 | }, 15 | "scripts": { 16 | "test": "xo && nyc ava", 17 | "update-md": "inject-in-tag ./rule-description.js readme.md" 18 | }, 19 | "files": [ 20 | "index.js", 21 | "rules" 22 | ], 23 | "keywords": [ 24 | "eslint", 25 | "plugin", 26 | "eslint-plugin", 27 | "eslintplugin", 28 | "fp", 29 | "FP", 30 | "function", 31 | "functional", 32 | "programming", 33 | "lodash", 34 | "ramda", 35 | "monad", 36 | "immutable", 37 | "function", 38 | "functions", 39 | "method", 40 | "methods" 41 | ], 42 | "dependencies": { 43 | "create-eslint-index": "^1.0.0", 44 | "eslint-ast-utils": "^1.0.0", 45 | "lodash": "^4.13.1", 46 | "req-all": "^0.1.0" 47 | }, 48 | "devDependencies": { 49 | "ava": "^0.17.0", 50 | "eslint-ava-rule-tester": "^0.1.1", 51 | "inject-in-tag": "^1.1.1", 52 | "nyc": "^6.4.4", 53 | "pify": "^2.3.0", 54 | "xo": "^0.15.1" 55 | }, 56 | "peerDependencies": { 57 | "eslint": ">=3" 58 | }, 59 | "xo": { 60 | "esnext": true, 61 | "space": 2 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-fp [![Build Status](https://travis-ci.org/jfmengels/eslint-plugin-fp.svg?branch=master)](https://travis-ci.org/jfmengels/eslint-plugin-fp) 2 | 3 | ESLint rules for functional programming 4 | 5 | 6 | ## Install 7 | 8 | ``` 9 | $ npm install --save-dev eslint eslint-plugin-fp 10 | ``` 11 | 12 | ## Usage 13 | 14 | Configure it in `package.json`. 15 | 16 | 17 | ```json 18 | { 19 | "name": "my-awesome-project", 20 | "eslintConfig": { 21 | "env": { 22 | "es6": true 23 | }, 24 | "plugins": [ 25 | "fp" 26 | ], 27 | "rules": { 28 | "fp/no-arguments": "error", 29 | "fp/no-class": "error", 30 | "fp/no-delete": "error", 31 | "fp/no-events": "error", 32 | "fp/no-get-set": "error", 33 | "fp/no-let": "error", 34 | "fp/no-loops": "error", 35 | "fp/no-mutating-assign": "error", 36 | "fp/no-mutating-methods": "error", 37 | "fp/no-mutation": "error", 38 | "fp/no-nil": "error", 39 | "fp/no-proxy": "error", 40 | "fp/no-rest-parameters": "error", 41 | "fp/no-this": "error", 42 | "fp/no-throw": "error", 43 | "fp/no-unused-expression": "error", 44 | "fp/no-valueof-field": "error", 45 | "no-var": "error" 46 | } 47 | } 48 | } 49 | ``` 50 | 51 | 52 | 53 | ## Rules 54 | 55 | 56 | - [no-arguments](docs/rules/no-arguments.md) - Forbid the use of `arguments`. 57 | - [no-class](docs/rules/no-class.md) - Forbid the use of `class`. 58 | - [no-delete](docs/rules/no-delete.md) - Forbid the use of `delete`. 59 | - [no-events](docs/rules/no-events.md) - Forbid the use of the `events` module. 60 | - [no-get-set](docs/rules/no-get-set.md) - Forbid the use of getters and setters. 61 | - [no-let](docs/rules/no-let.md) - Forbid the use of `let`. 62 | - [no-loops](docs/rules/no-loops.md) - Forbid the use of loops. 63 | - [no-mutating-assign](docs/rules/no-mutating-assign.md) - Forbid the use of [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) with a variable as first argument. 64 | - [no-mutating-methods](docs/rules/no-mutating-methods.md) - Forbid the use of mutating methods. 65 | - [no-mutation](docs/rules/no-mutation.md) - Forbid the use of mutating operators. 66 | - [no-nil](docs/rules/no-nil.md) - Forbid the use of `null` and `undefined`. 67 | - [no-proxy](docs/rules/no-proxy.md) - Forbid the use of `Proxy`. 68 | - [no-rest-parameters](docs/rules/no-rest-parameters.md) - Forbid the use of rest parameters. 69 | - [no-this](docs/rules/no-this.md) - Forbid the use of `this`. 70 | - [no-throw](docs/rules/no-throw.md) - Forbid the use of `throw`. 71 | - [no-unused-expression](docs/rules/no-unused-expression.md) - Enforce that an expression gets used. 72 | - [no-valueof-field](docs/rules/no-valueof-field.md) - Forbid the creation of `valueOf` fields. 73 | 74 | 75 | 76 | ## Recommended configuration 77 | 78 | This plugin exports a [`recommended` configuration](index.js) that enforces good practices. 79 | 80 | To enable this configuration, use the `extends` property in your `package.json`. 81 | 82 | ```json 83 | { 84 | "name": "my-awesome-project", 85 | "eslintConfig": { 86 | "plugins": [ 87 | "fp" 88 | ], 89 | "extends": "plugin:fp/recommended" 90 | } 91 | } 92 | ``` 93 | 94 | See [ESLint documentation](http://eslint.org/docs/user-guide/configuring#extending-configuration-files) for more information about extending configuration files. 95 | 96 | MIT © [Jeroen Engels](https://github.com/jfmengels) 97 | -------------------------------------------------------------------------------- /rule-description.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const reqAll = require('req-all'); 4 | const createIndex = require('create-eslint-index'); 5 | const index = require('./'); 6 | 7 | const rules = reqAll('rules', {camelize: false}); 8 | 9 | const settings = { 10 | descriptionField: 'meta.docs.description', 11 | docPath: 'docs/rules' 12 | }; 13 | 14 | const exampleConfiguration = { 15 | name: 'my-awesome-project', 16 | eslintConfig: { 17 | env: { 18 | es6: true 19 | }, 20 | plugins: [ 21 | 'fp' 22 | ], 23 | rules: index.configs.recommended.rules 24 | } 25 | }; 26 | 27 | module.exports = { 28 | RULES: `\n${createIndex.createRulesDescription(settings, rules)}\n\n`, 29 | // eslint-disable-next-line prefer-template 30 | EXAMPLE_CONFIGURATION: '\n```json\n' + JSON.stringify(exampleConfiguration, null, 2) + '\n```\n' 31 | }; 32 | -------------------------------------------------------------------------------- /rules/no-arguments.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isProperty(node) { 4 | return Boolean( 5 | node && 6 | node.parent && 7 | node.parent.type === 'Property' && 8 | node.parent.key === node 9 | ); 10 | } 11 | 12 | function isPropertyAccess(node) { 13 | return Boolean( 14 | node && 15 | node.parent && 16 | node.parent.type === 'MemberExpression' && 17 | node.parent.property === node 18 | ); 19 | } 20 | 21 | const create = function (context) { 22 | return { 23 | Identifier(node) { 24 | if (node.name === 'arguments' && !isProperty(node) && !isPropertyAccess(node)) { 25 | context.report({ 26 | node, 27 | message: 'Unallowed use of `arguments`. Use regular function arguments instead' 28 | }); 29 | } 30 | } 31 | }; 32 | }; 33 | 34 | module.exports = { 35 | create, 36 | meta: { 37 | docs: { 38 | description: 'Forbid the use of `arguments`.', 39 | recommended: 'error', 40 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-arguments.md' 41 | } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /rules/no-class.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const create = function (context) { 4 | function report(node) { 5 | context.report({ 6 | node, 7 | message: 'Unallowed use of `class`. Use functions instead' 8 | }); 9 | } 10 | 11 | return { 12 | ClassDeclaration: report, 13 | ClassExpression: report 14 | }; 15 | }; 16 | 17 | module.exports = { 18 | create, 19 | meta: { 20 | docs: { 21 | description: 'Forbid the use of `class`.', 22 | recommended: 'error', 23 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-class.md' 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /rules/no-delete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const create = function (context) { 4 | return { 5 | UnaryExpression(node) { 6 | if (node.operator === 'delete') { 7 | context.report({ 8 | node, 9 | message: 'Unallowed use of `delete`' 10 | }); 11 | } 12 | } 13 | }; 14 | }; 15 | 16 | module.exports = { 17 | create, 18 | meta: { 19 | docs: { 20 | description: 'Forbid the use of `delete`.', 21 | recommended: 'error', 22 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-delete.md' 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /rules/no-events.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const astUtils = require('eslint-ast-utils'); 4 | 5 | function report(context, node) { 6 | context.report({ 7 | node, 8 | message: 'Unallowed use of `events`' 9 | }); 10 | } 11 | 12 | const create = function (context) { 13 | return { 14 | ImportDeclaration(node) { 15 | if (node.source.value === 'events') { 16 | report(context, node); 17 | } 18 | }, 19 | CallExpression(node) { 20 | if (astUtils.isStaticRequire(node) && node.arguments[0].value === 'events') { 21 | report(context, node); 22 | } 23 | } 24 | }; 25 | }; 26 | 27 | module.exports = { 28 | create, 29 | meta: { 30 | docs: { 31 | description: 'Forbid the use of the `events` module.', 32 | recommended: 'error', 33 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-events.md' 34 | } 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /rules/no-get-set.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mutatingMethods = [ 4 | '__defineGetter__', 5 | '__defineSetter__' 6 | ]; 7 | 8 | function getNameIfPropertyIsIdentifier(property) { 9 | return property.type === 'Identifier' && 10 | mutatingMethods.indexOf(property.name) !== -1 && 11 | property.name; 12 | } 13 | 14 | function getNameIfPropertyIsLiteral(property) { 15 | return property.type === 'Literal' && 16 | mutatingMethods.indexOf(property.value) !== -1 && 17 | property.value; 18 | } 19 | 20 | const create = function (context) { 21 | return { 22 | Property(node) { 23 | if (node.kind === 'get' || node.kind === 'set') { 24 | context.report({ 25 | node, 26 | message: `Unallowed use of \`${node.kind}\`` 27 | }); 28 | } 29 | }, 30 | CallExpression(node) { 31 | if (node.callee.type !== 'MemberExpression' || node.callee.object.type !== 'Identifier') { 32 | return; 33 | } 34 | 35 | const name = getNameIfPropertyIsIdentifier(node.callee.property) || getNameIfPropertyIsLiteral(node.callee.property); 36 | if (name) { 37 | context.report({ 38 | node, 39 | message: name === '__defineGetter__' ? 40 | 'Unallowed use of a getter using `__defineGetter__`' : 41 | 'Unallowed use of a setter using `__defineSetter__`' 42 | }); 43 | } 44 | } 45 | }; 46 | }; 47 | 48 | module.exports = { 49 | create, 50 | meta: { 51 | docs: { 52 | description: 'Forbid the use of getters and setters.', 53 | recommended: 'error', 54 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-get-set.md' 55 | } 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /rules/no-let.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const create = function (context) { 4 | return { 5 | VariableDeclaration(node) { 6 | if (node.kind === 'let') { 7 | context.report({ 8 | node, 9 | message: 'Unallowed use of `let`. Use `const` instead' 10 | }); 11 | } 12 | } 13 | }; 14 | }; 15 | 16 | module.exports = { 17 | create, 18 | meta: { 19 | docs: { 20 | description: 'Forbid the use of `let`.', 21 | recommended: 'error', 22 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-let.md' 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /rules/no-loops.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const create = function (context) { 4 | function reportForLoop(node) { 5 | context.report({ 6 | node, 7 | message: 'Unallowed use of `for` loop' 8 | }); 9 | } 10 | 11 | function reportWhileLoop(node) { 12 | context.report({ 13 | node, 14 | message: 'Unallowed use of `while` loop. Use recursion instead' 15 | }); 16 | } 17 | 18 | return { 19 | ForStatement: reportForLoop, 20 | ForInStatement: reportForLoop, 21 | ForOfStatement: reportForLoop, 22 | 23 | WhileStatement: reportWhileLoop, 24 | DoWhileStatement: reportWhileLoop 25 | }; 26 | }; 27 | 28 | module.exports = { 29 | create, 30 | meta: { 31 | docs: { 32 | description: 'Forbid the use of loops.', 33 | recommended: 'error', 34 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-loops.md' 35 | } 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /rules/no-mutating-assign.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash/fp'); 4 | 5 | const isObjectAssign = _.matches({ 6 | type: 'MemberExpression', 7 | object: { 8 | type: 'Identifier', 9 | name: 'Object' 10 | }, 11 | property: { 12 | type: 'Identifier', 13 | name: 'assign' 14 | } 15 | }); 16 | 17 | const isObjectExpression = _.flow( 18 | _.property('type'), 19 | _.includes(_, ['ObjectExpression', 'ArrayExpression']) 20 | ); 21 | 22 | const isObjectCreateNull = _.matches({ 23 | type: 'CallExpression', 24 | callee: { 25 | type: 'MemberExpression', 26 | object: { 27 | type: 'Identifier', 28 | name: 'Object' 29 | }, 30 | property: { 31 | type: 'Identifier', 32 | name: 'create' 33 | } 34 | }, 35 | arguments: [{ 36 | type: 'Literal', 37 | value: null 38 | }] 39 | }); 40 | 41 | const isFunctionExpression = _.flow( 42 | _.property('type'), 43 | _.includes(_, ['FunctionExpression', 'ArrowFunctionExpression']) 44 | ); 45 | 46 | function isAllowedFirstArgument(arg) { 47 | return isObjectExpression(arg) || isObjectCreateNull(arg) || isFunctionExpression(arg); 48 | } 49 | 50 | const create = function (context) { 51 | return { 52 | CallExpression(node) { 53 | if (isObjectAssign(node.callee) && !isAllowedFirstArgument(node.arguments[0])) { 54 | context.report({ 55 | node, 56 | message: 'Unallowed use of mutating `Object.assign`' 57 | }); 58 | } 59 | } 60 | }; 61 | }; 62 | 63 | module.exports = { 64 | create, 65 | meta: { 66 | docs: { 67 | description: 'Forbid the use of [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) with a variable as first argument.', 68 | recommended: 'error', 69 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-mutating-assign.md' 70 | } 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /rules/no-mutating-methods.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mutatingMethods = [ 4 | 'copyWithin', 5 | 'pop', 6 | 'push', 7 | 'reverse', 8 | 'shift', 9 | 'sort', 10 | 'splice', 11 | 'unshift', 12 | 'unwatch', 13 | 'watch' 14 | ]; 15 | 16 | const mutatingObjectMethods = [ 17 | 'defineProperties', 18 | 'defineProperty', 19 | 'setPrototypeOf' 20 | ]; 21 | 22 | function getNameIfPropertyIsIdentifier(property) { 23 | return property.type === 'Identifier' && 24 | mutatingMethods.indexOf(property.name) !== -1 && 25 | property.name; 26 | } 27 | 28 | function getNameIfPropertyIsLiteral(property) { 29 | return property.type === 'Literal' && 30 | mutatingMethods.indexOf(property.value) !== -1 && 31 | property.value; 32 | } 33 | 34 | const create = function (context) { 35 | const options = context.options[0] || {}; 36 | const allowedObjects = options.allowedObjects || []; 37 | 38 | return { 39 | CallExpression(node) { 40 | if (node.callee.type !== 'MemberExpression') { 41 | return; 42 | } 43 | 44 | if (node.callee.object.type === 'Identifier' && allowedObjects.indexOf(node.callee.object.name) !== -1) { 45 | return; 46 | } 47 | 48 | if (node.callee.object.name === 'Object') { 49 | if (mutatingObjectMethods.indexOf(node.callee.property.name) !== -1) { 50 | context.report({ 51 | node, 52 | message: `The use of method \`Object.${node.callee.property.name}\` is not allowed as it will mutate its arguments` 53 | }); 54 | } 55 | return; 56 | } 57 | 58 | const name = getNameIfPropertyIsIdentifier(node.callee.property) || getNameIfPropertyIsLiteral(node.callee.property); 59 | if (name) { 60 | context.report({ 61 | node, 62 | message: `The use of method \`${name}\` is not allowed as it might be a mutating method` 63 | }); 64 | return; 65 | } 66 | } 67 | }; 68 | }; 69 | 70 | const schema = [{ 71 | type: 'object', 72 | properties: { 73 | allowedObjects: { 74 | type: 'array', 75 | items: { 76 | type: 'string' 77 | } 78 | } 79 | } 80 | }]; 81 | 82 | module.exports = { 83 | create, 84 | schema, 85 | meta: { 86 | docs: { 87 | description: 'Forbid the use of mutating methods.', 88 | recommended: 'error', 89 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-mutating-methods.md' 90 | } 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /rules/no-mutation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash/fp'); 4 | 5 | const isModuleExports = _.matches({ 6 | type: 'MemberExpression', 7 | object: { 8 | type: 'Identifier', 9 | name: 'module' 10 | }, 11 | property: { 12 | type: 'Identifier', 13 | name: 'exports' 14 | } 15 | }); 16 | 17 | const isExports = _.matches({ 18 | type: 'Identifier', name: 'exports' 19 | }); 20 | 21 | function isModuleExportsMemberExpression(node) { 22 | return _.overSome([ 23 | isExports, 24 | isModuleExports, 25 | function (node) { 26 | return node.type === 'MemberExpression' && isModuleExportsMemberExpression(node.object); 27 | } 28 | ])(node); 29 | } 30 | 31 | const isCommonJsExport = _.flow( 32 | _.property('left'), 33 | _.overSome([ 34 | isExports, 35 | isModuleExports, 36 | isModuleExportsMemberExpression 37 | ]) 38 | ); 39 | 40 | function errorMessage(isCommonJs) { 41 | const baseMessage = 'Unallowed reassignment'; 42 | return baseMessage + (isCommonJs ? '. You may want to activate the `commonjs` option for this rule' : ''); 43 | } 44 | 45 | function makeException(exception) { 46 | if (!exception.object && !exception.property) { 47 | return _.stubFalse; 48 | } 49 | let query = {type: 'MemberExpression'}; 50 | if (exception.object) { 51 | query = _.assign(query, {object: {type: 'Identifier', name: exception.object}}); 52 | } 53 | if (exception.property) { 54 | query = _.assign(query, {property: {type: 'Identifier', name: exception.property}}); 55 | } 56 | return _.matches(query); 57 | } 58 | 59 | function isExempted(exceptions, node) { 60 | if (node.type !== 'MemberExpression') { 61 | return false; 62 | } 63 | const matches = exceptions.some(matcher => matcher(node)); 64 | return matches || 65 | (node.object.type === 'MemberExpression' && isExempted(exceptions, node.object)); 66 | } 67 | 68 | const create = function (context) { 69 | const options = context.options[0] || {}; 70 | const acceptCommonJs = options.commonjs; 71 | const exceptions = _.map(makeException, options.exceptions); 72 | if (options.allowThis) { 73 | exceptions.push(_.matches({type: 'MemberExpression', object: {type: 'ThisExpression'}})); 74 | } 75 | return { 76 | AssignmentExpression(node) { 77 | const isCommonJs = isCommonJsExport(node); 78 | if ((isCommonJs && acceptCommonJs) || isExempted(exceptions, node.left)) { 79 | return; 80 | } 81 | context.report({ 82 | node, 83 | message: errorMessage(isCommonJs) 84 | }); 85 | }, 86 | UpdateExpression(node) { 87 | context.report({ 88 | node, 89 | message: `Unallowed use of \`${node.operator}\` operator` 90 | }); 91 | } 92 | }; 93 | }; 94 | 95 | const schema = [{ 96 | type: 'object', 97 | properties: { 98 | commonjs: { 99 | type: 'boolean' 100 | }, 101 | allowThis: { 102 | type: 'boolean' 103 | }, 104 | exceptions: { 105 | type: 'array', 106 | items: { 107 | type: 'object', 108 | properties: { 109 | object: { 110 | type: 'string' 111 | }, 112 | property: { 113 | type: 'string' 114 | } 115 | } 116 | } 117 | } 118 | } 119 | }]; 120 | 121 | module.exports = { 122 | create, 123 | meta: { 124 | schema, 125 | docs: { 126 | description: 'Forbid the use of mutating operators.', 127 | recommended: 'error', 128 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-mutation.md' 129 | } 130 | } 131 | }; 132 | -------------------------------------------------------------------------------- /rules/no-nil.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash/fp'); 4 | 5 | function isComparison(node) { 6 | return node.parent && 7 | node.parent.type === 'BinaryExpression' && 8 | _.includes(node.parent.operator, ['==', '!=', '===', '!==']); 9 | } 10 | 11 | function reportUseOutsideOfComparison(context, node) { 12 | if (!isComparison(node)) { 13 | context.report({ 14 | node, 15 | message: 'Unallowed use of `null` or `undefined`' 16 | }); 17 | } 18 | } 19 | 20 | const endsWithReturnStatement = _.flow( 21 | _.last, 22 | _.matches({type: 'ReturnStatement'}) 23 | ); 24 | 25 | function reportFunctions(context, node) { 26 | if (node.body.type === 'BlockStatement' && 27 | !endsWithReturnStatement(node.body.body) 28 | ) { 29 | context.report({ 30 | node, 31 | message: 'Function must end with a return statement, so that it doesn\'t return `undefined`' 32 | }); 33 | } 34 | } 35 | 36 | const create = function (context) { 37 | const reportFunc = _.partial(reportFunctions, [context]); 38 | return { 39 | Literal(node) { 40 | if (node.value === null) { 41 | reportUseOutsideOfComparison(context, node); 42 | } 43 | }, 44 | Identifier(node) { 45 | if (node.name === 'undefined') { 46 | reportUseOutsideOfComparison(context, node); 47 | } 48 | }, 49 | VariableDeclarator(node) { 50 | if (node.init === null) { 51 | context.report({ 52 | node, 53 | message: 'Variable must be initialized, so that it doesn\'t evaluate to `undefined`' 54 | }); 55 | } 56 | }, 57 | ReturnStatement(node) { 58 | if (node.argument === null) { 59 | context.report({ 60 | node, 61 | message: 'Return statement must return an explicit value, so that it doesn\'t evaluate to `undefined`' 62 | }); 63 | } 64 | }, 65 | ArrowFunctionExpression: reportFunc, 66 | FunctionDeclaration: reportFunc, 67 | FunctionExpression: reportFunc 68 | }; 69 | }; 70 | 71 | module.exports = { 72 | create, 73 | meta: { 74 | docs: { 75 | description: 'Forbid the use of `null` and `undefined`.', 76 | recommended: 'error', 77 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-nil.md' 78 | } 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /rules/no-proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const create = function (context) { 4 | return { 5 | Identifier(node) { 6 | if (node.name === 'Proxy') { 7 | context.report({ 8 | node, 9 | message: 'Unallowed use of `Proxy`' 10 | }); 11 | } 12 | } 13 | }; 14 | }; 15 | 16 | module.exports = { 17 | create, 18 | meta: { 19 | docs: { 20 | description: 'Forbid the use of `Proxy`.', 21 | recommended: 'error', 22 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-proxy.md' 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /rules/no-rest-parameters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const create = function (context) { 4 | return { 5 | RestElement(node) { 6 | context.report({ 7 | node, 8 | message: 'Unallowed use of rest parameters. Use regular function arguments instead' 9 | }); 10 | } 11 | }; 12 | }; 13 | 14 | module.exports = { 15 | create, 16 | meta: { 17 | docs: { 18 | description: 'Forbid the use of rest parameters.', 19 | recommended: 'error', 20 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-rest-parameters.md' 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /rules/no-this.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const create = function (context) { 4 | return { 5 | ThisExpression(node) { 6 | context.report({ 7 | node, 8 | message: 'Unallowed use of `this`' 9 | }); 10 | } 11 | }; 12 | }; 13 | 14 | module.exports = { 15 | create, 16 | meta: { 17 | docs: { 18 | description: 'Forbid the use of `this`.', 19 | recommended: 'error', 20 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-this.md' 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /rules/no-throw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const create = function (context) { 4 | return { 5 | ThrowStatement(node) { 6 | context.report({ 7 | node, 8 | message: 'Unallowed use of `throw`' 9 | }); 10 | } 11 | }; 12 | }; 13 | 14 | module.exports = { 15 | create, 16 | meta: { 17 | docs: { 18 | description: 'Forbid the use of `throw`.', 19 | recommended: 'error', 20 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-throw.md' 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /rules/no-unused-expression.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash/fp'); 4 | 5 | const hasSideEffect = _.overSome([ 6 | {type: 'AssignmentExpression'}, 7 | {type: 'UpdateExpression', operator: '++'}, 8 | {type: 'UpdateExpression', operator: '--'}, 9 | {type: 'UnaryExpression', operator: 'delete'} 10 | ]); 11 | 12 | const isUseStrictStatement = _.matches( 13 | {type: 'Literal', value: 'use strict'} 14 | ); 15 | 16 | const isSuperCall = _.matches({ 17 | type: 'CallExpression', 18 | callee: { 19 | type: 'Super' 20 | } 21 | }); 22 | 23 | const report = (context, node) => { 24 | context.report({ 25 | node, 26 | message: 'Unused expression' 27 | }); 28 | }; 29 | 30 | const create = function (context) { 31 | const options = context.options[0] || {}; 32 | const allowUseStrict = options.allowUseStrict; 33 | return { 34 | ExpressionStatement(node) { 35 | if (hasSideEffect(node.expression) || 36 | (isUseStrictStatement(node.expression) && allowUseStrict) 37 | ) { 38 | return; 39 | } 40 | if (isSuperCall(node.expression)) { 41 | return; 42 | } 43 | report(context, node); 44 | }, 45 | SequenceExpression(node) { 46 | if (!node.parent || node.parent.type !== 'ExpressionStatement') { // Avoid duplicate errors 47 | report(context, node); 48 | } 49 | } 50 | }; 51 | }; 52 | 53 | const schema = [{ 54 | type: 'object', 55 | properties: { 56 | allowUseStrict: { 57 | type: 'boolean' 58 | } 59 | } 60 | }]; 61 | 62 | module.exports = { 63 | create, 64 | schema, 65 | meta: { 66 | docs: { 67 | description: 'Enforce that an expression gets used.', 68 | recommended: 'error', 69 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-unused-expression.md' 70 | } 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /rules/no-valueof-field.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isValueOfProperty(node) { 4 | return (node.key.type === 'Identifier' && node.key.name === 'valueOf') || 5 | (node.key.type === 'Literal' && node.key.value === 'valueOf'); 6 | } 7 | 8 | function report(context, node) { 9 | context.report({ 10 | node, 11 | message: 'Unallowed use of `valueOf` field' 12 | }); 13 | } 14 | 15 | const create = function (context) { 16 | return { 17 | Property(node) { 18 | if (isValueOfProperty(node)) { 19 | report(context, node); 20 | } 21 | }, 22 | AssignmentExpression(node) { 23 | if (node.left.type === 'MemberExpression' && 24 | node.left.property.type === 'Identifier' && 25 | node.left.property.name === 'valueOf' 26 | ) { 27 | report(context, node.left.property); 28 | } 29 | } 30 | }; 31 | }; 32 | 33 | module.exports = { 34 | create, 35 | meta: { 36 | docs: { 37 | description: 'Forbid the creation of `valueOf` fields.', 38 | recommended: 'error', 39 | url: 'https://github.com/jfmengels/eslint-plugin-fp/tree/master/docs/rules/no-valueof-field.md' 40 | } 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /test/no-arguments.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-arguments'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-arguments', 16 | message: 'Unallowed use of `arguments`. Use regular function arguments instead' 17 | }; 18 | 19 | ruleTester.run('no-arguments', rule, { 20 | valid: [ 21 | 'function foo() {}', 22 | 'function foo(a, b) {}', 23 | 'function foo(a, b) { return a + b; }', 24 | 'var foo = (a, b) => a + b;', 25 | 'function foo(...args) {}', 26 | 'function foo(...args) {}', 27 | 'function foo() { console.log(argument); }', 28 | 'function foo() { console.log(node.arguments); }', 29 | 'node.arguments', 30 | 'var obj = { arguments: [] }' 31 | ], 32 | invalid: [ 33 | { 34 | code: 'function foo() { console.log(arguments); }', 35 | errors: [error] 36 | }, 37 | { 38 | code: 'function foo() { console.log(arguments[0]); }', 39 | errors: [error] 40 | }, 41 | { 42 | code: 'function foo() { console.log(arguments.node); }', 43 | errors: [error] 44 | }, 45 | { 46 | code: 'function foo() { console.log({value: arguments}); }', 47 | errors: [error] 48 | }, 49 | { 50 | code: 'function foo() { var args = Array.prototype.slice.call(arguments); }', 51 | errors: [error] 52 | } 53 | ] 54 | }); 55 | -------------------------------------------------------------------------------- /test/no-class.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-class'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-class', 16 | message: 'Unallowed use of `class`. Use functions instead' 17 | }; 18 | 19 | ruleTester.run('no-class', rule, { 20 | valid: [ 21 | 'function foo() {}' 22 | ], 23 | invalid: [ 24 | { 25 | code: `class Polygon { 26 | constructor(height, width) { 27 | this.height = height; 28 | this.width = width; 29 | } 30 | }`, 31 | errors: [error] 32 | }, 33 | { 34 | code: 'export default class MyComponent extends React.Component {}', 35 | errors: [error] 36 | }, 37 | { 38 | code: 'module.exports = class MyClass {}', 39 | errors: [error] 40 | }, 41 | { 42 | code: 'const MyClass = class {}', 43 | errors: [error] 44 | } 45 | ] 46 | }); 47 | -------------------------------------------------------------------------------- /test/no-delete.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-delete'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-delete', 16 | message: 'Unallowed use of `delete`' 17 | }; 18 | 19 | ruleTester.run('no-delete', rule, { 20 | valid: [ 21 | '+foo.bar', 22 | '-foo.bar', 23 | '+ foo.bar', 24 | '- foo.bar', 25 | '+foo[bar]', 26 | '-foo[bar]', 27 | '+ foo[bar]', 28 | '- foo[bar]', 29 | 'bar.delete' 30 | ], 31 | invalid: [ 32 | { 33 | code: 'delete foo.bar;', 34 | errors: [error] 35 | }, 36 | { 37 | code: 'delete foo[bar];', 38 | errors: [error] 39 | } 40 | ] 41 | }); 42 | -------------------------------------------------------------------------------- /test/no-events.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-events'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-events', 16 | message: 'Unallowed use of `events`' 17 | }; 18 | 19 | ruleTester.run('no-events', rule, { 20 | valid: [ 21 | 'import foo from "foo"', 22 | 'import {bar} from "foo"', 23 | 'import events from "eventsE"', 24 | 'import events from "Eevents"', 25 | 'require("foo");', 26 | 'require("eventsE");', 27 | 'require("Efoo");', 28 | 'var events;' 29 | ], 30 | invalid: [ 31 | { 32 | code: 'import EventEmitter from "events";', 33 | errors: [error] 34 | }, 35 | { 36 | code: 'import {listen} from "events";', 37 | errors: [error] 38 | }, 39 | { 40 | code: 'require("events");', 41 | errors: [error] 42 | } 43 | ] 44 | }); 45 | -------------------------------------------------------------------------------- /test/no-get-set.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-get-set'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = message => ({ 15 | ruleId: 'no-get-set', 16 | message 17 | }); 18 | const getError = error('Unallowed use of `get`'); 19 | const setError = error('Unallowed use of `set`'); 20 | 21 | ruleTester.run('no-get-set', rule, { 22 | valid: [ 23 | 'var obj = {foo: "bar"}', 24 | 'var obj = {foo: "bar", bar: 1, baz: {}}', 25 | 'obj.foo()', 26 | 'foo()' 27 | ], 28 | invalid: [ 29 | { 30 | code: 'var obj = { get foo () {} }', 31 | errors: [getError] 32 | }, 33 | { 34 | code: 'var obj = { get foo () {}, bar: "baz" }', 35 | errors: [getError] 36 | }, 37 | { 38 | code: 'var obj = { get foo () {}, get bar () {} }', 39 | errors: [getError, getError] 40 | }, 41 | { 42 | code: 'var obj = { set foo (a) {} }', 43 | errors: [setError] 44 | }, 45 | { 46 | code: 'var obj = { set foo (a) {}, set bar (a) {} }', 47 | errors: [setError, setError] 48 | }, 49 | { 50 | code: 'var obj = { get foo () {}, set foo (a) {} }', 51 | errors: [getError, setError] 52 | }, 53 | { 54 | code: 'person.__defineGetter__("name", fn);', 55 | errors: [error('Unallowed use of a getter using `__defineGetter__`')] 56 | }, 57 | { 58 | code: 'person.__defineSetter__("name", fn);', 59 | errors: [error('Unallowed use of a setter using `__defineSetter__`')] 60 | }, 61 | { 62 | code: 'person["__defineGetter__"]("name", fn);', 63 | errors: [error('Unallowed use of a getter using `__defineGetter__`')] 64 | }, 65 | { 66 | code: 'person["__defineSetter__"]("name", fn);', 67 | errors: [error('Unallowed use of a setter using `__defineSetter__`')] 68 | } 69 | ] 70 | }); 71 | -------------------------------------------------------------------------------- /test/no-let.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-let'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-let', 16 | message: 'Unallowed use of `let`. Use `const` instead' 17 | }; 18 | 19 | ruleTester.run('no-let', rule, { 20 | valid: [ 21 | 'const a = 1;', 22 | 'const a = 1, b = 2;', 23 | 'const a = 1; const b = 2;', 24 | 'export const a = 1;', 25 | // Allowing `var` as there is a core ESLint rule for it 26 | 'var a;', 27 | 'var a, b;', 28 | 'var a = 1;', 29 | 'var a = 1, b = 2;', 30 | 'var a = 1; const b = 2;' 31 | ], 32 | invalid: [ 33 | { 34 | code: 'let a;', 35 | errors: [error] 36 | }, 37 | { 38 | code: 'let a, b;', 39 | errors: [error] 40 | }, 41 | { 42 | code: 'let a = 2', 43 | errors: [error] 44 | }, 45 | { 46 | code: 'let a = 2, b = 100;', 47 | errors: [error] 48 | }, 49 | { 50 | code: 'let a = 2; let b = 100;', 51 | errors: [error, error] 52 | }, 53 | { 54 | code: 'export let a = 2;', 55 | errors: [error] 56 | } 57 | ] 58 | }); 59 | -------------------------------------------------------------------------------- /test/no-loops.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-loops'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = message => ({ 15 | ruleId: 'no-loops', 16 | message 17 | }); 18 | const forError = error('Unallowed use of `for` loop'); 19 | const whileError = error('Unallowed use of `while` loop. Use recursion instead'); 20 | 21 | ruleTester.run('no-loops', rule, { 22 | valid: [], 23 | invalid: [ 24 | { 25 | code: 'for (var i = 0; i < foo.length; i++) {}', 26 | errors: [forError] 27 | }, 28 | { 29 | code: 'for (variable in object) {}', 30 | errors: [forError] 31 | }, 32 | { 33 | code: 'for (variable of object) {}', 34 | errors: [forError] 35 | }, 36 | { 37 | code: 'while (i < 5) {}', 38 | errors: [whileError] 39 | }, 40 | { 41 | code: 'do {} while (i < 5);', 42 | errors: [whileError] 43 | } 44 | ] 45 | }); 46 | -------------------------------------------------------------------------------- /test/no-mutating-assign.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-mutating-assign'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-mutating-assign', 16 | message: 'Unallowed use of mutating `Object.assign`' 17 | }; 18 | 19 | ruleTester.run('no-mutating-assign', rule, { 20 | valid: [ 21 | 'foo.bar(a, b);', 22 | 'foo.assign(a, b);', 23 | 'Object.foo(a, b);', 24 | 'Object.assign({});', 25 | 'Object.assign({}, b);', 26 | 'Object.assign(Object.create(null));', 27 | 'Object.assign(Object.create(null), b);', 28 | 'Object.assign({}, b, c, d, e);', 29 | 'Object.assign({foo: 1, bar: 2}, b);', 30 | 'Object.assign([1, 2], b);', 31 | 'Object.assign(() => {}, a);', 32 | 'Object.assign(function() {}, a);', 33 | 'Object.assign(function foo() {}, a);' 34 | ], 35 | invalid: [ 36 | { 37 | code: 'Object.assign();', 38 | errors: [error] 39 | }, 40 | { 41 | code: 'Object.assign(a);', 42 | errors: [error] 43 | }, 44 | { 45 | code: 'Object.assign(a, b);', 46 | errors: [error] 47 | }, 48 | { 49 | code: 'Object.assign(a, b, c, d, e);', 50 | errors: [error] 51 | }, 52 | { 53 | code: 'var a = {foo: 1, bar: 2}; Object.assign(a, b);', 54 | errors: [error] 55 | }, 56 | { 57 | code: 'var fn = () => {}; Object.assign(fn, b);', 58 | errors: [error] 59 | }, 60 | { 61 | code: 'function fn() {}; Object.assign(fn, b);', 62 | errors: [error] 63 | }, 64 | { 65 | code: 'Object.assign(Object.create)', 66 | errors: [error] 67 | } 68 | ] 69 | }); 70 | -------------------------------------------------------------------------------- /test/no-mutating-methods.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-mutating-methods'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const methodError = methodName => ({ 15 | ruleId: 'no-mutating-methods', 16 | message: `The use of method \`${methodName}\` is not allowed as it might be a mutating method` 17 | }); 18 | 19 | const objectError = methodName => ({ 20 | ruleId: 'no-mutating-methods', 21 | message: `The use of method \`Object.${methodName}\` is not allowed as it will mutate its arguments` 22 | }); 23 | 24 | ruleTester.run('no-mutating-methods', rule, { 25 | valid: [ 26 | 'value.foo()', 27 | 'value.bar()', 28 | 'value.concat()', 29 | 'value["foo"](a)', 30 | { 31 | code: '_.push(a, b)', 32 | options: [{ 33 | allowedObjects: ['_'] 34 | }] 35 | }, 36 | { 37 | code: '_.sort(a, b)', 38 | options: [{ 39 | allowedObjects: ['_'] 40 | }] 41 | }, 42 | { 43 | code: 'R.reverse(a, b)', 44 | options: [{ 45 | allowedObjects: ['R'] 46 | }] 47 | }, 48 | { 49 | code: 'R.sort(a, b)', 50 | options: [{ 51 | allowedObjects: ['R'] 52 | }] 53 | }, 54 | 'Object.keys(a)', 55 | 'Object.values(a)' 56 | ], 57 | invalid: [ 58 | { 59 | code: 'value.copyWithin(a);', 60 | errors: [methodError('copyWithin')] 61 | }, 62 | { 63 | code: 'value.pop(a);', 64 | errors: [methodError('pop')] 65 | }, 66 | { 67 | code: 'value.push(a);', 68 | errors: [methodError('push')] 69 | }, 70 | { 71 | code: 'value.reverse(a);', 72 | errors: [methodError('reverse')] 73 | }, 74 | { 75 | code: 'value.shift(a);', 76 | errors: [methodError('shift')] 77 | }, 78 | { 79 | code: 'value.sort(a);', 80 | errors: [methodError('sort')] 81 | }, 82 | { 83 | code: 'value.splice(a);', 84 | errors: [methodError('splice')] 85 | }, 86 | { 87 | code: 'value.unshift(a);', 88 | errors: [methodError('unshift')] 89 | }, 90 | { 91 | code: 'value.watch(a);', 92 | errors: [methodError('watch')] 93 | }, 94 | { 95 | code: 'value.unwatch(a);', 96 | errors: [methodError('unwatch')] 97 | }, 98 | { 99 | code: '_.sort(a)', 100 | errors: [methodError('sort')] 101 | }, 102 | { 103 | code: 'value["push"](a)', 104 | errors: [methodError('push')] 105 | }, 106 | { 107 | code: 'value["pop"](a)', 108 | errors: [methodError('pop')] 109 | }, 110 | { 111 | code: 'value.push(a)', 112 | options: [{ 113 | allowedObjects: ['foo'] 114 | }], 115 | errors: [methodError('push')] 116 | }, 117 | { 118 | code: 'R.sort(a)', 119 | options: [{ 120 | allowedObjects: ['_'] 121 | }], 122 | errors: [methodError('sort')] 123 | }, 124 | { 125 | code: 'R.sort(a)', 126 | options: [{ 127 | allowedObjects: ['ramda'] 128 | }], 129 | errors: [methodError('sort')] 130 | }, 131 | { 132 | code: 'R.foo().sort(a)', 133 | options: [{ 134 | allowedObjects: ['R'] 135 | }], 136 | errors: [methodError('sort')] 137 | }, 138 | { 139 | code: 'R.foo.sort(a)', 140 | options: [{ 141 | allowedObjects: ['R'] 142 | }], 143 | errors: [methodError('sort')] 144 | }, 145 | { 146 | code: 'foo().sort(a)', 147 | errors: [methodError('sort')] 148 | }, 149 | { 150 | code: 'R().sort(a)', 151 | options: [{ 152 | allowedObjects: ['R'] 153 | }], 154 | errors: [methodError('sort')] 155 | }, 156 | { 157 | code: 'Object.defineProperties(a)', 158 | errors: [objectError('defineProperties')] 159 | }, 160 | { 161 | code: 'Object.defineProperty(a)', 162 | errors: [objectError('defineProperty')] 163 | }, 164 | { 165 | code: 'Object.setPrototypeOf(a)', 166 | errors: [objectError('setPrototypeOf')] 167 | } 168 | ] 169 | }); 170 | -------------------------------------------------------------------------------- /test/no-mutation.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-mutation'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = message => ({ 15 | ruleId: 'no-mutation', 16 | message 17 | }); 18 | const reassignmentError = error('Unallowed reassignment'); 19 | const incrementError = error('Unallowed use of `++` operator'); 20 | const decrementError = error('Unallowed use of `--` operator'); 21 | const commonJsError = error('Unallowed reassignment. You may want to activate the `commonjs` option for this rule'); 22 | 23 | ruleTester.run('no-mutation', rule, { 24 | valid: [ 25 | 'var a = 2;', 26 | 'let a = 2;', 27 | 'const a = 2;', 28 | 'function foo(a={}) {}', 29 | { 30 | code: 'exports = {};', 31 | options: [{commonjs: true}] 32 | }, 33 | { 34 | code: 'exports.foo = {};', 35 | options: [{commonjs: true}] 36 | }, 37 | { 38 | code: 'exports.foo.bar = {};', 39 | options: [{commonjs: true}] 40 | }, 41 | { 42 | code: 'module.exports = {};', 43 | options: [{commonjs: true}] 44 | }, 45 | { 46 | code: 'module.exports.foo = {};', 47 | options: [{commonjs: true}] 48 | }, 49 | { 50 | code: 'module.exports.foo.bar = {};', 51 | options: [{commonjs: true}] 52 | }, 53 | { 54 | code: 'foo.bar = {};', 55 | options: [{exceptions: [ 56 | {object: 'foo', property: 'bar'} 57 | ]}] 58 | }, 59 | { 60 | code: 'foo.bar = {};', 61 | options: [{exceptions: [ 62 | {object: 'foo'} 63 | ]}] 64 | }, 65 | { 66 | code: 'baz.propTypes = {};', 67 | options: [{exceptions: [ 68 | {property: 'propTypes'} 69 | ]}] 70 | }, 71 | { 72 | code: 'module.exports = {};', 73 | options: [{exceptions: [ 74 | {object: 'module', property: 'exports'} 75 | ]}] 76 | }, 77 | { 78 | code: 'module.exports[foo].bar = {};', 79 | options: [{exceptions: [ 80 | {object: 'module', property: 'exports'} 81 | ]}] 82 | }, 83 | { 84 | code: 'module.exports.foo = {};', 85 | options: [{exceptions: [ 86 | {object: 'foo', property: 'bar'}, 87 | {object: 'module', property: 'exports'} 88 | ]}] 89 | }, 90 | { 91 | code: 'foo.bar = {};', 92 | options: [{exceptions: [ 93 | {object: 'foo', property: 'bar'}, 94 | {object: 'module', property: 'exports'} 95 | ]}] 96 | }, 97 | { 98 | code: 'this.foo = 100;', 99 | options: [{allowThis: true}] 100 | }, 101 | { 102 | code: 'this.foo.bar = 100;', 103 | options: [{allowThis: true}] 104 | }, 105 | { 106 | code: 'function bar() { this.foo = 100; }', 107 | options: [{allowThis: true}] 108 | } 109 | ], 110 | invalid: [ 111 | { 112 | code: 'a = 2;', 113 | errors: [reassignmentError] 114 | }, 115 | { 116 | code: 'a += 2;', 117 | errors: [reassignmentError] 118 | }, 119 | { 120 | code: 'a -= 2;', 121 | errors: [reassignmentError] 122 | }, 123 | { 124 | code: 'a *= 2;', 125 | errors: [reassignmentError] 126 | }, 127 | { 128 | code: 'a /= 2;', 129 | errors: [reassignmentError] 130 | }, 131 | { 132 | code: 'a %= 2;', 133 | errors: [reassignmentError] 134 | }, 135 | { 136 | code: 'a++;', 137 | errors: [incrementError] 138 | }, 139 | { 140 | code: '++a;', 141 | errors: [incrementError] 142 | }, 143 | { 144 | code: 'a--;', 145 | errors: [decrementError] 146 | }, 147 | { 148 | code: '--a;', 149 | errors: [decrementError] 150 | }, 151 | { 152 | code: 'function foo(a) { a = a || {}; }', 153 | errors: [reassignmentError] 154 | }, 155 | { 156 | code: 'module.foo = {};', 157 | errors: [reassignmentError] 158 | }, 159 | { 160 | code: 'foo.exports = {};', 161 | errors: [reassignmentError] 162 | }, 163 | { 164 | code: 'exports = {};', 165 | errors: [commonJsError] 166 | }, 167 | { 168 | code: 'exports.foo = {};', 169 | errors: [commonJsError] 170 | }, 171 | { 172 | code: 'exports.foo.bar = {};', 173 | errors: [commonJsError] 174 | }, 175 | { 176 | code: 'exports[foo] = {};', 177 | errors: [commonJsError] 178 | }, 179 | { 180 | code: 'exports.foo[bar] = {};', 181 | errors: [commonJsError] 182 | }, 183 | { 184 | code: 'exports[foo].bar = {};', 185 | errors: [commonJsError] 186 | }, 187 | { 188 | code: 'module.exports = {};', 189 | errors: [commonJsError] 190 | }, 191 | { 192 | code: 'module.exports.foo = {};', 193 | errors: [commonJsError] 194 | }, 195 | { 196 | code: 'module.exports[foo] = {};', 197 | errors: [commonJsError] 198 | }, 199 | { 200 | code: 'module.exports.foo[bar] = {};', 201 | errors: [commonJsError] 202 | }, 203 | { 204 | code: 'module.exports[foo].bar = {};', 205 | errors: [commonJsError] 206 | }, 207 | { 208 | code: 'foo.bar = {};', 209 | options: [{exceptions: [ 210 | {object: 'foo', property: 'boo'} 211 | ]}], 212 | errors: [reassignmentError] 213 | }, 214 | { 215 | code: 'baz.propTypes = {};', 216 | options: [{exceptions: [ 217 | {object: 'foo'} 218 | ]}], 219 | errors: [reassignmentError] 220 | }, 221 | { 222 | code: 'baz.propTypes = {};', 223 | options: [{exceptions: [ 224 | {property: 'props'} 225 | ]}], 226 | errors: [reassignmentError] 227 | }, 228 | { 229 | code: 'baz.propTypes = {};', 230 | options: [{exceptions: [{}]}], 231 | errors: [reassignmentError] 232 | }, 233 | { 234 | code: 'this.foo = 100;', 235 | errors: [reassignmentError] 236 | }, 237 | { 238 | code: 'this.foo.bar = 100;', 239 | errors: [reassignmentError] 240 | }, 241 | { 242 | code: 'function bar() { this.foo = 100; }', 243 | errors: [reassignmentError] 244 | } 245 | ] 246 | }); 247 | -------------------------------------------------------------------------------- /test/no-nil.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-nil'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = message => ({ 15 | ruleId: 'no-nil', 16 | message 17 | }); 18 | const useOfNullOrUndefinedError = error('Unallowed use of `null` or `undefined`'); 19 | const notInitializedError = error('Variable must be initialized, so that it doesn\'t evaluate to `undefined`'); 20 | const returnUndefinedError = error('Return statement must return an explicit value, so that it doesn\'t evaluate to `undefined`'); 21 | const functionReturnError = error('Function must end with a return statement, so that it doesn\'t return `undefined`'); 22 | 23 | ruleTester.run('no-nil', rule, { 24 | valid: [ 25 | 'var a = 1;', 26 | 'let a = 1;', 27 | 'const a = 1;', 28 | 'var a = b;', 29 | 'let a = b;', 30 | 'const a = b;', 31 | 'a == null', 32 | 'a == undefined', 33 | 'a != null', 34 | 'a != undefined', 35 | 'a === null', 36 | 'a === undefined', 37 | 'a !== null', 38 | 'a !== undefined', 39 | 'function foo() { return 1; }', 40 | 'function foo(a=1) { return 1; }', 41 | 'const foo = () => 1;' 42 | ], 43 | invalid: [ 44 | { 45 | code: 'var a = null;', 46 | errors: [useOfNullOrUndefinedError] 47 | }, 48 | { 49 | code: 'let a = null;', 50 | errors: [useOfNullOrUndefinedError] 51 | }, 52 | { 53 | code: 'const a = null;', 54 | errors: [useOfNullOrUndefinedError] 55 | }, 56 | { 57 | code: 'var a = undefined;', 58 | errors: [useOfNullOrUndefinedError] 59 | }, 60 | { 61 | code: 'let a = undefined;', 62 | errors: [useOfNullOrUndefinedError] 63 | }, 64 | { 65 | code: 'const a = undefined;', 66 | errors: [useOfNullOrUndefinedError] 67 | }, 68 | { 69 | code: 'a < null;', 70 | errors: [useOfNullOrUndefinedError] 71 | }, 72 | { 73 | code: 'a < undefined;', 74 | errors: [useOfNullOrUndefinedError] 75 | }, 76 | { 77 | code: 'a < undefined;', 78 | errors: [useOfNullOrUndefinedError] 79 | }, 80 | { 81 | code: 'a === foo || null', 82 | errors: [useOfNullOrUndefinedError] 83 | }, 84 | { 85 | code: '(a === foo) || null', 86 | errors: [useOfNullOrUndefinedError] 87 | }, 88 | { 89 | code: 'a === (foo || null)', 90 | errors: [useOfNullOrUndefinedError] 91 | }, 92 | { 93 | code: 'const obj = {foo: null};', 94 | errors: [useOfNullOrUndefinedError] 95 | }, 96 | { 97 | code: 'const obj = {foo: undefined};', 98 | errors: [useOfNullOrUndefinedError] 99 | }, 100 | { 101 | code: 'let a;', 102 | errors: [notInitializedError] 103 | }, 104 | { 105 | code: 'var a;', 106 | errors: [notInitializedError] 107 | }, 108 | { 109 | code: 'var a, b;', 110 | errors: [notInitializedError, notInitializedError] 111 | }, 112 | { 113 | code: 'function foo(a=null) { return 1; }', 114 | errors: [useOfNullOrUndefinedError] 115 | }, 116 | { 117 | code: 'function foo(a=undefined) { return 1; }', 118 | errors: [useOfNullOrUndefinedError] 119 | }, 120 | { 121 | code: 'const foo = () => null', 122 | errors: [useOfNullOrUndefinedError] 123 | }, 124 | { 125 | code: 'const foo = () => undefined', 126 | errors: [useOfNullOrUndefinedError] 127 | }, 128 | { 129 | code: 'const foo = () => {}', 130 | errors: [functionReturnError] 131 | }, 132 | { 133 | code: 'function foo() {}', 134 | errors: [functionReturnError] 135 | }, 136 | { 137 | code: 'function foo() { return; }', 138 | errors: [returnUndefinedError] 139 | }, 140 | { 141 | code: 'function foo() { return null; }', 142 | errors: [useOfNullOrUndefinedError] 143 | }, 144 | { 145 | code: 'function foo() { return undefined; }', 146 | errors: [useOfNullOrUndefinedError] 147 | }, 148 | { 149 | code: 'function foo() { if (foo) { return; } return a + b; }', 150 | errors: [returnUndefinedError] 151 | }, 152 | { 153 | code: 'a.map(function() {});', 154 | errors: [functionReturnError] 155 | }, 156 | { 157 | code: 'function foo() { a + b; }', 158 | errors: [functionReturnError] 159 | }, 160 | { 161 | code: 'function foo() { a + b; return; }', 162 | errors: [returnUndefinedError] 163 | } 164 | ] 165 | }); 166 | -------------------------------------------------------------------------------- /test/no-proxy.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-proxy'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-proxy', 16 | message: 'Unallowed use of `Proxy`' 17 | }; 18 | 19 | ruleTester.run('no-proxy', rule, { 20 | valid: [ 21 | 'Map', 22 | 'Map()', 23 | 'new Map()' 24 | ], 25 | invalid: [ 26 | { 27 | code: 'Proxy', 28 | errors: [error] 29 | }, 30 | { 31 | code: 'new Proxy(foo, bar);', 32 | errors: [error] 33 | }, 34 | { 35 | code: 'Proxy.revocable()', 36 | errors: [error] 37 | } 38 | ] 39 | }); 40 | -------------------------------------------------------------------------------- /test/no-rest-parameters.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-rest-parameters'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | parser: 'babel-eslint', 11 | sourceType: 'module' 12 | } 13 | }); 14 | 15 | const error = { 16 | ruleId: 'no-rest-parameters', 17 | message: 'Unallowed use of rest parameters. Use regular function arguments instead' 18 | }; 19 | 20 | ruleTester.run('no-rest-parameters', rule, { 21 | valid: [ 22 | 'function foo() {}', 23 | 'function foo(a, b) {}', 24 | 'function foo(a, b) { return a + b; }', 25 | 'var foo = (a, b) => a + b;', 26 | 'function foo() { console.log(arguments); }', 27 | 'var a = [...b, c]', 28 | { 29 | code: 'var a = {...b, c: 1}', 30 | parserOptions: { 31 | ecmaFeatures: { 32 | experimentalObjectRestSpread: true 33 | } 34 | } 35 | } 36 | ], 37 | invalid: [ 38 | { 39 | code: 'function foo(...args) {}', 40 | errors: [error] 41 | }, 42 | { 43 | code: 'function foo(a, b, ...args) {}', 44 | errors: [error] 45 | }, 46 | { 47 | code: 'fn(function(...args) {})', 48 | errors: [error] 49 | }, 50 | { 51 | code: 'var foo = function(...args) {}', 52 | errors: [error] 53 | }, 54 | { 55 | code: 'var foo = (...args) => {}', 56 | errors: [error] 57 | } 58 | ] 59 | }); 60 | -------------------------------------------------------------------------------- /test/no-this.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-this'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-this', 16 | message: 'Unallowed use of `this`' 17 | }; 18 | 19 | ruleTester.run('no-this', rule, { 20 | valid: [ 21 | 'foo;', 22 | 'var foo;' 23 | ], 24 | invalid: [ 25 | { 26 | code: 'this;', 27 | errors: [error] 28 | }, 29 | { 30 | code: 'this.a;', 31 | errors: [error] 32 | }, 33 | { 34 | code: 'function a() {this;};', 35 | errors: [error] 36 | }, 37 | { 38 | code: 'function a() {return this.foo();};', 39 | errors: [error] 40 | } 41 | ] 42 | }); 43 | -------------------------------------------------------------------------------- /test/no-throw.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-throw'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-throw', 16 | message: 'Unallowed use of `throw`' 17 | }; 18 | 19 | ruleTester.run('no-throw', rule, { 20 | valid: [ 21 | 'new Error("foo");' 22 | ], 23 | invalid: [ 24 | { 25 | code: 'throw new Error("foo");', 26 | errors: [error] 27 | }, 28 | { 29 | code: 'throw error;', 30 | errors: [error] 31 | } 32 | ] 33 | }); 34 | -------------------------------------------------------------------------------- /test/no-unused-expression.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-unused-expression'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-unused-expression', 16 | message: 'Unused expression' 17 | }; 18 | 19 | ruleTester.run('no-unused-expression', rule, { 20 | valid: [ 21 | 'const a = 1 + 2;', 22 | 'function foo() {}', 23 | 'const foo = () => {}', 24 | 'function foo(a, b) { return a + b; }', 25 | 'const foo = (a, b) => a + b;', 26 | 'a = 1;', 27 | 'export function foo(x) { return x * x; }', 28 | 'export default () => {};', 29 | 'export const a = 1, b = 2;', 30 | 'const a = 1; const b = 2;', 31 | 'export const a = 1;', 32 | 'delete foo.bar', 33 | '--foo', 34 | '++foo', 35 | 'foo--', 36 | 'foo++', 37 | 'class MyClass extends SuperClass { constructor(arg) { super(arg) } }', 38 | { 39 | code: '"use strict";', 40 | options: [{allowUseStrict: true}] 41 | }, 42 | { 43 | code: 'function foo() { "use strict"; return 2; }', 44 | options: [{allowUseStrict: true}] 45 | } 46 | ], 47 | invalid: [ 48 | { 49 | code: 'foo;', 50 | errors: [error] 51 | }, 52 | { 53 | code: '[];', 54 | errors: [error] 55 | }, 56 | { 57 | code: '1 + 2;', 58 | errors: [error] 59 | }, 60 | { 61 | code: 'foo()', 62 | errors: [error] 63 | }, 64 | { 65 | code: '1, 2;', 66 | errors: [error] 67 | }, 68 | { 69 | code: '(1, 2);', 70 | errors: [error] 71 | }, 72 | { 73 | code: 'var a = (1, 2);', 74 | errors: [error] 75 | }, 76 | { 77 | code: 'a = (1, 2);', 78 | errors: [error] 79 | }, 80 | { 81 | code: 'a = 1, 2;', 82 | errors: [error] 83 | }, 84 | { 85 | code: '[].forEach(function() {})', 86 | errors: [error] 87 | }, 88 | { 89 | code: 'function foo(a, b) { a + b; }', 90 | errors: [error] 91 | }, 92 | { 93 | code: '"use strict";', 94 | errors: [error] 95 | }, 96 | { 97 | code: 'function foo() { "use strict"; return 2; }', 98 | errors: [error] 99 | }, 100 | { 101 | code: [ 102 | 'class MyClass extends SuperClass {', 103 | ' constructor(arg) {', 104 | ' super(arg);', 105 | ' super.method();', 106 | ' }', 107 | '}' 108 | ].join('\n'), 109 | errors: [error] 110 | } 111 | ] 112 | }); 113 | -------------------------------------------------------------------------------- /test/no-valueof-field.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import avaRuleTester from 'eslint-ava-rule-tester'; 3 | import rule from '../rules/no-valueof-field'; 4 | 5 | const ruleTester = avaRuleTester(test, { 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const error = { 15 | ruleId: 'no-valueof-field', 16 | message: 'Unallowed use of `valueOf` field' 17 | }; 18 | 19 | ruleTester.run('no-valueof-field', rule, { 20 | valid: [ 21 | 'function valueOf() {}', 22 | 'const object = { value: () => {} };', 23 | 'const object = { ["value"]: () => {} };', 24 | 'const object = {}; object.value = () => {}', 25 | 'const object = {}; object.prototype.value = () => {}', 26 | 'valueOf = 2;', 27 | 'object.valueOf();', 28 | 'const array = [valueOf, valueOf];' 29 | ], 30 | invalid: [ 31 | { 32 | code: 'const object = { valueOf: () => {} };', 33 | errors: [error] 34 | }, 35 | { 36 | code: 'const object = { ["valueOf"]: () => {} };', 37 | errors: [error] 38 | }, 39 | { 40 | code: 'const valueOf = () => {}; const object = { valueOf };', 41 | errors: [error] 42 | }, 43 | { 44 | code: 'const object = {}; object.valueOf = () => {}', 45 | errors: [error] 46 | }, 47 | { 48 | code: 'object.prototype.valueOf = () => {}', 49 | errors: [error] 50 | } 51 | ] 52 | }); 53 | -------------------------------------------------------------------------------- /test/package.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import test from 'ava'; 3 | import pify from 'pify'; 4 | import index from '../'; 5 | 6 | test('every rule should defined in the index file and recommended settings', async t => { 7 | const files = await pify(fs.readdir, Promise)('rules/'); 8 | const rules = files.filter(file => file.indexOf('.js') === file.length - 3); 9 | 10 | rules.forEach(file => { 11 | const name = file.slice(0, -3); 12 | t.truthy(index.rules[name], `'${name}' is not exported in 'index.js'`); 13 | t.truthy(index.rules[name].meta.docs.description, `'${name}' does not have a description`); 14 | t.truthy(index.rules[name].meta.docs.recommended, `'${name}' does not have a recommended setting`); 15 | t.truthy(index.configs.recommended.rules[`fp/${name}`], `'${name}' is not set in the recommended config`); 16 | }); 17 | 18 | t.is(Object.keys(index.rules).length, rules.length, 19 | 'There are more exported rules than rule files.'); 20 | }); 21 | 22 | test('no-var should be turned on in the recommended settings', async t => { 23 | t.true(index.configs.recommended.rules['no-var'] === 'error'); 24 | }); 25 | --------------------------------------------------------------------------------