diff --git a/docs/rules/indent.md b/docs/rules/indent.md index 748b725237e..38f7447325e 100644 --- a/docs/rules/indent.md +++ b/docs/rules/indent.md @@ -69,7 +69,7 @@ if (a) { This rule has an object option: * `"SwitchCase"` (default: 0) enforces indentation level for `case` clauses in `switch` statements -* `"VariableDeclarator"` (default: 1) enforces indentation level for `var` declarators; can also take an object to define separate rules for `var`, `let` and `const` declarations. +* `"VariableDeclarator"` (default: 1) enforces indentation level for `var` declarators; can also take an object to define separate rules for `var`, `let` and `const` declarations. It can also be `"first"`, indicating all the declarators should be aligned with the first declarator. * `"outerIIFEBody"` (default: 1) enforces indentation level for file-level IIFEs. * `"MemberExpression"` (default: 1) enforces indentation level for multi-line property chains. This can also be set to `"off"` to disable checking for MemberExpression indentation. * `"FunctionDeclaration"` takes an object to define rules for function declarations. @@ -213,6 +213,40 @@ const a = 1, c = 3; ``` +Examples of **incorrect** code for this rule with the `2, { "VariableDeclarator": "first" }` options: + +```js +/*eslint indent: ["error", 2, { "VariableDeclarator": "first" }]*/ +/*eslint-env es6*/ + +var a, + b, + c; +let a, + b, + c; +const a = 1, + b = 2, + c = 3; +``` + +Examples of **correct** code for this rule with the `2, { "VariableDeclarator": "first" }` options: + +```js +/*eslint indent: ["error", 2, { "VariableDeclarator": "first" }]*/ +/*eslint-env es6*/ + +var a, + b, + c; +let a, + b, + c; +const a = 1, + b = 2, + c = 3; +``` + Examples of **correct** code for this rule with the `2, { "VariableDeclarator": { "var": 2, "let": 2, "const": 3 } }` options: ```js diff --git a/lib/rules/indent.js b/lib/rules/indent.js index 0b87412c8fc..af7e2b147a0 100644 --- a/lib/rules/indent.js +++ b/lib/rules/indent.js @@ -522,25 +522,13 @@ module.exports = { }, VariableDeclarator: { oneOf: [ - { - type: "integer", - minimum: 0 - }, + ELEMENT_LIST_SCHEMA, { type: "object", properties: { - var: { - type: "integer", - minimum: 0 - }, - let: { - type: "integer", - minimum: 0 - }, - const: { - type: "integer", - minimum: 0 - } + var: ELEMENT_LIST_SCHEMA, + let: ELEMENT_LIST_SCHEMA, + const: ELEMENT_LIST_SCHEMA }, additionalProperties: false } @@ -661,7 +649,7 @@ module.exports = { if (context.options[1]) { lodash.merge(options, context.options[1]); - if (typeof options.VariableDeclarator === "number") { + if (typeof options.VariableDeclarator === "number" || options.VariableDeclarator === "first") { options.VariableDeclarator = { var: options.VariableDeclarator, let: options.VariableDeclarator, @@ -1349,10 +1337,27 @@ module.exports = { }, VariableDeclaration(node) { - const variableIndent = Object.prototype.hasOwnProperty.call(options.VariableDeclarator, node.kind) + let variableIndent = Object.prototype.hasOwnProperty.call(options.VariableDeclarator, node.kind) ? options.VariableDeclarator[node.kind] : DEFAULT_VARIABLE_INDENT; + const firstToken = sourceCode.getFirstToken(node), + lastToken = sourceCode.getLastToken(node); + + if (options.VariableDeclarator[node.kind] === "first") { + if (node.declarations.length > 1) { + addElementListIndent( + node.declarations, + firstToken, + lastToken, + "first" + ); + return; + } + + variableIndent = DEFAULT_VARIABLE_INDENT; + } + if (node.declarations[node.declarations.length - 1].loc.start.line > node.loc.start.line) { /* @@ -1374,13 +1379,10 @@ module.exports = { * on the same line as the start of the declaration, provided that there are declarators that * follow this one. */ - const firstToken = sourceCode.getFirstToken(node); - offsets.setDesiredOffsets(node.range, firstToken, variableIndent, true); } else { - offsets.setDesiredOffsets(node.range, sourceCode.getFirstToken(node), variableIndent); + offsets.setDesiredOffsets(node.range, firstToken, variableIndent); } - const lastToken = sourceCode.getLastToken(node); if (astUtils.isSemicolonToken(lastToken)) { offsets.ignoreToken(lastToken); diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js index 8f20a8ea8e8..2690830b919 100644 --- a/tests/lib/rules/indent.js +++ b/tests/lib/rules/indent.js @@ -614,6 +614,24 @@ ruleTester.run("indent", rule, { `, options: [2, { VariableDeclarator: 1 }] }, + { + code: unIndent` + let foo = 'foo', + bar = bar; + const a = 'a', + b = 'b'; + `, + options: [2, { VariableDeclarator: "first" }] + }, + { + code: unIndent` + let foo = 'foo', + bar = bar // <-- no semicolon here + const a = 'a', + b = 'b' // <-- no semicolon here + `, + options: [2, { VariableDeclarator: "first" }] + }, { code: unIndent` var foo = 1, @@ -632,6 +650,43 @@ ruleTester.run("indent", rule, { `, options: [2, { VariableDeclarator: { var: 2 } }] }, + { + code: unIndent` + var foo = 'foo', + bar = bar; + `, + options: [2, { VariableDeclarator: { var: "first" } }] + }, + { + code: unIndent` + var foo = 'foo', + bar = 'bar' // <-- no semicolon here + `, + options: [2, { VariableDeclarator: { var: "first" } }] + }, + { + code: unIndent` + let foo = 1, + bar = 2, + baz + `, + options: [2, { VariableDeclarator: "first" }] + }, + { + code: unIndent` + let + foo + `, + options: [4, { VariableDeclarator: "first" }] + }, + { + code: unIndent` + let foo = 1, + bar = + 2 + `, + options: [2, { VariableDeclarator: "first" }] + }, { code: unIndent` var abc = @@ -5933,6 +5988,39 @@ ruleTester.run("indent", rule, { [2, 4, 2, "Identifier"] ]) }, + { + code: unIndent` + let foo = 'foo', + bar = bar; + const a = 'a', + b = 'b'; + `, + output: unIndent` + let foo = 'foo', + bar = bar; + const a = 'a', + b = 'b'; + `, + options: [2, { VariableDeclarator: "first" }], + errors: expectedErrors([ + [2, 4, 2, "Identifier"], + [4, 6, 2, "Identifier"] + ]) + }, + { + code: unIndent` + var foo = 'foo', + bar = bar; + `, + output: unIndent` + var foo = 'foo', + bar = bar; + `, + options: [2, { VariableDeclarator: { var: "first" } }], + errors: expectedErrors([ + [2, 4, 2, "Identifier"] + ]) + }, { code: unIndent` if(true)