diff --git a/lib/rules/key-spacing.js b/lib/rules/key-spacing.js index 15734501565..1cf677865d2 100644 --- a/lib/rules/key-spacing.js +++ b/lib/rules/key-spacing.js @@ -118,6 +118,8 @@ module.exports = { recommended: false }, + fixable: "whitespace", + schema: [{ anyOf: [ { @@ -270,9 +272,16 @@ module.exports = { */ function report(property, side, whitespace, expected, mode) { var diff = whitespace.length - expected, - key = property.key, - firstTokenAfterColon = sourceCode.getTokenAfter(getNextColon(key)), - location = side === "key" ? key.loc.start : firstTokenAfterColon.loc.start; + nextColon = getNextColon(property.key), + tokenBeforeColon = sourceCode.getTokenBefore(nextColon), + tokenAfterColon = sourceCode.getTokenAfter(nextColon), + isKeySide = side === "key", + locStart = isKeySide ? tokenBeforeColon.loc.start : tokenAfterColon.loc.start, + isExtra = diff > 0, + diffAbs = Math.abs(diff), + spaces = Array(diffAbs + 1).join(" "), + fix, + range; if (( diff && mode === "strict" || @@ -280,10 +289,41 @@ module.exports = { diff > 0 && !expected && mode === "minimum") && !(expected && containsLineTerminator(whitespace)) ) { - context.report(property[side], location, messages[side], { - error: diff > 0 ? "Extra" : "Missing", - computed: property.computed ? "computed " : "", - key: getKey(property) + if (isExtra) { + + // Remove whitespace + if (isKeySide) { + range = [tokenBeforeColon.end, tokenBeforeColon.end + diffAbs]; + } else { + range = [tokenAfterColon.start - diffAbs, tokenAfterColon.start]; + } + fix = function(fixer) { + return fixer.removeRange(range); + }; + } else { + + // Add whitespace + if (isKeySide) { + fix = function(fixer) { + return fixer.insertTextAfter(tokenBeforeColon, spaces); + }; + } else { + fix = function(fixer) { + return fixer.insertTextBefore(tokenAfterColon, spaces); + }; + } + } + + context.report({ + node: property[side], + loc: locStart, + message: messages[side], + data: { + error: isExtra ? "Extra" : "Missing", + computed: property.computed ? "computed " : "", + key: getKey(property) + }, + fix: fix }); } } diff --git a/tests/lib/rules/key-spacing.js b/tests/lib/rules/key-spacing.js index b4eca277df7..f7e6c314bbf 100644 --- a/tests/lib/rules/key-spacing.js +++ b/tests/lib/rules/key-spacing.js @@ -511,6 +511,7 @@ ruleTester.run("key-spacing", rule, { invalid: [{ code: "var bat = function() { return { foo:bar, 'key': value }; };", + output: "var bat = function() { return { foo:bar, 'key':value }; };", options: [{ beforeColon: false, afterColon: false @@ -518,11 +519,13 @@ ruleTester.run("key-spacing", rule, { errors: [{ message: "Extra space before value for key 'key'.", type: "Identifier", line: 1, column: 49 }] }, { code: "var obj = { [ (a + b) ]:value };", + output: "var obj = { [ (a + b) ]: value };", options: [{}], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Missing space before value for computed key 'a + b'.", type: "Identifier", line: 1, column: 25 }] }, { code: "fn({ foo:bar, 'key' :value });", + output: "fn({ foo:bar, 'key':value });", options: [{ beforeColon: false, afterColon: false @@ -530,6 +533,7 @@ ruleTester.run("key-spacing", rule, { errors: [{ message: "Extra space after key 'key'.", type: "Literal", line: 1, column: 15 }] }, { code: "var obj = {prop :(42)};", + output: "var obj = {prop : (42)};", options: [{ beforeColon: true, afterColon: true @@ -537,6 +541,7 @@ ruleTester.run("key-spacing", rule, { errors: [{ message: "Missing space before value for key 'prop'.", type: "Literal", line: 1, column: 18 }] }, { code: "({'a' : foo, b: bar() }).b();", + output: "({'a' : foo, b : bar() }).b();", options: [{ beforeColon: true, afterColon: true @@ -544,6 +549,7 @@ ruleTester.run("key-spacing", rule, { errors: [{ message: "Missing space after key 'b'.", type: "Identifier", line: 1, column: 14 }] }, { code: "({'a' :foo(), b: bar() }).b();", + output: "({'a' : foo(), b : bar() }).b();", options: [{ beforeColon: true, afterColon: true @@ -556,6 +562,7 @@ ruleTester.run("key-spacing", rule, { ] }, { code: "bar = { key:value };", + output: "bar = { key: value };", options: [{ beforeColon: false, afterColon: true @@ -569,6 +576,13 @@ ruleTester.run("key-spacing", rule, { " 'a' : (2 * 2)", "};" ].join("\n"), + output: [ + "obj = {", + " key : value,", + " foobar: fn(),", + " 'a' : (2 * 2)", + "};" + ].join("\n"), options: [{ align: "colon" }], @@ -586,6 +600,14 @@ ruleTester.run("key-spacing", rule, { " c :call()", "}).a();" ].join("\n"), + output: [ + "({", + " 'a' :val,", + " foo :fn(),", + " b :[42],", + " c :call()", + "}).a();" + ].join("\n"), options: [{ align: "colon", beforeColon: true, @@ -606,6 +628,15 @@ ruleTester.run("key-spacing", rule, { " [a] : value", "};" ].join("\n"), + output: [ + "var obj = {", + " a: fn(),", + " 'b': 42,", + " foo: (bar),", + " bat: 'valid',", + " [a]: value", + "};" + ].join("\n"), options: [{ align: "value" }], @@ -614,7 +645,7 @@ ruleTester.run("key-spacing", rule, { { message: "Extra space before value for key 'a'.", type: "CallExpression", line: 2, column: 11 }, { message: "Extra space after key 'b'.", type: "Literal", line: 3, column: 5 }, { message: "Missing space before value for key 'foo'.", type: "Identifier", line: 4, column: 9 }, - { message: "Extra space after computed key 'a'.", type: "Identifier", line: 6, column: 6 } + { message: "Extra space after computed key 'a'.", type: "Identifier", line: 6, column: 7 } ] }, { code: [ @@ -625,6 +656,14 @@ ruleTester.run("key-spacing", rule, { " bar : call()", "};" ].join("\n"), + output: [ + "foo = {", + " a : value,", + " b : 42,", + " foo :['a'],", + " bar :call()", + "};" + ].join("\n"), options: [{ align: "value", beforeColon: true, @@ -644,6 +683,15 @@ ruleTester.run("key-spacing", rule, { " fg:0", "})" ].join("\n"), + output: [ + "({", + " a : 0,", + " bcd: 0,", + "", + " e : 0,", + " fg: 0", + "})" + ].join("\n"), options: [{ align: "colon" }], @@ -661,6 +709,12 @@ ruleTester.run("key-spacing", rule, { " :anotherLongValue", "};" ].join("\n"), + output: [ + "foo = {", + " key:longValueName,", + " key2:anotherLongValue", + "};" + ].join("\n"), options: [{ beforeColon: false, afterColon: false @@ -680,6 +734,16 @@ ruleTester.run("key-spacing", rule, { " key123: 'forty two'", "};" ].join("\n"), + output: [ + "foo = {", + " key1: 42,", + " // still the same group", + " key12: '42', /*", + "", + " */", + " key123: 'forty two'", + "};" + ].join("\n"), options: [{ align: "value" }], @@ -689,16 +753,19 @@ ruleTester.run("key-spacing", rule, { ] }, { code: "foo = { key:(1+2) };", + output: "foo = { key: (1+2) };", errors: [ { message: "Missing space before value for key 'key'.", line: 1, column: 13, type: "BinaryExpression" } ] }, { code: "foo = { key:( ( (1+2) ) ) };", + output: "foo = { key: ( ( (1+2) ) ) };", errors: [ { message: "Missing space before value for key 'key'.", line: 1, column: 13, type: "BinaryExpression" } ] }, { code: "var obj = {a : 'foo', bar: 'bam'};", + output: "var obj = {a: 'foo', bar: 'bam'};", options: [{ align: "colon" }], errors: [ { message: "Extra space after key 'a'.", line: 1, column: 12, type: "Identifier" } @@ -710,6 +777,12 @@ ruleTester.run("key-spacing", rule, { " , b : 20", "};" ].join("\n"), + output: [ + "var x = {", + " foo: 10", + " , b : 20", + "};" + ].join("\n"), options: [{ align: "colon" }], errors: [ { message: "Extra space after key 'b'.", line: 3, column: 5, type: "Identifier" } @@ -721,6 +794,12 @@ ruleTester.run("key-spacing", rule, { " /*lol*/ b : 20", "};" ].join("\n"), + output: [ + "var x = {", + " foo : 10,", + " /*lol*/ b : 20", + "};" + ].join("\n"), options: [{ align: "colon", beforeColon: true }], errors: [ { message: "Missing space after key 'b'.", line: 3, column: 11, type: "Identifier" } @@ -730,6 +809,10 @@ ruleTester.run("key-spacing", rule, { "obj = { key ", " : longName };" ].join("\n"), + output: [ + "obj = { key ", + " : longName };" + ].join("\n"), options: [{ beforeColon: true, afterColon: true @@ -745,6 +828,13 @@ ruleTester.run("key-spacing", rule, { " baz: 456", "};" ].join("\n"), + output: [ + "var obj = {", + " foobar: 123,", + " prop,", + " baz: 456", + "};" + ].join("\n"), parserOptions: { ecmaVersion: 6 }, options: [{ align: "value" }], errors: [ @@ -758,6 +848,13 @@ ruleTester.run("key-spacing", rule, { " baz: 456", "};" ].join("\n"), + output: [ + "var obj = {", + " foobar: 123,", + " prop,", + " baz: 456", + "};" + ].join("\n"), parserOptions: { ecmaVersion: 6 }, options: [{ align: "value" }], errors: [ @@ -771,6 +868,13 @@ ruleTester.run("key-spacing", rule, { " baz: 456", "};" ].join("\n"), + output: [ + "var obj = {", + " foobar: 123,", + " method() { },", + " baz: 456", + "};" + ].join("\n"), parserOptions: { ecmaVersion: 6 }, options: [{ align: "value" }], errors: [ @@ -784,6 +888,13 @@ ruleTester.run("key-spacing", rule, { " baz: 456", "};" ].join("\n"), + output: [ + "var obj = {", + " foobar: 123,", + " method() { },", + " baz: 456", + "};" + ].join("\n"), parserOptions: { ecmaVersion: 6 }, options: [{ align: "value" }], errors: [ @@ -799,6 +910,15 @@ ruleTester.run("key-spacing", rule, { " baz: 456", "};" ].join("\n"), + output: [ + "var obj = {", + " foobar: 123,", + " method() {", + " return 42;", + " },", + " baz: 456", + "};" + ].join("\n"), parserOptions: { ecmaVersion: 6 }, options: [{ align: "value" }], errors: [ @@ -811,6 +931,12 @@ ruleTester.run("key-spacing", rule, { " , cats: cats", "};" ].join("\n"), + output: [ + "var obj = {", + " foo : foo", + " , cats: cats", + "};" + ].join("\n"), options: [{ align: "colon" }], errors: [ { message: "Missing space after key 'foo'.", line: 2, column: 5, type: "Identifier" } @@ -822,6 +948,12 @@ ruleTester.run("key-spacing", rule, { " , cats: cats", "};" ].join("\n"), + output: [ + "var obj = {", + " foo : foo", + " , cats: cats", + "};" + ].join("\n"), options: [{ align: "colon" }], errors: [ { message: "Extra space before value for key 'cats'.", line: 3, column: 12, type: "Identifier" } @@ -832,6 +964,11 @@ ruleTester.run("key-spacing", rule, { " , cats: cats", "};" ].join("\n"), + output: [ + "var obj = { foo : foo", + " , cats: cats", + "};" + ].join("\n"), options: [{ align: "colon" }], errors: [ { message: "Missing space after key 'foo'.", line: 1, column: 13, type: "Identifier" } @@ -852,6 +989,11 @@ ruleTester.run("key-spacing", rule, { " , cats: cats", "};" ].join("\n"), + output: [ + "var obj = { foo : foo", + " , cats: cats", + "};" + ].join("\n"), options: [{ align: "colon" }], errors: [ { message: "Missing space before value for key 'foo'.", line: 1, column: 18, type: "Identifier" } @@ -862,6 +1004,11 @@ ruleTester.run("key-spacing", rule, { " , cats: cats", "};" ].join("\n"), + output: [ + "var obj = { foo : foo", + " , cats: cats", + "};" + ].join("\n"), options: [{ align: "colon" }], errors: [ { message: "Extra space before value for key 'foo'.", line: 1, column: 20, type: "Identifier" } @@ -872,6 +1019,11 @@ ruleTester.run("key-spacing", rule, { " , cats: cats", "};" ].join("\n"), + output: [ + "var obj = { foo : foo", + " , cats: cats", + "};" + ].join("\n"), options: [{ align: "colon" }], errors: [ { message: "Extra space before value for key 'cats'.", line: 2, column: 20, type: "Identifier" } @@ -895,6 +1047,21 @@ ruleTester.run("key-spacing", rule, { " f : 0", "})" ].join("\n"), + output: [ + "({", + " ...x,", + " a : 0,", + " // same group", + " bcd: 0, /*", + " end of group */", + "", + " // different group", + " e: 0,", + " ...y,", + " /* group b */", + " f: 0", + "})" + ].join("\n"), options: [{ align: "colon" }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } }, errors: [ @@ -911,6 +1078,12 @@ ruleTester.run("key-spacing", rule, { " get b() { return 42; }", "})" ].join("\n"), + output: [ + "({", + " a: 42,", + " get b() { return 42; }", + "})" + ].join("\n"), options: [{ align: "colon" }], @@ -924,6 +1097,12 @@ ruleTester.run("key-spacing", rule, { " c : 42", "})" ].join("\n"), + output: [ + "({", + " set a(b) { b; },", + " c: 42", + "})" + ].join("\n"), options: [{ align: "value" }], @@ -939,6 +1118,14 @@ ruleTester.run("key-spacing", rule, { " def: 42", "})" ].join("\n"), + output: [ + "({", + " a : 42,", + " get b() { return 42; },", + " set c(v) { v; },", + " def: 42", + "})" + ].join("\n"), options: [{ align: "colon" }], @@ -955,6 +1142,15 @@ ruleTester.run("key-spacing", rule, { " def2 : {a1: 1, b1:2, c1:3}", "})" ].join("\n"), + output: [ + "({", + " a : 42,", + " get b() { return 42; },", + " set c(v) { v; },", + " def : 42,", + " def2 : {a1:1, b1:2, c1:3}", + "})" + ].join("\n"), options: [{ singleLine: { afterColon: false, @@ -980,6 +1176,15 @@ ruleTester.run("key-spacing", rule, { " de1: {a2: 1, b2 : 2, c2 : 3}", "})" ].join("\n"), + output: [ + "({", + " a : 42,", + " get b() { return 42; },", + " set c(v) { v; },", + " def: 42,", + " de1: {a2 : 1, b2 : 2, c2 : 3}", + "})" + ].join("\n"), options: [{ multiLine: { afterColon: true, @@ -1002,6 +1207,13 @@ ruleTester.run("key-spacing", rule, { " ex:e", "};" ].join("\n"), + output: [ + "obj = {", + " get fx() { return 'f'; },", + " get gx() { return 'g'; },", + " ex: e", + "};" + ].join("\n"), options: [{ align: "colon", beforeColon: false, @@ -1019,6 +1231,13 @@ ruleTester.run("key-spacing", rule, { " ex : e", "};" ].join("\n"), + output: [ + "obj = {", + " get fx() { return 'f'; },", + " get gx() { return 'g'; },", + " ex: e", + "};" + ].join("\n"), options: [{ align: "colon", beforeColon: false, @@ -1037,6 +1256,14 @@ ruleTester.run("key-spacing", rule, { " defInv: 43", "})" ].join("\n"), + output: [ + "({", + " aInv : 43,", + " get b() { return 43; },", + " set c(v) { v; },", + " defInv: 43", + "})" + ].join("\n"), options: [{ multiLine: { afterColon: true, @@ -1052,6 +1279,7 @@ ruleTester.run("key-spacing", rule, { // https://github.com/eslint/eslint/issues/5724 { code: "({ a:b, ...object, c : d })", + output: "({ a: b, ...object, c: d })", options: [{ align: "colon" }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { experimentalObjectRestSpread: true } }, errors: [