diff --git a/eslint/babel-eslint-plugin/README.md b/eslint/babel-eslint-plugin/README.md index 48611f3cd3..a712dee52c 100644 --- a/eslint/babel-eslint-plugin/README.md +++ b/eslint/babel-eslint-plugin/README.md @@ -33,7 +33,8 @@ original ones as well!). "babel/object-shorthand": 1, "babel/arrow-parens": 1, "babel/no-await-in-loop": 1, - "babel/flow-object-type": 1 + "babel/flow-object-type": 1, + "babel/func-params-comma-dangle": 1 } } ``` @@ -57,3 +58,4 @@ the current JavaScript standard or supported by `eslint`. - `babel/flow-object-type`: Require a particular separator between properties in Flow object types. (🛠 ) - Use the option `semicolon` to require semicolons (e.g. `type Foo = { bar: number; baz: string }`). - Use the option `comma` to require commas (e.g. `type Foo = { bar: number, baz: string }`). +- `babel/func-params-comma-dangle`: Require or forbid trailing commas for function paramater lists. Behaves like, and takes the same options as, `eslint`'s [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle). (🛠 ) diff --git a/eslint/babel-eslint-plugin/index.js b/eslint/babel-eslint-plugin/index.js index 7a3f7f4617..3656458f0b 100644 --- a/eslint/babel-eslint-plugin/index.js +++ b/eslint/babel-eslint-plugin/index.js @@ -10,6 +10,7 @@ module.exports = { 'arrow-parens': require('./rules/arrow-parens'), 'no-await-in-loop': require('./rules/no-await-in-loop'), 'flow-object-type': require('./rules/flow-object-type'), + 'func-params-comma-dangle': require('./rules/func-params-comma-dangle'), }, rulesConfig: { 'generator-star-spacing': 0, @@ -20,5 +21,6 @@ module.exports = { 'arrow-parens': 0, 'no-await-in-loop': 0, 'flow-object-type': 0, + 'func-params-comma-dangle': 0, } }; diff --git a/eslint/babel-eslint-plugin/rules/func-params-comma-dangle.js b/eslint/babel-eslint-plugin/rules/func-params-comma-dangle.js new file mode 100644 index 0000000000..d750323d39 --- /dev/null +++ b/eslint/babel-eslint-plugin/rules/func-params-comma-dangle.js @@ -0,0 +1,137 @@ +'use strict'; + +// Based on https://github.com/eslint/eslint/blob/v2.11.1/lib/rules/comma-dangle.js + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +function last(arr) { + return arr.length !== 0 ? arr[arr.length - 1] : undefined; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = function(context) { + var mode = context.options[0]; + var UNEXPECTED_MESSAGE = 'Unexpected trailing comma.'; + var MISSING_MESSAGE = 'Missing trailing comma.'; + + function isMultiline(node) { + var lastItem = last(node.params || node.arguments); + + if (!lastItem) { + return false; + } + + var sourceCode = context.getSourceCode(); + var penultimateToken = sourceCode.getLastToken(lastItem); + var lastToken = sourceCode.getTokenAfter(penultimateToken); + + if (lastToken.value === ',') { + penultimateToken = lastToken; + lastToken = sourceCode.getTokenAfter(lastToken); + } + + return lastToken.loc.end.line !== penultimateToken.loc.end.line; + } + + function forbidTrailingComma(node) { + var lastItem = last(node.params || node.arguments); + + if (!lastItem) { + return; + } + + var sourceCode = context.getSourceCode(); + var trailingToken = sourceCode.getTokenAfter(lastItem); + + if (trailingToken.value === ',') { + context.report({ + node: lastItem, + loc: trailingToken.loc.start, + message: UNEXPECTED_MESSAGE, + fix: function(fixer) { + return fixer.remove(trailingToken); + } + }); + } + } + + function forceTrailingComma(node) { + var lastItem = last(node.params || node.arguments); + + if (!lastItem) { + return; + } + + // `f(...a,)` is ok, but `function f(...a,) {}` is invalid syntax. + if (lastItem.type === 'RestElement') { + return; + } + + var sourceCode = context.getSourceCode(); + var penultimateToken = lastItem; + var trailingToken = sourceCode.getTokenAfter(lastItem); + + // `f = a, => {}` is invalid syntax. + if (node.type === 'ArrowFunctionExpression' && + node.params.length === 1 && + sourceCode.getTokenBefore(lastItem).value !== '(') { + return; + } + + if (trailingToken.value !== ',') { + context.report({ + node: lastItem, + loc: lastItem.loc.end, + message: MISSING_MESSAGE, + fix: function(fixer) { + return fixer.insertTextAfter(penultimateToken, ','); + } + }); + } + } + + function forceTrailingCommaIfMultiline(node) { + if (isMultiline(node)) { + forceTrailingComma(node); + } else { + forbidTrailingComma(node); + } + } + + function allowTrailingCommaIfMultiline(node) { + if (!isMultiline(node)) { + forbidTrailingComma(node); + } + } + + var checkForTrailingComma; + if (mode === 'always') { + checkForTrailingComma = forceTrailingComma; + } else if (mode === 'always-multiline') { + checkForTrailingComma = forceTrailingCommaIfMultiline; + } else if (mode === 'only-multiline') { + checkForTrailingComma = allowTrailingCommaIfMultiline; + } else { + checkForTrailingComma = forbidTrailingComma; + } + + return { + ArrowFunctionExpression: checkForTrailingComma, + FunctionDeclaration: checkForTrailingComma, + FunctionExpression: checkForTrailingComma, + CallExpression: checkForTrailingComma, + NewExpression: checkForTrailingComma, + }; +}; + +module.exports.fixable = 'code'; +module.exports.schema = [ + { + enum: ['always', 'always-multiline', 'only-multiline', 'never'] + } +]; diff --git a/eslint/babel-eslint-plugin/tests/func-params-comma-dangle.js b/eslint/babel-eslint-plugin/tests/func-params-comma-dangle.js new file mode 100644 index 0000000000..9fc5c2110f --- /dev/null +++ b/eslint/babel-eslint-plugin/tests/func-params-comma-dangle.js @@ -0,0 +1,278 @@ +'use strict'; + +var rule = require('../rules/func-params-comma-dangle'); +var RuleTester = require('eslint').RuleTester; + +var MISSING_I = [{message: 'Missing trailing comma.', type: 'Identifier'}]; +var MISSING_AP = [{message: 'Missing trailing comma.', type: 'AssignmentPattern'}]; +var MISSING_SE = [{message: 'Missing trailing comma.', type: 'SpreadElement'}]; +var UNEXPECTED_I = [{message: 'Unexpected trailing comma.', type: 'Identifier'}]; +var UNEXPECTED_AP = [{message: 'Unexpected trailing comma.', type: 'AssignmentPattern'}]; +var UNEXPECTED_SE = [{message: 'Unexpected trailing comma.', type: 'SpreadElement'}]; + +var ruleTester = new RuleTester({parser: 'babel-eslint'}); +ruleTester.run('func-params-comma-dangle', rule, { + valid: [ + {code: 'function f() {}'}, + {code: 'function f(\n) {}'}, + {code: 'function f(...a) {}'}, + {code: 'function f(...a\n) {}'}, + + {code: 'f()'}, + {code: 'f(\n)'}, + + {code: 'new F()'}, + {code: 'new F(\n)'}, + + // FunctionDeclaration + {code: 'function f(a) {}', options: ['always-multiline']}, + {code: 'function f(a) {}', options: ['never']}, + {code: 'function f(a) {}', options: ['only-multiline']}, + {code: 'function f(a,) {}', options: ['always']}, + {code: 'function f(a,\n) {}', options: ['always']}, + {code: 'function f(a,\n) {}', options: ['always-multiline']}, + {code: 'function f(a,\n) {}', options: ['only-multiline']}, + {code: 'function f(a\n) {}', options: ['never']}, + {code: 'function f(a\n) {}', options: ['only-multiline']}, + + {code: 'function f(a=1) {}', options: ['always-multiline']}, + {code: 'function f(a=1) {}', options: ['never']}, + {code: 'function f(a=1) {}', options: ['only-multiline']}, + {code: 'function f(a=1,) {}', options: ['always']}, + {code: 'function f(a=1,\n) {}', options: ['always']}, + {code: 'function f(a=1,\n) {}', options: ['always-multiline']}, + {code: 'function f(a=1,\n) {}', options: ['only-multiline']}, + {code: 'function f(a=1\n) {}', options: ['never']}, + {code: 'function f(a=1\n) {}', options: ['only-multiline']}, + + {code: 'function f(a:T) {}', options: ['always-multiline']}, + {code: 'function f(a:T) {}', options: ['never']}, + {code: 'function f(a:T) {}', options: ['only-multiline']}, + {code: 'function f(a:T,) {}', options: ['always']}, + {code: 'function f(a:T,\n) {}', options: ['always']}, + {code: 'function f(a:T,\n) {}', options: ['always-multiline']}, + {code: 'function f(a:T,\n) {}', options: ['only-multiline']}, + {code: 'function f(a:T\n) {}', options: ['never']}, + {code: 'function f(a:T\n) {}', options: ['only-multiline']}, + + // FunctionExpression + {code: 'f = function(a) {}', options: ['always-multiline']}, + {code: 'f = function(a) {}', options: ['never']}, + {code: 'f = function(a) {}', options: ['only-multiline']}, + {code: 'f = function(a,) {}', options: ['always']}, + {code: 'f = function(a,\n) {}', options: ['always']}, + {code: 'f = function(a,\n) {}', options: ['always-multiline']}, + {code: 'f = function(a,\n) {}', options: ['only-multiline']}, + {code: 'f = function(a\n) {}', options: ['never']}, + {code: 'f = function(a\n) {}', options: ['only-multiline']}, + + {code: 'f = function(a=1) {}', options: ['always-multiline']}, + {code: 'f = function(a=1) {}', options: ['never']}, + {code: 'f = function(a=1) {}', options: ['only-multiline']}, + {code: 'f = function(a=1,) {}', options: ['always']}, + {code: 'f = function(a=1,\n) {}', options: ['always']}, + {code: 'f = function(a=1,\n) {}', options: ['always-multiline']}, + {code: 'f = function(a=1,\n) {}', options: ['only-multiline']}, + {code: 'f = function(a=1\n) {}', options: ['never']}, + {code: 'f = function(a=1\n) {}', options: ['only-multiline']}, + + {code: 'f = function(a:T) {}', options: ['always-multiline']}, + {code: 'f = function(a:T) {}', options: ['never']}, + {code: 'f = function(a:T) {}', options: ['only-multiline']}, + {code: 'f = function(a:T,) {}', options: ['always']}, + {code: 'f = function(a:T,\n) {}', options: ['always']}, + {code: 'f = function(a:T,\n) {}', options: ['always-multiline']}, + {code: 'f = function(a:T,\n) {}', options: ['only-multiline']}, + {code: 'f = function(a:T\n) {}', options: ['never']}, + {code: 'f = function(a:T\n) {}', options: ['only-multiline']}, + + // ArrowFunctionExpression + {code: 'f = (a) => {}', options: ['always-multiline']}, + {code: 'f = (a) => {}', options: ['never']}, + {code: 'f = (a) => {}', options: ['only-multiline']}, + {code: 'f = (a,) => {}', options: ['always']}, + {code: 'f = (a,\n) => {}', options: ['always']}, + {code: 'f = (a,\n) => {}', options: ['always-multiline']}, + {code: 'f = (a,\n) => {}', options: ['only-multiline']}, + {code: 'f = (a\n) => {}', options: ['never']}, + {code: 'f = (a\n) => {}', options: ['only-multiline']}, + + {code: 'f = a => {}', options: ['always-multiline']}, + {code: 'f = a => {}', options: ['never']}, + {code: 'f = a => {}', options: ['only-multiline']}, + {code: 'f = a => {}', options: ['always']}, + + {code: 'f = (a=1) => {}', options: ['always-multiline']}, + {code: 'f = (a=1) => {}', options: ['never']}, + {code: 'f = (a=1) => {}', options: ['only-multiline']}, + {code: 'f = (a=1,) => {}', options: ['always']}, + {code: 'f = (a=1,\n) => {}', options: ['always']}, + {code: 'f = (a=1,\n) => {}', options: ['always-multiline']}, + {code: 'f = (a=1,\n) => {}', options: ['only-multiline']}, + {code: 'f = (a=1\n) => {}', options: ['never']}, + {code: 'f = (a=1\n) => {}', options: ['only-multiline']}, + + {code: 'f = (a:T) => {}', options: ['always-multiline']}, + {code: 'f = (a:T) => {}', options: ['never']}, + {code: 'f = (a:T) => {}', options: ['only-multiline']}, + // Arrow functions with flow types aren't getting the correct loc. + // {code: 'f = (a:T,) => {}', options: ['always']}, + // {code: 'f = (a:T,\n) => {}', options: ['always']}, + {code: 'f = (a:T,\n) => {}', options: ['always-multiline']}, + {code: 'f = (a:T,\n) => {}', options: ['only-multiline']}, + {code: 'f = (a:T\n) => {}', options: ['never']}, + {code: 'f = (a:T\n) => {}', options: ['only-multiline']}, + + // CallExpression + {code: 'f(a)', options: ['always-multiline']}, + {code: 'f(a)', options: ['never']}, + {code: 'f(a)', options: ['only-multiline']}, + {code: 'f(a,)', options: ['always']}, + {code: 'f(a,\n)', options: ['always']}, + {code: 'f(a,\n)', options: ['always-multiline']}, + {code: 'f(a,\n)', options: ['only-multiline']}, + {code: 'f(a\n)', options: ['never']}, + {code: 'f(a\n)', options: ['only-multiline']}, + {code: 'f(...a)', options: ['always-multiline']}, + {code: 'f(...a)', options: ['never']}, + {code: 'f(...a)', options: ['only-multiline']}, + {code: 'f(...a,)', options: ['always']}, + {code: 'f(...a,\n)', options: ['always']}, + {code: 'f(...a,\n)', options: ['always-multiline']}, + {code: 'f(...a,\n)', options: ['only-multiline']}, + {code: 'f(...a\n)', options: ['never']}, + {code: 'f(...a\n)', options: ['only-multiline']}, + + // NewExpression + {code: 'new F(a)', options: ['always-multiline']}, + {code: 'new F(a)', options: ['never']}, + {code: 'new F(a)', options: ['only-multiline']}, + {code: 'new F(a,)', options: ['always']}, + {code: 'new F(a,\n)', options: ['always']}, + {code: 'new F(a,\n)', options: ['always-multiline']}, + {code: 'new F(a,\n)', options: ['only-multiline']}, + {code: 'new F(a\n)', options: ['never']}, + {code: 'new F(a\n)', options: ['only-multiline']}, + {code: 'new F(...a)', options: ['always-multiline']}, + {code: 'new F(...a)', options: ['never']}, + {code: 'new F(...a)', options: ['only-multiline']}, + {code: 'new F(...a,)', options: ['always']}, + {code: 'new F(...a,\n)', options: ['always']}, + {code: 'new F(...a,\n)', options: ['always-multiline']}, + {code: 'new F(...a,\n)', options: ['only-multiline']}, + {code: 'new F(...a\n)', options: ['never']}, + {code: 'new F(...a\n)', options: ['only-multiline']}, + ], + invalid: [ + // FunctionDeclaration + {code: 'function f(a) {}', output: 'function f(a,) {}', options: ['always'], errors: MISSING_I}, + {code: 'function f(a,) {}', output: 'function f(a) {}', options: ['always-multiline'], errors: UNEXPECTED_I}, + {code: 'function f(a,) {}', output: 'function f(a) {}', options: ['only-multiline'], errors: UNEXPECTED_I}, + {code: 'function f(a,) {}', output: 'function f(a) {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'function f(a,\n) {}', output: 'function f(a\n) {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'function f(a\n) {}', output: 'function f(a,\n) {}', options: ['always'], errors: MISSING_I}, + {code: 'function f(a\n) {}', output: 'function f(a,\n) {}', options: ['always-multiline'], errors: MISSING_I}, + + {code: 'function f(a=1) {}', output: 'function f(a=1,) {}', options: ['always'], errors: MISSING_AP}, + {code: 'function f(a=1,) {}', output: 'function f(a=1) {}', options: ['always-multiline'], errors: UNEXPECTED_AP}, + {code: 'function f(a=1,) {}', output: 'function f(a=1) {}', options: ['always-multiline'], errors: UNEXPECTED_AP}, + {code: 'function f(a=1,) {}', output: 'function f(a=1) {}', options: ['only-multiline'], errors: UNEXPECTED_AP}, + {code: 'function f(a=1,) {}', output: 'function f(a=1) {}', options: ['never'], errors: UNEXPECTED_AP}, + {code: 'function f(a=1,\n) {}', output: 'function f(a=1\n) {}', options: ['never'], errors: UNEXPECTED_AP}, + {code: 'function f(a=1\n) {}', output: 'function f(a=1,\n) {}', options: ['always'], errors: MISSING_AP}, + {code: 'function f(a=1\n) {}', output: 'function f(a=1,\n) {}', options: ['always-multiline'], errors: MISSING_AP}, + + {code: 'function f(a:T) {}', output: 'function f(a:T,) {}', options: ['always'], errors: MISSING_I}, + {code: 'function f(a:T,) {}', output: 'function f(a:T) {}', options: ['always-multiline'], errors: UNEXPECTED_I}, + {code: 'function f(a:T,) {}', output: 'function f(a:T) {}', options: ['only-multiline'], errors: UNEXPECTED_I}, + {code: 'function f(a:T,) {}', output: 'function f(a:T) {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'function f(a:T,\n) {}', output: 'function f(a:T\n) {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'function f(a:T\n) {}', output: 'function f(a:T,\n) {}', options: ['always'], errors: MISSING_I}, + {code: 'function f(a:T\n) {}', output: 'function f(a:T,\n) {}', options: ['always-multiline'], errors: MISSING_I}, + + // FunctionExpression + {code: 'f = function f(a) {}', output: 'f = function f(a,) {}', options: ['always'], errors: MISSING_I}, + {code: 'f = function f(a,) {}', output: 'f = function f(a) {}', options: ['always-multiline'], errors: UNEXPECTED_I}, + {code: 'f = function f(a,) {}', output: 'f = function f(a) {}', options: ['only-multiline'], errors: UNEXPECTED_I}, + {code: 'f = function f(a,) {}', output: 'f = function f(a) {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'f = function f(a,\n) {}', output: 'f = function f(a\n) {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'f = function f(a\n) {}', output: 'f = function f(a,\n) {}', options: ['always'], errors: MISSING_I}, + {code: 'f = function f(a\n) {}', output: 'f = function f(a,\n) {}', options: ['always-multiline'], errors: MISSING_I}, + + {code: 'f = function f(a=1) {}', output: 'f = function f(a=1,) {}', options: ['always'], errors: MISSING_AP}, + {code: 'f = function f(a=1,) {}', output: 'f = function f(a=1) {}', options: ['always-multiline'], errors: UNEXPECTED_AP}, + {code: 'f = function f(a=1,) {}', output: 'f = function f(a=1) {}', options: ['only-multiline'], errors: UNEXPECTED_AP}, + {code: 'f = function f(a=1,) {}', output: 'f = function f(a=1) {}', options: ['never'], errors: UNEXPECTED_AP}, + {code: 'f = function f(a=1,\n) {}', output: 'f = function f(a=1\n) {}', options: ['never'], errors: UNEXPECTED_AP}, + {code: 'f = function f(a=1\n) {}', output: 'f = function f(a=1,\n) {}', options: ['always'], errors: MISSING_AP}, + {code: 'f = function f(a=1\n) {}', output: 'f = function f(a=1,\n) {}', options: ['always-multiline'], errors: MISSING_AP}, + + {code: 'f = function f(a:T) {}', output: 'f = function f(a:T,) {}', options: ['always'], errors: MISSING_I}, + {code: 'f = function f(a:T,) {}', output: 'f = function f(a:T) {}', options: ['always-multiline'], errors: UNEXPECTED_I}, + {code: 'f = function f(a:T,) {}', output: 'f = function f(a:T) {}', options: ['only-multiline'], errors: UNEXPECTED_I}, + {code: 'f = function f(a:T,) {}', output: 'f = function f(a:T) {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'f = function f(a:T,\n) {}', output: 'f = function f(a:T\n) {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'f = function f(a:T\n) {}', output: 'f = function f(a:T,\n) {}', options: ['always'], errors: MISSING_I}, + {code: 'f = function f(a:T\n) {}', output: 'f = function f(a:T,\n) {}', options: ['always-multiline'], errors: MISSING_I}, + + // ArrowFunctionExpression + {code: 'f = (a) => {}', output: 'f = (a,) => {}', options: ['always'], errors: MISSING_I}, + {code: 'f = (a,) => {}', output: 'f = (a) => {}', options: ['always-multiline'], errors: UNEXPECTED_I}, + {code: 'f = (a,) => {}', output: 'f = (a) => {}', options: ['only-multiline'], errors: UNEXPECTED_I}, + {code: 'f = (a,) => {}', output: 'f = (a) => {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'f = (a,\n) => {}', output: 'f = (a\n) => {}', options: ['never'], errors: UNEXPECTED_I}, + {code: 'f = (a\n) => {}', output: 'f = (a,\n) => {}', options: ['always'], errors: MISSING_I}, + {code: 'f = (a\n) => {}', output: 'f = (a,\n) => {}', options: ['always-multiline'], errors: MISSING_I}, + + {code: 'f = (a=1) => {}', output: 'f = (a=1,) => {}', options: ['always'], errors: MISSING_AP}, + {code: 'f = (a=1,) => {}', output: 'f = (a=1) => {}', options: ['always-multiline'], errors: UNEXPECTED_AP}, + {code: 'f = (a=1,) => {}', output: 'f = (a=1) => {}', options: ['only-multiline'], errors: UNEXPECTED_AP}, + {code: 'f = (a=1,) => {}', output: 'f = (a=1) => {}', options: ['never'], errors: UNEXPECTED_AP}, + {code: 'f = (a=1,\n) => {}', output: 'f = (a=1\n) => {}', options: ['never'], errors: UNEXPECTED_AP}, + {code: 'f = (a=1\n) => {}', output: 'f = (a=1,\n) => {}', options: ['always'], errors: MISSING_AP}, + {code: 'f = (a=1\n) => {}', output: 'f = (a=1,\n) => {}', options: ['always-multiline'], errors: MISSING_AP}, + + // Arrow functions with flow types aren't getting the correct loc. + // {code: 'f = (a:T) => {}', output: 'f = (a:T,) => {}', options: ['always'], errors: MISSING_I}, + // {code: 'f = (a:T,) => {}', output: 'f = (a:T) => {}', options: ['always-multiline'], errors: UNEXPECTED_I}, + // {code: 'f = (a:T,) => {}', output: 'f = (a:T) => {}', options: ['only-multiline'], errors: UNEXPECTED_I}, + // {code: 'f = (a:T,) => {}', output: 'f = (a:T) => {}', options: ['never'], errors: UNEXPECTED_I}, + // {code: 'f = (a:T,\n) => {}', output: 'f = (a:T\n) => {}', options: ['never'], errors: UNEXPECTED_I}, + // {code: 'f = (a:T\n) => {}', output: 'f = (a:T,\n) => {}', options: ['always'], errors: MISSING_I}, + // {code: 'f = (a:T\n) => {}', output: 'f = (a:T,\n) => {}', options: ['always-multiline'], errors: MISSING_I}, + + // CallExpression + {code: 'f(a)', output: 'f(a,)', options: ['always'], errors: MISSING_I}, + {code: 'f(a,)', output: 'f(a)', options: ['always-multiline'], errors: UNEXPECTED_I}, + {code: 'f(a,)', output: 'f(a)', options: ['only-multiline'], errors: UNEXPECTED_I}, + {code: 'f(a,)', output: 'f(a)', options: ['never'], errors: UNEXPECTED_I}, + {code: 'f(a,\n)', output: 'f(a\n)', options: ['never'], errors: UNEXPECTED_I}, + {code: 'f(a\n)', output: 'f(a,\n)', options: ['always'], errors: MISSING_I}, + {code: 'f(a\n)', output: 'f(a,\n)', options: ['always-multiline'], errors: MISSING_I}, + + {code: 'f(...a)', output: 'f(...a,)', options: ['always'], errors: MISSING_SE}, + {code: 'f(...a,)', output: 'f(...a)', options: ['always-multiline'], errors: UNEXPECTED_SE}, + {code: 'f(...a,)', output: 'f(...a)', options: ['only-multiline'], errors: UNEXPECTED_SE}, + {code: 'f(...a,)', output: 'f(...a)', options: ['never'], errors: UNEXPECTED_SE}, + {code: 'f(...a,\n)', output: 'f(...a\n)', options: ['never'], errors: UNEXPECTED_SE}, + {code: 'f(...a\n)', output: 'f(...a,\n)', options: ['always'], errors: MISSING_SE}, + {code: 'f(...a\n)', output: 'f(...a,\n)', options: ['always-multiline'], errors: MISSING_SE}, + + // NewExpression + {code: 'new F(a)', output: 'new F(a,)', options: ['always'], errors: MISSING_I}, + {code: 'new F(a,)', output: 'new F(a)', options: ['always-multiline'], errors: UNEXPECTED_I}, + {code: 'new F(a,)', output: 'new F(a)', options: ['only-multiline'], errors: UNEXPECTED_I}, + {code: 'new F(a,)', output: 'new F(a)', options: ['never'], errors: UNEXPECTED_I}, + {code: 'new F(a,\n)', output: 'new F(a\n)', options: ['never'], errors: UNEXPECTED_I}, + {code: 'new F(a\n)', output: 'new F(a,\n)', options: ['always'], errors: MISSING_I}, + {code: 'new F(a\n)', output: 'new F(a,\n)', options: ['always-multiline'], errors: MISSING_I}, + + {code: 'new F(...a)', output: 'new F(...a,)', options: ['always'], errors: MISSING_SE}, + {code: 'new F(...a,)', output: 'new F(...a)', options: ['always-multiline'], errors: UNEXPECTED_SE}, + {code: 'new F(...a,)', output: 'new F(...a)', options: ['only-multiline'], errors: UNEXPECTED_SE}, + {code: 'new F(...a,)', output: 'new F(...a)', options: ['never'], errors: UNEXPECTED_SE}, + {code: 'new F(...a,\n)', output: 'new F(...a\n)', options: ['never'], errors: UNEXPECTED_SE}, + {code: 'new F(...a\n)', output: 'new F(...a,\n)', options: ['always'], errors: MISSING_SE}, + {code: 'new F(...a\n)', output: 'new F(...a,\n)', options: ['always-multiline'], errors: MISSING_SE}, + ], +});