diff --git a/conf/eslint-recommended.js b/conf/eslint-recommended.js index a37ee4862ba2..5d7f4d33c000 100755 --- a/conf/eslint-recommended.js +++ b/conf/eslint-recommended.js @@ -63,6 +63,7 @@ module.exports = { "linebreak-style": "off", "lines-around-comment": "off", "lines-around-directive": "off", + "lines-between-class-members": "off", "max-depth": "off", "max-len": "off", "max-lines": "off", @@ -221,7 +222,6 @@ module.exports = { "operator-assignment": "off", "operator-linebreak": "off", "padded-blocks": "off", - "padding-line-after-class-members": "off", "padding-line-between-statements": "off", "prefer-arrow-callback": "off", "prefer-const": "off", diff --git a/docs/rules/lines-between-class-members.md b/docs/rules/lines-between-class-members.md index fecd30be085a..ee0b125216e2 100644 --- a/docs/rules/lines-between-class-members.md +++ b/docs/rules/lines-between-class-members.md @@ -1,6 +1,6 @@ -# require or disallow an empty line after after class members (lines-between-class-members) +# require or disallow an empty line between class members (lines-between-class-members) -This rule improves readability by enforcing lines between class members. it will not check empty lines before the first member and after the last member, since that is already taken care of by padded-blocks. +This rule improves readability by enforcing lines between class members. It will not check empty lines before the first member and after the last member, since that is already taken care of by padded-blocks. ## Rule Details @@ -44,10 +44,8 @@ String option: Object option: -* `"multiline": "always"` require an empty line after after multiline class members -* `"multiline": "never"` disallows an empty line after after multiline class members -* `"singleline": "always"` require an empty line after after singleline class members -* `"singleline": "never"` disallows an empty line after after singleline class members +* `"exceptAfterSingleLine": "false"`(default) **do not** skip checking empty lines after singleline class members +* `"exceptAfterSingleLine": "true"` skip checking empty lines after singleline class members Examples of **incorrect** code for this rule with the string option: @@ -83,70 +81,17 @@ class Foo{ } ``` -Examples of **incorrect** code for this rule with the object option: +Examples of **correct** code for this rule with the object option: ```js -/* eslint lines-between-class-members: ["error", { multiline: "always" }]*/ -class Foo{ - bar(){ - bar(); - } - baz(){} -} - -/* eslint lines-between-class-members: ["error", { multiline: "never" }]*/ -class Foo{ - bar(){ - bar(); - } - - baz(){} -} - -/* eslint lines-between-class-members: ["error", { singleline: "always" }]*/ -class Foo{ - bar(){} - baz(){} -} - -/* eslint lines-between-class-members: ["error", { singleline: "never" }]*/ +/* eslint lines-between-class-members: ["error", "always", { exceptAfterSingleLine: true }]*/ class Foo{ bar(){} - baz(){} } ``` -Examples of **correct** code for this rule with the object option: - -```js -/* eslint lines-between-class-members: ["error", { multiline: "always" }]*/ -class Foo{ - bar(){ - bar(); - } - - baz(){} -} +## Related Rules -/* eslint lines-between-class-members: ["error", { multiline: "never" }]*/ -class Foo{ - bar(){ - bar(); - } - baz(){} -} - -/* eslint lines-between-class-members: ["error", { singleline: "always" }]*/ -class Foo{ - bar(){} - - baz(){} -} - -/* eslint lines-between-class-members: ["error", { singleline: "never" }]*/ -class Foo{ - bar(){} - baz(){} -} -``` +* [padded-blocks](padded-blocks.md) +* [padding-line-between-statement](padding-line-between-statement.md) diff --git a/lib/rules/lines-between-class-members.js b/lib/rules/lines-between-class-members.js index ba95ba5ba3f6..48d16395bf80 100644 --- a/lib/rules/lines-between-class-members.js +++ b/lib/rules/lines-between-class-members.js @@ -28,27 +28,20 @@ module.exports = { type: "object", properties: { exceptAfterSingleLine: { - enum: [true, false] + type: "boolean" } }, - additionalProperties: false, - minProperties: 0 + additionalProperties: false } ] }, create(context) { - const options = {}; - const config = context.options[0] || "always"; + const options = []; - if (typeof config === "string") { - options.multiline = options.singleline = config; - } - - if (context.options[1].exceptAfterSingleLine) { - options.singleline = "never"; - } + options[0] = context.options[0] || "always"; + options[1] = context.options[1] || { exceptAfterSingleLine: false }; const ALWAYS_MESSAGE = "Expected blank line between class members."; const NEVER_MESSAGE = "Unexpected blank line between class members."; @@ -71,34 +64,34 @@ module.exports = { * @returns {void} undefined. */ function checkPadding(node) { - const body = node.body; for (let i = 0; i < body.length - 1; i++) { - // only check padding lines after methods definition. - if (body[i] && body[i].type !== "MethodDefinition") { - continue; - } - - const curFirst = sourceCode.getFirstToken(body[i]); - const curLast = sourceCode.getLastToken(body[i]); - const comments = sourceCode.getCommentsBefore(body[i + 1]); - const nextFirst = comments.length ? comments[0] : sourceCode.getFirstToken(body[i + 1]); - const isPadded = isPaddingBetweenTokens(curLast, nextFirst); - const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast); - - if ((isMulti && options.multiline && (options.multiline === "always") !== isPadded) || - !isMulti && options.singleline && (options.singleline === "always") !== isPadded) { - context.report({ - node: body[i + 1], - message: isPadded ? NEVER_MESSAGE : ALWAYS_MESSAGE, - fix(fixer) { - return isPadded - ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n") - : fixer.insertTextAfter(curLast, "\n"); - } - }); + // only check padding lines after class members(skip empty). + if (body[i]) { + + const curFirst = sourceCode.getFirstToken(body[i]); + const curLast = sourceCode.getLastToken(body[i]); + const comments = sourceCode.getCommentsBefore(body[i + 1]); + const nextFirst = comments.length ? comments[0] : sourceCode.getFirstToken(body[i + 1]); + const isPadded = isPaddingBetweenTokens(curLast, nextFirst); + const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast); + const skip = !isMulti && options[1].exceptAfterSingleLine; + + + if ((options[0] === "always" && !skip && !isPadded) || + (options[0] === "never" && isPadded)) { + context.report({ + node: body[i + 1], + message: isPadded ? NEVER_MESSAGE : ALWAYS_MESSAGE, + fix(fixer) { + return isPadded + ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n") + : fixer.insertTextAfter(curLast, "\n"); + } + }); + } } } } diff --git a/tests/lib/rules/lines-between-class-members.js b/tests/lib/rules/lines-between-class-members.js index 7e51c09f5a22..dfa17670b640 100644 --- a/tests/lib/rules/lines-between-class-members.js +++ b/tests/lib/rules/lines-between-class-members.js @@ -25,7 +25,7 @@ const NEVER_MESSAGE = "Unexpected blank line between class members."; const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); -ruleTester.run("lines-between-class-methods", rule, { +ruleTester.run("lines-between-class-members", rule, { valid: [ "class foo{}", "class foo{\n\n}", @@ -44,124 +44,25 @@ ruleTester.run("lines-between-class-methods", rule, { { code: "class foo{ bar(){}\n\n/*comments*/baz(){}}", options: ["always"] }, { code: "class foo{ bar(){}\n\n//comments\nbaz(){}}", options: ["always"] }, - { code: "class foo{ bar(){\n}\n\nbaz(){}}", options: [{ multiline: "always" }] }, - { code: "class foo{ bar(){\n}\n\n/*comments*/baz(){}}", options: [{ multiline: "always" }] }, - { code: "class foo{ bar(){\n}\n\n//comments\nbaz(){}}", options: [{ multiline: "always" }] }, - { code: "class foo{ bar(){}\nbaz(){}}", options: [{ multiline: "always" }] }, - { code: "class foo{ bar(){}\n\nbaz(){}}", options: [{ multiline: "always" }] }, - - { code: "class foo{ bar(){\n}\nbaz(){}}", options: [{ multiline: "never" }] }, - { code: "class foo{ bar(){\n}\n/*comments*/baz(){}}", options: [{ multiline: "never" }] }, - { code: "class foo{ bar(){\n}\n//comments\nbaz(){}}", options: [{ multiline: "never" }] }, - { code: "class foo{ bar(){}\nbaz(){}}", options: [{ multiline: "never" }] }, - { code: "class foo{ bar(){}\n\nbaz(){}}", options: [{ multiline: "never" }] }, - - { code: "class foo{ bar(){}\n\nbaz(){}}", options: [{ singleline: "always" }] }, - { code: "class foo{ bar(){}\n\n/*comments*/baz(){}}", options: [{ singleline: "always" }] }, - { code: "class foo{ bar(){}\n\n//comments\nbaz(){}}", options: [{ singleline: "always" }] }, - - { code: "class foo{ bar(){}\nbaz(){}}", options: [{ singleline: "never" }] }, - { code: "class foo{ bar(){}\n/*comments*/baz(){}}", options: [{ singleline: "never" }] }, - { code: "class foo{ bar(){}\n//comments\nbaz(){}}", options: [{ singleline: "never" }] } + { code: "class foo{ bar(){}\nbaz(){}}", options: ["always", { exceptAfterSingleLine: true }] }, + { code: "class foo{ bar(){\n}\n\nbaz(){}}", options: ["always", { exceptAfterSingleLine: true }] } ], invalid: [ { code: "class foo{ bar(){}\nbaz(){}}", output: "class foo{ bar(){}\n\nbaz(){}}", - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n/*comments*/baz(){}}", - output: "class foo{ bar(){}\n\n/*comments*/baz(){}}", - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n//comments\nbaz(){}}", - output: "class foo{ bar(){}\n\n//comments\nbaz(){}}", + options: ["always"], errors: [{ message: ALWAYS_MESSAGE }] }, { code: "class foo{ bar(){}\n\nbaz(){}}", output: "class foo{ bar(){}\nbaz(){}}", options: ["never"], errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\n/*comments*/baz(){}}", - output: "class foo{ bar(){}\n/*comments*/baz(){}}", - options: ["never"], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\n//comments\nbaz(){}}", - output: "class foo{ bar(){}\n//comments\nbaz(){}}", - options: ["never"], - errors: [{ message: NEVER_MESSAGE }] }, { code: "class foo{ bar(){\n}\nbaz(){}}", output: "class foo{ bar(){\n}\n\nbaz(){}}", - options: [{ multiline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n/*comments*/baz(){}}", - output: "class foo{ bar(){\n}\n\n/*comments*/baz(){}}", - options: [{ multiline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n//comments\nbaz(){}}", - output: "class foo{ bar(){\n}\n\n//comments\nbaz(){}}", - options: [{ multiline: "always" }], + options: ["always", { exceptAfterSingleLine: true }], errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n\nbaz(){}}", - output: "class foo{ bar(){\n}\nbaz(){}}", - options: [{ multiline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n\n/*comments*/baz(){}}", - output: "class foo{ bar(){\n}\n/*comments*/baz(){}}", - options: [{ multiline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n\n//comments\nbaz(){}}", - output: "class foo{ bar(){\n}\n//comments\nbaz(){}}", - options: [{ multiline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\nbaz(){}}", - output: "class foo{ bar(){}\n\nbaz(){}}", - options: [{ singleline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n/*comments*/baz(){}}", - output: "class foo{ bar(){}\n\n/*comments*/baz(){}}", - options: [{ singleline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n//comments\nbaz(){}}", - output: "class foo{ bar(){}\n\n//comments\nbaz(){}}", - options: [{ singleline: "always" }], - errors: [{ message: ALWAYS_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\nbaz(){}}", - output: "class foo{ bar(){}\nbaz(){}}", - options: [{ singleline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\n/*comments*/baz(){}}", - output: "class foo{ bar(){}\n/*comments*/baz(){}}", - options: [{ singleline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){}\n\n//comments\nbaz(){}}", - output: "class foo{ bar(){}\n//comments\nbaz(){}}", - options: [{ singleline: "never" }], - errors: [{ message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\nbaz(){}\n\nfn(){}}", - output: "class foo{ bar(){\n}\n\nbaz(){}\nfn(){}}", - options: [{ multiline: "always", singleline: "never" }], - errors: [{ message: ALWAYS_MESSAGE }, { message: NEVER_MESSAGE }] - }, { - code: "class foo{ bar(){\n}\n\nbaz(){}\nfn(){}}", - output: "class foo{ bar(){\n}\nbaz(){}\n\nfn(){}}", - options: [{ multiline: "never", singleline: "always" }], - errors: [{ message: NEVER_MESSAGE }, { message: ALWAYS_MESSAGE }] } ] });