diff --git a/changelog_unreleased/javascript/14077.md b/changelog_unreleased/javascript/14077.md new file mode 100644 index 000000000000..eb48a23b8dcd --- /dev/null +++ b/changelog_unreleased/javascript/14077.md @@ -0,0 +1,19 @@ +#### Add parentheses to head of `ExpressionStatement` instead of the whole statement (#14077 by @fisker) + + +```jsx +// Input +({}).toString.call(foo) === "[object Array]" + ? foo.forEach(iterateArray) + : iterateObject(foo); + +// Prettier stable +({}.toString.call(foo) === "[object Array]" + ? foo.forEach(iterateArray) + : iterateObject(foo)); + +// Prettier main +({}).toString.call(foo.forEach) === "[object Array]" + ? foo.forEach(iterateArray) + : iterateObject(foo); +``` diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js index 02b8bf7ac1a6..a915287dab68 100644 --- a/src/language-js/needs-parens.js +++ b/src/language-js/needs-parens.js @@ -128,6 +128,26 @@ function needsParens(path, options) { return false; } + if ( + node.type === "ObjectExpression" || + node.type === "FunctionExpression" || + node.type === "ClassExpression" || + node.type === "DoExpression" + ) { + const expression = path.findAncestor( + (node) => node.type === "ExpressionStatement" + )?.expression; + if ( + expression && + startsWithNoLookaheadToken( + expression, + (leftmostNode) => leftmostNode === node + ) + ) { + return true; + } + } + switch (parent.type) { case "ParenthesizedExpression": return false; @@ -201,21 +221,6 @@ function needsParens(path, options) { } break; } - case "ExpressionStatement": { - if ( - startsWithNoLookaheadToken( - node, - (node) => - node.type === "ObjectExpression" || - node.type === "FunctionExpression" || - node.type === "ClassExpression" || - node.type === "DoExpression" - ) - ) { - return true; - } - break; - } case "ArrowFunctionExpression": { if ( name === "body" && diff --git a/tests/format/flow-repo/arith/__snapshots__/jsfmt.spec.js.snap b/tests/format/flow-repo/arith/__snapshots__/jsfmt.spec.js.snap index eaf8052c2b70..c6aaea5aa68f 100644 --- a/tests/format/flow-repo/arith/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/flow-repo/arith/__snapshots__/jsfmt.spec.js.snap @@ -168,7 +168,7 @@ let tests = [ x + ""; // error "" + x; // error x + {}; // error - ({} + x); // error + ({}) + x; // error }, // when one side is a string or number and the other is invalid, we @@ -344,10 +344,10 @@ let tests = [ "foo" < 1; // error "foo" < "bar"; 1 < { foo: 1 }; // error -({ foo: 1 } < 1); // error -({ foo: 1 } < { foo: 1 }); // error +({ foo: 1 }) < 1; // error +({ foo: 1 }) < { foo: 1 }; // error "foo" < { foo: 1 }; // error -({ foo: 1 } < "foo"); // error +({ foo: 1 }) < "foo"; // error var x = (null: ?number); 1 < x; // 2 errors: null !~> number; undefined !~> number diff --git a/tests/format/flow-repo/binary/__snapshots__/jsfmt.spec.js.snap b/tests/format/flow-repo/binary/__snapshots__/jsfmt.spec.js.snap index c971794e144c..13ec952db4ea 100644 --- a/tests/format/flow-repo/binary/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/flow-repo/binary/__snapshots__/jsfmt.spec.js.snap @@ -98,7 +98,7 @@ let tests = [ function () { null in {}; // error void 0 in {}; // error - ({} in {}); // error + ({}) in {}; // error [] in {}; // error false in []; // error }, diff --git a/tests/format/flow-repo/object-method/__snapshots__/jsfmt.spec.js.snap b/tests/format/flow-repo/object-method/__snapshots__/jsfmt.spec.js.snap index b0743465ac67..299f77c497b8 100644 --- a/tests/format/flow-repo/object-method/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/flow-repo/object-method/__snapshots__/jsfmt.spec.js.snap @@ -160,7 +160,7 @@ function foo() { function bar(f: () => void) { f(); // passing global object as \`this\` - ({ f }.f()); // passing container object as \`this\` + ({ f }).f(); // passing container object as \`this\` } bar(foo); // error, since \`this\` is used non-trivially in \`foo\` diff --git a/tests/format/js/classes/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/classes/__snapshots__/jsfmt.spec.js.snap index 0587ffe17f9e..ec8ab049c7b2 100644 --- a/tests/format/js/classes/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/classes/__snapshots__/jsfmt.spec.js.snap @@ -111,10 +111,10 @@ printWidth: 80 (class a extends b {}) + 1; =====================================output===================================== -(class {} + 1); -(class a {} + 1); -(class extends b {} + 1); -(class a extends b {} + 1); +(class {}) + 1; +(class a {}) + 1; +(class extends b {}) + 1; +(class a extends b {}) + 1; ================================================================================ `; @@ -128,7 +128,7 @@ printWidth: 80 (class {})(class {}); =====================================output===================================== -(class {}(class {})); +(class {})(class {}); ================================================================================ `; @@ -225,8 +225,8 @@ printWidth: 80 (class {}).a; =====================================output===================================== -(class {}[1]); -(class {}.a); +(class {})[1]; +(class {}).a; ================================================================================ `; @@ -356,7 +356,7 @@ printWidth: 80 if (1) (class {}) ? 1 : 2; =====================================output===================================== -if (1) (class {} ? 1 : 2); +if (1) (class {}) ? 1 : 2; ================================================================================ `; diff --git a/tests/format/js/decorators/class-expression/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/decorators/class-expression/__snapshots__/jsfmt.spec.js.snap index b92b25fadf48..e12bdedd8818 100644 --- a/tests/format/js/decorators/class-expression/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/decorators/class-expression/__snapshots__/jsfmt.spec.js.snap @@ -387,14 +387,14 @@ semi: false (@deco class {}).name; =====================================output===================================== -;(( +;( @deco class Foo {} -).name) -;(( +).name +;( @deco class {} -).name) +).name ================================================================================ `; @@ -409,14 +409,14 @@ printWidth: 80 (@deco class {}).name; =====================================output===================================== -(( +( @deco class Foo {} -).name); -(( +).name; +( @deco class {} -).name); +).name; ================================================================================ `; diff --git a/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap index e975afc7e768..c45caccc8275 100644 --- a/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap @@ -252,7 +252,7 @@ function foo() { } (do {}); -(do {} + 1); +(do {}) + 1; 1 + do {}; () => do {}; diff --git a/tests/format/js/function/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/function/__snapshots__/jsfmt.spec.js.snap index eceb788eb601..880984ffee97 100644 --- a/tests/format/js/function/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/function/__snapshots__/jsfmt.spec.js.snap @@ -19,7 +19,7 @@ a + function() {}; new function() {}; =====================================output===================================== -(function () {}.length); +(function () {}).length; typeof function () {}; export default (function () {})(); (function () {})()\`\`; @@ -27,7 +27,7 @@ export default (function () {})(); new (function () {})(); (function () {}); a = function f() {} || b; -(function () {} && a); +(function () {}) && a; a + function () {}; new (function () {})(); diff --git a/tests/format/js/method-chain/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/method-chain/__snapshots__/jsfmt.spec.js.snap index d62714180449..0fb85afc2c6c 100644 --- a/tests/format/js/method-chain/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/method-chain/__snapshots__/jsfmt.spec.js.snap @@ -1522,8 +1522,8 @@ method() ["abc"]((x) => x) [abc]((x) => x); -({}.a().b()); -({}.a().b()); +({}).a().b(); +({}).a().b(); ================================================================================ `; diff --git a/tests/format/js/no-semi/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/no-semi/__snapshots__/jsfmt.spec.js.snap index 806961a62f05..0029a99c7b8e 100644 --- a/tests/format/js/no-semi/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/no-semi/__snapshots__/jsfmt.spec.js.snap @@ -727,9 +727,9 @@ x x ;(() => {})() x -;({ a: 1 }.entries()) +;({ a: 1 }).entries() x -;({ a: 1 }.entries()) +;({ a: 1 }).entries() x ; x @@ -910,9 +910,9 @@ x; x; (() => {})(); x; -({ a: 1 }.entries()); +({ a: 1 }).entries(); x; -({ a: 1 }.entries()); +({ a: 1 }).entries(); x; ; x; diff --git a/tests/format/js/objects/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/objects/__snapshots__/jsfmt.spec.js.snap index 0ebca7556d93..0964f4242127 100644 --- a/tests/format/js/objects/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/objects/__snapshots__/jsfmt.spec.js.snap @@ -129,12 +129,12 @@ const a3 = { =====================================output===================================== () => ({}\`\`); -({}\`\`); +({})\`\`; a = () => ({}.x); -({} && a, b); -({}::b, 0); -({}::b()\`\`[""].c++ && 0 ? 0 : 0, 0); -({}(), 0); +({}) && a, b; +({})::b, 0; +({})::b()\`\`[""].c++ && 0 ? 0 : 0, 0; +({})(), 0; ({} = 0); ({} = 0), 1; diff --git a/tests/format/js/optional-chaining/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/optional-chaining/__snapshots__/jsfmt.spec.js.snap index 04f80f3b84fe..c1c21992f5f0 100644 --- a/tests/format/js/optional-chaining/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/optional-chaining/__snapshots__/jsfmt.spec.js.snap @@ -172,8 +172,8 @@ a = () => ({}?.b() && a); (x) => ({}?.().b); (x) => ({}?.b()); (x) => ({}?.b.b); -({}?.a().b()); -({ a: 1 }?.entries()); +({})?.a().b(); +({ a: 1 })?.entries(); new (foo?.bar)(); new (foo?.bar())(); diff --git a/tests/format/js/template/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/template/__snapshots__/jsfmt.spec.js.snap index 00b56b604d4c..4837c1522f82 100644 --- a/tests/format/js/template/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/template/__snapshots__/jsfmt.spec.js.snap @@ -391,7 +391,7 @@ async function f() { b()\`\`; // "ClassExpression" -(class {}\`\`); +(class {})\`\`; // "ConditionalExpression" (b ? c : d)\`\`; @@ -409,7 +409,7 @@ b.c\`\`; new B()\`\`; // "ObjectExpression" -({}\`\`); +({})\`\`; // "SequenceExpression" (b, c)\`\`; diff --git a/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap index 27a91f18a2e4..116898b60be7 100644 --- a/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap @@ -81,14 +81,14 @@ export default class Column extends (RcTable.Column as React.ComponentClass< >) {} export const MobxTypedForm = class extends (Form as { new (): any }) {}; export abstract class MobxTypedForm1 extends (Form as { new (): any }) {} -({} as {}); +({}) as {}; function* g() { const test = (yield "foo") as number; } async function g1() { const test = (await "foo") as number; } -({} as X); +({}) as X; () => ({} as X); const state = JSON.stringify({ next: window.location.href, diff --git a/tests/format/typescript/non-null/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/non-null/__snapshots__/jsfmt.spec.js.snap index 0a5b720109e7..69c079c618be 100644 --- a/tests/format/typescript/non-null/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/non-null/__snapshots__/jsfmt.spec.js.snap @@ -35,9 +35,9 @@ const f = ((a) => { log(a); })!; -if (a) ({ a, ...b }.a()!.c()); +if (a) ({ a, ...b }).a()!.c(); -(function () {}!()); +(function () {})!(); class a extends ({}!) {} diff --git a/tests/format/typescript/satisfies-operators/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/satisfies-operators/__snapshots__/jsfmt.spec.js.snap index e86017c94b54..18ab6ef363fe 100644 --- a/tests/format/typescript/satisfies-operators/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/satisfies-operators/__snapshots__/jsfmt.spec.js.snap @@ -282,7 +282,7 @@ let obj: { f(s: string): void } & Record = { g(s) {}, } satisfies { g(s: string): void } & Record -;({ f(x) {} } satisfies { f(s: string): void }) +;({ f(x) {} }) satisfies { f(s: string): void } const car = { start() {}, @@ -354,7 +354,7 @@ let obj: { f(s: string): void } & Record = { g(s) {}, } satisfies { g(s: string): void } & Record; -({ f(x) {} } satisfies { f(s: string): void }); +({ f(x) {} }) satisfies { f(s: string): void }; const car = { start() {}, @@ -742,8 +742,8 @@ foo satisfies unknown as Bar; foo as unknown satisfies Bar; =====================================output===================================== -;({} satisfies {}) -;({} satisfies X) +;({}) satisfies {} +;({}) satisfies X ;() => ({} satisfies X) this.isTabActionBar((e.target || e.srcElement) satisfies HTMLElement) @@ -798,8 +798,8 @@ foo satisfies unknown as Bar; foo as unknown satisfies Bar; =====================================output===================================== -({} satisfies {}); -({} satisfies X); +({}) satisfies {}; +({}) satisfies X; () => ({} satisfies X); this.isTabActionBar((e.target || e.srcElement) satisfies HTMLElement);