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 cf9e1d58345c..0fd0207dbf47 100644 --- a/src/language-js/needs-parens.js +++ b/src/language-js/needs-parens.js @@ -127,6 +127,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; @@ -200,21 +220,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 b771d6f39e28..1e59f82362c7 100644 --- a/tests/format/flow-repo/arith/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/flow-repo/arith/__snapshots__/jsfmt.spec.js.snap @@ -156,7 +156,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 @@ -322,10 +322,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 b5e32ea11a1b..ac49857961df 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 @@ -143,7 +143,7 @@ function foo() { this.m(); } 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 cc7c660dd953..3f779a172790 100644 --- a/tests/format/js/classes/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/classes/__snapshots__/jsfmt.spec.js.snap @@ -101,10 +101,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; ================================================================================ `; @@ -118,7 +118,7 @@ printWidth: 80 (class {})(class {}); =====================================output===================================== -(class {}(class {})); +(class {})(class {}); ================================================================================ `; @@ -209,8 +209,8 @@ printWidth: 80 (class {}).a; =====================================output===================================== -(class {}[1]); -(class {}.a); +(class {})[1]; +(class {}).a; ================================================================================ `; @@ -336,7 +336,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 ec9c9b8ededf..85dfa50ae28a 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 @@ -375,14 +375,14 @@ semi: false (@deco class {}).name; =====================================output===================================== -;(( +;( @deco class Foo {} -).name) -;(( +).name +;( @deco class {} -).name) +).name ================================================================================ `; @@ -397,14 +397,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 bea0659aa7ef..7efa5a964522 100644 --- a/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap @@ -250,7 +250,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 6c3bbf8272f7..7eefc33f02cd 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 5bdccf844893..ffe2433746e9 100644 --- a/tests/format/js/method-chain/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/method-chain/__snapshots__/jsfmt.spec.js.snap @@ -1455,8 +1455,8 @@ method().then(x => x) =====================================output===================================== method().then((x) => x)["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 7e0011674862..3d1345e20120 100644 --- a/tests/format/js/objects/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/objects/__snapshots__/jsfmt.spec.js.snap @@ -125,12 +125,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 be2cc728c600..ff80058803d7 100644 --- a/tests/format/js/template/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/template/__snapshots__/jsfmt.spec.js.snap @@ -388,7 +388,7 @@ async function f() { (await b)\`\`; } b()\`\`; // "ClassExpression" -(class {}\`\`); +(class {})\`\`; // "ConditionalExpression" (b ? c : d)\`\`; @@ -406,7 +406,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 d7a336fdb2f0..cc8dd0508fe8 100644 --- a/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`array-pattern.ts format 1`] = ` +exports["array-pattern.ts format 1"] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 @@ -14,7 +14,7 @@ printWidth: 80 ================================================================================ `; -exports[`as.ts format 1`] = ` +exports["as.ts format 1"] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 @@ -81,10 +81,10 @@ 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, nonce } as State); @@ -121,7 +121,7 @@ const iter2 = createIterator( ================================================================================ `; -exports[`assignment.ts format 1`] = ` +exports["assignment.ts format 1"] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 @@ -191,7 +191,7 @@ this.intervalID = setInterval(() => { self.step(); }, 30) as unknown as number; ================================================================================ `; -exports[`assignment2.ts format 1`] = ` +exports["assignment2.ts format 1"] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 @@ -265,7 +265,7 @@ const originalPrototype = originalConstructor.prototype as TComponent & ================================================================================ `; -exports[`export_default_as.ts format 1`] = ` +exports["export_default_as.ts format 1"] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 @@ -279,7 +279,7 @@ export default (function log() {} as typeof console.log); ================================================================================ `; -exports[`long-identifiers.ts format 1`] = ` +exports["long-identifiers.ts format 1"] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 @@ -320,7 +320,7 @@ averredBathersBoxroomBuggyNurl( ================================================================================ `; -exports[`nested-await-and-as.ts format 1`] = ` +exports["nested-await-and-as.ts format 1"] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 @@ -347,7 +347,7 @@ const getAccountCount = async () => ================================================================================ `; -exports[`return.ts format 1`] = ` +exports["return.ts format 1"] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 @@ -366,7 +366,7 @@ function foo() { return { foo: 1, bar: 2 } as Foo; } ================================================================================ `; -exports[`ternary.ts format 1`] = ` +exports["ternary.ts format 1"] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 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 8d2be798074c..50537948ccd3 100644 --- a/tests/format/typescript/non-null/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/non-null/__snapshots__/jsfmt.spec.js.snap @@ -29,9 +29,9 @@ const myFunction3 = (key) => ({}!.a); 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 021fa989ab89..a09e92e078f8 100644 --- a/tests/format/typescript/satisfies-operators/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/satisfies-operators/__snapshots__/jsfmt.spec.js.snap @@ -272,7 +272,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() {}, @@ -344,7 +344,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() {}, @@ -724,8 +724,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) @@ -778,8 +778,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);