Merge pull request babel/eslint-plugin-babel#38 from zaygraveyard/issue36

Fix: `object-curly-spacing` had been crashing on an empty object patt…
This commit is contained in:
Jason Quense 2015-12-20 16:43:33 -05:00
parent 885bfe95dc
commit 9eebd7b057
2 changed files with 366 additions and 96 deletions

View File

@ -6,6 +6,8 @@
* @copyright 2014 Vignesh Anand. All rights reserved.
* @copyright 2015 Jamund Ferguson. All rights reserved.
* @copyright 2015 Mathieu M-Gosselin. All rights reserved.
* @copyright 2015 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
"use strict";
@ -14,7 +16,8 @@
//------------------------------------------------------------------------------
module.exports = function(context) {
var spaced = context.options[0] === "always";
var spaced = context.options[0] === "always",
sourceCode = context.getSourceCode();
/**
* Determines whether an option is set, relative to the spacing option.
@ -44,7 +47,7 @@ module.exports = function(context) {
* @returns {boolean} Whether or not there is space between the tokens.
*/
function isSpaced(left, right) {
return left.range[1] < right.range[0];
return sourceCode.isSpaceBetweenTokens(left, right);
}
/**
@ -64,7 +67,7 @@ module.exports = function(context) {
* @returns {void}
*/
function reportNoBeginningSpace(node, token) {
context.report(node, token.loc.start,
context.report(node, token.loc.end,
"There should be no space after '" + token.value + "'");
}
@ -86,7 +89,7 @@ module.exports = function(context) {
* @returns {void}
*/
function reportRequiredBeginningSpace(node, token) {
context.report(node, token.loc.start,
context.report(node, token.loc.end,
"A space is required after '" + token.value + "'");
}
@ -135,96 +138,115 @@ module.exports = function(context) {
}
}
/**
* Reports a given object node if spacing in curly braces is invalid.
* @param {ASTNode} node - An ObjectExpression or ObjectPattern node to check.
* @returns {void}
*/
function checkForObject(node) {
if (node.properties.length === 0) {
return;
}
var firstSpecifier = node.properties[0],
lastSpecifier = node.properties[node.properties.length - 1];
var first = sourceCode.getTokenBefore(firstSpecifier),
last = sourceCode.getTokenAfter(lastSpecifier);
// support trailing commas
if (last.value === ",") {
last = sourceCode.getTokenAfter(last);
}
var second = sourceCode.getTokenAfter(first),
penultimate = sourceCode.getTokenBefore(last);
validateBraceSpacing(node, first, second, penultimate, last);
}
/**
* Reports a given import node if spacing in curly braces is invalid.
* @param {ASTNode} node - An ImportDeclaration node to check.
* @returns {void}
*/
function checkForImport(node) {
if (node.specifiers.length === 0) {
return;
}
var firstSpecifier = node.specifiers[0],
lastSpecifier = node.specifiers[node.specifiers.length - 1];
if (lastSpecifier.type !== "ImportSpecifier") {
return;
}
if (firstSpecifier.type !== "ImportSpecifier") {
firstSpecifier = node.specifiers[1];
}
var first = sourceCode.getTokenBefore(firstSpecifier),
last = sourceCode.getTokenAfter(lastSpecifier);
// to support a trailing comma.
if (last.value === ",") {
last = sourceCode.getTokenAfter(last);
}
var second = sourceCode.getTokenAfter(first),
penultimate = sourceCode.getTokenBefore(last);
validateBraceSpacing(node, first, second, penultimate, last);
}
/**
* Reports a given export node if spacing in curly braces is invalid.
* @param {ASTNode} node - An ExportNamedDeclaration node to check.
* @returns {void}
*/
function checkForExport(node) {
if (node.specifiers.length === 0) {
return;
}
var firstSpecifier = node.specifiers[0],
lastSpecifier = node.specifiers[node.specifiers.length - 1],
first = sourceCode.getTokenBefore(firstSpecifier),
last = sourceCode.getTokenAfter(lastSpecifier);
// export * as x from '...';
// export x from '...';
if (first.value === "export") {
return;
}
// to support a trailing comma.
if (last.value === ",") {
last = sourceCode.getTokenAfter(last);
}
var second = sourceCode.getTokenAfter(first),
penultimate = sourceCode.getTokenBefore(last);
validateBraceSpacing(node, first, second, penultimate, last);
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
// var {x} = y;
ObjectPattern: function(node) {
var firstSpecifier = node.properties[0],
lastSpecifier = node.properties[node.properties.length - 1];
var first = context.getTokenBefore(firstSpecifier),
second = context.getFirstToken(firstSpecifier),
penultimate = context.getLastToken(lastSpecifier),
last = context.getTokenAfter(lastSpecifier);
// support trailing commas
if (last.value === ",") {
penultimate = last;
last = context.getTokenAfter(last);
}
validateBraceSpacing(node, first, second, penultimate, last);
},
// import {y} from 'x';
ImportDeclaration: function(node) {
var firstSpecifier = node.specifiers[0],
lastSpecifier = node.specifiers[node.specifiers.length - 1];
// don't do anything for namespace or default imports
if (firstSpecifier && lastSpecifier && firstSpecifier.type === "ImportSpecifier" && lastSpecifier.type === "ImportSpecifier") {
var first = context.getTokenBefore(firstSpecifier),
second = context.getFirstToken(firstSpecifier),
penultimate = context.getLastToken(lastSpecifier),
last = context.getTokenAfter(lastSpecifier);
// support trailing commas
if (last.value === ",") {
penultimate = last;
last = context.getTokenAfter(last);
}
validateBraceSpacing(node, first, second, penultimate, last);
}
},
// export {name} from 'yo';
ExportNamedDeclaration: function(node) {
if (!node.specifiers.length) {
return;
}
var firstSpecifier = node.specifiers[0],
lastSpecifier = node.specifiers[node.specifiers.length - 1],
first = context.getTokenBefore(firstSpecifier),
second = context.getFirstToken(firstSpecifier),
penultimate = context.getLastToken(lastSpecifier),
last = context.getTokenAfter(lastSpecifier);
if (first.value === "export") {
return;
}
// support trailing commas
if (last.value === ",") {
penultimate = last;
last = context.getTokenAfter(last);
}
validateBraceSpacing(node, first, second, penultimate, last);
},
ObjectPattern: checkForObject,
// var y = {x: 'y'}
ObjectExpression: function(node) {
if (node.properties.length === 0) {
return;
}
ObjectExpression: checkForObject,
var first = context.getFirstToken(node),
second = context.getFirstToken(node, 1),
penultimate = context.getLastToken(node, 1),
last = context.getLastToken(node);
validateBraceSpacing(node, first, second, penultimate, last);
}
// import {y} from 'x';
ImportDeclaration: checkForImport,
// export {name} from 'yo';
ExportNamedDeclaration: checkForExport
};
};

View File

@ -31,18 +31,28 @@ ruleTester.run('babel/object-curly-spacing', rule, {
{ code: "var { x: { z }, y } = y", options: ["always"], ecmaFeatures: { destructuring: true } },
{ code: "var {\ny,\n} = x", options: ["always"], ecmaFeatures: { destructuring: true } },
{ code: "var { y, } = x", options: ["always"], ecmaFeatures: { destructuring: true } },
{ code: "var { y: x } = x", options: ["always"], ecmaFeatures: { destructuring: true } },
// always - import / export
{ code: "import door from 'room'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import * as door from 'room'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import { door } from 'room'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import {\ndoor } from 'room'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "export { door } from 'room'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import { house, mouse } from 'caravan'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import {\nhouse,\nmouse\n} from 'caravan'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import {\nhouse,\nmouse,\n} from 'caravan'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import house, { mouse } from 'caravan'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import door, { house, mouse } from 'caravan'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "export { door }", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "export {\ndoor,\nhouse\n}", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "export {\ndoor,\nhouse,\n}", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import 'room'", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import { bar as x } from 'foo';", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import { x, } from 'foo';", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "import {\nx,\n} from 'foo';", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "export { x, } from 'foo';", options: ["always"], ecmaFeatures: { modules: true } },
{ code: "export {\nx,\n} from 'foo';", options: ["always"], ecmaFeatures: { modules: true } },
// always - empty object
{ code: "var foo = {};", options: ["always"] },
@ -81,8 +91,11 @@ ruleTester.run('babel/object-curly-spacing', rule, {
{ code: "var {\nx: {z\n}, y} = y", options: ["never"], ecmaFeatures: { destructuring: true } },
{ code: "var {\ny,\n} = x", options: ["never"], ecmaFeatures: { destructuring: true } },
{ code: "var {y,} = x", options: ["never"], ecmaFeatures: { destructuring: true } },
{ code: "var {y:x} = x", options: ["never"], ecmaFeatures: { destructuring: true } },
// never - import / export
{ code: "import door from 'room'", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "import * as door from 'room'", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "import {door} from 'room'", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "export {door} from 'room'", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "import {\ndoor} from 'room'", options: ["never"], ecmaFeatures: { modules: true } },
@ -95,6 +108,14 @@ ruleTester.run('babel/object-curly-spacing', rule, {
{ code: "export {\ndoor,\nmouse\n}", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "export {\ndoor,\nmouse,\n}", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "import 'room'", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "import x, {bar} from 'foo';", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "import x, {bar, baz} from 'foo';", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "import {bar as y} from 'foo';", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "import {x,} from 'foo';", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "import {\nx,\n} from 'foo';", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "export {x,} from 'foo';", options: ["never"], ecmaFeatures: { modules: true } },
{ code: "export {\nx,\n} from 'foo';", options: ["never"], ecmaFeatures: { modules: true } },
// never - empty object
{ code: "var foo = {};", options: ["never"] },
@ -102,9 +123,32 @@ ruleTester.run('babel/object-curly-spacing', rule, {
// never - objectsInObjects
{ code: "var obj = {'foo': {'bar': 1, 'baz': 2} };", options: ["never", {"objectsInObjects": true}]},
// https://github.com/eslint/eslint/issues/3658
// Empty cases.
{ code: "var {} = foo;", ecmaFeatures: { destructuring: true }},
{ code: "var [] = foo;", ecmaFeatures: { destructuring: true }},
{ code: "var {a: {}} = foo;", ecmaFeatures: { destructuring: true }},
{ code: "var {a: []} = foo;", ecmaFeatures: { destructuring: true }},
{ code: "import {} from 'foo';", ecmaFeatures: { modules: true }},
{ code: "export {} from 'foo';", ecmaFeatures: { modules: true }},
{ code: "export {};", ecmaFeatures: { modules: true }},
{ code: "var {} = foo;", options: ["never"], ecmaFeatures: { destructuring: true }},
{ code: "var [] = foo;", options: ["never"], ecmaFeatures: { destructuring: true }},
{ code: "var {a: {}} = foo;", options: ["never"], ecmaFeatures: { destructuring: true }},
{ code: "var {a: []} = foo;", options: ["never"], ecmaFeatures: { destructuring: true }},
{ code: "import {} from 'foo';", options: ["never"], ecmaFeatures: { modules: true }},
{ code: "export {} from 'foo';", options: ["never"], ecmaFeatures: { modules: true }},
{ code: "export {};", options: ["never"], ecmaFeatures: { modules: true }},
// Babel test cases.
{ code: "export * as x from \"mod\";", parser: "babel-eslint", ecmaFeatures: { modules: true } },
{ code: "export x from \"mod\";", parser: "babel-eslint", ecmaFeatures: { modules: true } },
// always - destructuring typed object param
{ code: "function fn({ a,b }:Object){}", options: ["always"], parser: "babel-eslint", ecmaFeatures: { destructuring: true } },
// never - destructuring typed object param
{ code: "function fn({a,b}: Object){}", options: ["never"], parser: "babel-eslint", ecmaFeatures: { destructuring: true } },
],
invalid: [
@ -119,7 +163,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "A space is required after '{'",
type: "ImportDeclaration",
line: 1,
column: 8
column: 9
},
{
message: "A space is required before '}'",
@ -129,6 +173,175 @@ ruleTester.run('babel/object-curly-spacing', rule, {
}
]
},
{
code: "import { bar as y} from 'foo.js';",
options: ["always"],
ecmaFeatures: {
modules: true
},
errors: [
{
message: "A space is required before '}'",
type: "ImportDeclaration",
line: 1,
column: 18
}
]
},
{
code: "import {bar as y} from 'foo.js';",
options: ["always"],
ecmaFeatures: {
modules: true
},
errors: [
{
message: "A space is required after '{'",
type: "ImportDeclaration",
line: 1,
column: 9
},
{
message: "A space is required before '}'",
type: "ImportDeclaration",
line: 1,
column: 17
}
]
},
{
code: "import { bar} from 'foo.js';",
options: ["always"],
ecmaFeatures: {
modules: true
},
errors: [
{
message: "A space is required before '}'",
type: "ImportDeclaration",
line: 1,
column: 13
}
]
},
{
code: "import x, { bar} from 'foo';",
options: ["always"],
ecmaFeatures: {
modules: true
},
errors: [
{
message: "A space is required before '}'",
type: "ImportDeclaration",
line: 1,
column: 16
}
]
},
{
code: "import x, { bar, baz} from 'foo';",
options: ["always"],
ecmaFeatures: {
modules: true
},
errors: [
{
message: "A space is required before '}'",
type: "ImportDeclaration",
line: 1,
column: 21
}
]
},
{
code: "import x, {bar} from 'foo';",
options: ["always"],
ecmaFeatures: {
modules: true
},
errors: [
{
message: "A space is required after '{'",
type: "ImportDeclaration",
line: 1,
column: 12
},
{
message: "A space is required before '}'",
type: "ImportDeclaration",
line: 1,
column: 15
}
]
},
{
code: "import x, {bar, baz} from 'foo';",
options: ["always"],
ecmaFeatures: {
modules: true
},
errors: [
{
message: "A space is required after '{'",
type: "ImportDeclaration",
line: 1,
column: 12
},
{
message: "A space is required before '}'",
type: "ImportDeclaration",
line: 1,
column: 20
}
]
},
{
code: "import {bar,} from 'foo';",
options: ["always"],
ecmaFeatures: {
modules: true
},
errors: [
{
message: "A space is required after '{'",
type: "ImportDeclaration",
line: 1,
column: 9
},
{
message: "A space is required before '}'",
type: "ImportDeclaration",
line: 1,
column: 13
}
]
},
{
code: "import { bar, } from 'foo';",
options: ["never"],
ecmaFeatures: {
modules: true
},
errors: [
{
message: "There should be no space after '{'",
type: "ImportDeclaration",
line: 1,
column: 9
},
{
message: "There should be no space before '}'",
type: "ImportDeclaration",
line: 1,
column: 15
}
]
},
{
code: "export {bar};",
options: ["always"],
@ -140,7 +353,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "A space is required after '{'",
type: "ExportNamedDeclaration",
line: 1,
column: 8
column: 9
},
{
message: "A space is required before '}'",
@ -226,6 +439,38 @@ ruleTester.run('babel/object-curly-spacing', rule, {
}
]
},
{
code: "var {a:b } = x;",
options: ["never"],
ecmaFeatures: { destructuring: true },
errors: [
{
message: "There should be no space before '}'",
type: "ObjectPattern",
line: 1,
column: 10
}
]
},
{
code: "var { a:b } = x;",
options: ["never"],
ecmaFeatures: { destructuring: true },
errors: [
{
message: "There should be no space after '{'",
type: "ObjectPattern",
line: 1,
column: 6
},
{
message: "There should be no space before '}'",
type: "ObjectPattern",
line: 1,
column: 11
}
]
},
// never-objectsInObjects
{
@ -262,7 +507,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "A space is required after '{'",
type: "ObjectExpression",
line: 1,
column: 11
column: 12
},
{
message: "A space is required before '}'",
@ -280,7 +525,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "A space is required after '{'",
type: "ObjectExpression",
line: 1,
column: 11
column: 12
}
]
},
@ -304,7 +549,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "There should be no space after '{'",
type: "ObjectExpression",
line: 1,
column: 11
column: 12
},
{
message: "There should be no space before '}'",
@ -334,7 +579,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "There should be no space after '{'",
type: "ObjectExpression",
line: 1,
column: 11
column: 12
}
]
},
@ -346,13 +591,13 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "There should be no space after '{'",
type: "ObjectExpression",
line: 1,
column: 11
column: 12
},
{
message: "There should be no space after '{'",
type: "ObjectExpression",
line: 1,
column: 18
column: 19
}
]
},
@ -386,7 +631,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "A space is required after '{'",
type: "ObjectExpression",
line: 1,
column: 22
column: 23
}
]
},
@ -394,6 +639,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
// destructuring
{
code: "var {x, y} = y",
utput: "var { x, y = y",
ecmaFeatures: {destructuring: true},
options: ["always"],
errors: [
@ -401,7 +647,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "A space is required after '{'",
type: "ObjectPattern",
line: 1,
column: 5
column: 6
},
{
message: "A space is required before '}'",
@ -413,6 +659,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
},
{
code: "var { x, y} = y",
ouput: "var { x, y } y",
ecmaFeatures: {destructuring: true},
options: ["always"],
errors: [
@ -426,6 +673,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
},
{
code: "var { x, y } = y",
ouput: "var {x, y} = ",
ecmaFeatures: {destructuring: true},
options: ["never"],
errors: [
@ -433,7 +681,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "There should be no space after '{'",
type: "ObjectPattern",
line: 1,
column: 5
column: 6
},
{
message: "There should be no space before '}'",
@ -478,7 +726,7 @@ ruleTester.run('babel/object-curly-spacing', rule, {
message: "A space is required after '{'",
type: "ObjectPattern",
line: 1,
column: 5
column: 6
}
]
},