Skip to content

Commit

Permalink
New: add rule default-param-last (fixes #11361) (#12188)
Browse files Browse the repository at this point in the history
* New: add rule default-param-last (fixes #11361)

* Chore: fix typos

* Chore: add test cases

* Apply suggestion: add column for a test case

* Update: change error message

* Docs: add example in opening section

* Add test cases about parameter destructuring
  • Loading branch information
golopot authored and platinumazure committed Sep 7, 2019
1 parent 7621f5d commit 0313441
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 0 deletions.
35 changes: 35 additions & 0 deletions docs/rules/default-param-last.md
@@ -0,0 +1,35 @@
# enforce default parameters to be last (default-param-last)

Putting default parameter at last allows function calls to omit optional tail arguments.

```js
// Correct: optional argument can be omitted
function createUser(id, isAdmin = false) {}
createUser("tabby")

// Incorrect: optional argument can **not** be omitted
function createUser(isAdmin = false, id) {}
createUser(undefined, "tabby")
```

## Rule Details

This rule enforces default parameters to be the last of parameters.

Examples of **incorrect** code for this rule:

```js
/* eslint default-param-last: ["error"] */

function f(a = 0, b) {}

function f(a, b = 0, c) {}
```

Examples of **correct** code for this rule:

```js
/* eslint default-param-last: ["error"] */

function f(a, b = 0) {}
```
61 changes: 61 additions & 0 deletions lib/rules/default-param-last.js
@@ -0,0 +1,61 @@
/**
* @fileoverview enforce default parameters to be last
* @author Chiawen Chen
*/

"use strict";

module.exports = {
meta: {
type: "suggestion",

docs: {
description: "enforce default parameters to be last",
category: "Best Practices",
recommended: false,
url: "https://eslint.org/docs/rules/default-param-last"
},

schema: [],

messages: {
shouldBeLast: "Default parameters should be last."
}
},

create(context) {

/**
* @param {ASTNode} node function node
* @returns {void}
*/
function handleFunction(node) {
let hasSeenPlainParam = false;

for (let i = node.params.length - 1; i >= 0; i -= 1) {
const param = node.params[i];

if (
param.type !== "AssignmentPattern" &&
param.type !== "RestElement"
) {
hasSeenPlainParam = true;
continue;
}

if (hasSeenPlainParam && param.type === "AssignmentPattern") {
context.report({
node: param,
messageId: "shouldBeLast"
});
}
}
}

return {
FunctionDeclaration: handleFunction,
FunctionExpression: handleFunction,
ArrowFunctionExpression: handleFunction
};
}
};
1 change: 1 addition & 0 deletions lib/rules/index.js
Expand Up @@ -37,6 +37,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
"constructor-super": () => require("./constructor-super"),
curly: () => require("./curly"),
"default-case": () => require("./default-case"),
"default-param-last": () => require("./default-param-last"),
"dot-location": () => require("./dot-location"),
"dot-notation": () => require("./dot-notation"),
"eol-last": () => require("./eol-last"),
Expand Down
106 changes: 106 additions & 0 deletions tests/lib/rules/default-param-last.js
@@ -0,0 +1,106 @@
/**
* @fileoverview Test file for default-param-last
* @author Chiawen Chen
*/
"use strict";

const rule = require("../../../lib/rules/default-param-last");
const { RuleTester } = require("../../../lib/rule-tester");

const SHOULD_BE_LAST = "shouldBeLast";

const ruleTester = new RuleTester({
parserOptions: { ecmaVersion: 8 }
});

const cannedError = {
messageId: SHOULD_BE_LAST,
type: "AssignmentPattern"
};

ruleTester.run("default-param-last", rule, {
valid: [
"function f() {}",
"function f(a) {}",
"function f(a = 5) {}",
"function f(a, b) {}",
"function f(a, b = 5) {}",
"function f(a, b = 5, c = 5) {}",
"function f(a, b = 5, ...c) {}",
"const f = () => {}",
"const f = (a) => {}",
"const f = (a = 5) => {}",
"const f = function f() {}",
"const f = function f(a) {}",
"const f = function f(a = 5) {}"
],
invalid: [
{
code: "function f(a = 5, b) {}",
errors: [
{
messageId: SHOULD_BE_LAST,
column: 12,
endColumn: 17
}
]
},
{
code: "function f(a = 5, b = 6, c) {}",
errors: [
{
messageId: SHOULD_BE_LAST,
column: 12,
endColumn: 17
},
{
messageId: SHOULD_BE_LAST,
column: 19,
endColumn: 24
}
]
},
{
code: "function f (a = 5, b, c = 6, d) {}",
errors: [cannedError, cannedError]
},
{
code: "function f(a = 5, b, c = 5) {}",
errors: [
{
messageId: SHOULD_BE_LAST,
column: 12,
endColumn: 17
}
]
},
{
code: "const f = (a = 5, b, ...c) => {}",
errors: [cannedError]
},
{
code: "const f = function f (a, b = 5, c) {}",
errors: [cannedError]
},
{
code: "const f = (a = 5, { b }) => {}",
errors: [cannedError]
},
{
code: "const f = ({ a } = {}, b) => {}",
errors: [cannedError]
},
{
code: "const f = ({ a, b } = { a: 1, b: 2 }, c) => {}",
errors: [cannedError]
},
{
code: "const f = ([a] = [], b) => {}",
errors: [cannedError]
},
{
code: "const f = ([a, b] = [1, 2], c) => {}",
errors: [cannedError]
}
]
});
1 change: 1 addition & 0 deletions tools/rule-types.json
Expand Up @@ -24,6 +24,7 @@
"constructor-super": "problem",
"curly": "suggestion",
"default-case": "suggestion",
"default-param-last": "suggestion",
"dot-location": "layout",
"dot-notation": "suggestion",
"eol-last": "layout",
Expand Down

0 comments on commit 0313441

Please sign in to comment.