From 44aef0b1ba067cd286ae5b79a9f1f17c751e4cdf Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 21 Mar 2023 16:13:11 +0800 Subject: [PATCH 1/5] Allow decorator on private members and class expressions (#14548) --- changelog_unreleased/typescript/14548.md | 24 +++++++ .../parse/postprocess/typescript.js | 30 ++++---- .../__snapshots__/jsfmt.spec.js.snap | 68 ------------------- .../decorators/class-expression/jsfmt.spec.js | 1 - .../__snapshots__/jsfmt.spec.js.snap | 42 ++++++++++++ tests/format/typescript/decorators/legacy.ts | 12 ++++ .../__snapshots__/jsfmt.spec.js.snap | 9 +-- .../jsfmt.spec.js | 2 +- 8 files changed, 97 insertions(+), 91 deletions(-) create mode 100644 changelog_unreleased/typescript/14548.md create mode 100644 tests/format/typescript/decorators/legacy.ts diff --git a/changelog_unreleased/typescript/14548.md b/changelog_unreleased/typescript/14548.md new file mode 100644 index 000000000..82262a846 --- /dev/null +++ b/changelog_unreleased/typescript/14548.md @@ -0,0 +1,24 @@ +#### Allow decorators on private members and class expressions (#14548 by @fisker) + + +```ts +// Input +class A { + @decorator() + #privateMethod () {} +} + +// Prettier stable +SyntaxError: Decorators are not valid here. (2:3) + 1 | class A { +> 2 | @decorator() + | ^^^^^^^^^^^^ + 3 | #privateMethod () {} + 4 | } + +// Prettier main +class A { + @decorator() + #privateMethod() {} +} +``` diff --git a/src/language-js/parse/postprocess/typescript.js b/src/language-js/parse/postprocess/typescript.js index 9b343a766..926f60432 100644 --- a/src/language-js/parse/postprocess/typescript.js +++ b/src/language-js/parse/postprocess/typescript.js @@ -30,6 +30,20 @@ function throwErrorOnTsNode(node, message) { throwSyntaxError({ loc: { start, end } }, message); } +function nodeCanBeDecorated(node) { + const ts = require("typescript"); + + return [true, false].some((useLegacyDecorators) => + // @ts-expect-error -- internal? + ts.nodeCanBeDecorated( + useLegacyDecorators, + node, + node.parent, + node.parent.parent + ) + ); +} + // Invalid decorators are removed since `@typescript-eslint/typescript-estree` v4 // https://github.com/typescript-eslint/typescript-eslint/pull/2375 // There is a `checkGrammarDecorators` in `typescript` package, consider use it directly in future @@ -44,16 +58,7 @@ function throwErrorForInvalidDecorator(node) { const { SyntaxKind } = ts; for (const modifier of modifiers) { if (ts.isDecorator(modifier)) { - const legacyDecorators = true; - if ( - // @ts-expect-error -- internal? - !ts.nodeCanBeDecorated( - legacyDecorators, - node, - node.parent, - node.parent.parent - ) - ) { + if (!nodeCanBeDecorated(node)) { if ( node.kind === SyntaxKind.MethodDeclaration && // @ts-expect-error -- internal? @@ -67,9 +72,8 @@ function throwErrorForInvalidDecorator(node) { throwErrorOnTsNode(modifier, "Decorators are not valid here."); } } else if ( - legacyDecorators && - (node.kind === SyntaxKind.GetAccessor || - node.kind === SyntaxKind.SetAccessor) + node.kind === SyntaxKind.GetAccessor || + node.kind === SyntaxKind.SetAccessor ) { // @ts-expect-error -- internal? const accessors = ts.getAllAccessorDeclarations( 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 123d72efa..38356821e 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 @@ -24,14 +24,6 @@ exports[`arguments.js [flow] format 1`] = ` 3 |" `; -exports[`arguments.js [typescript] format 1`] = ` -"Decorators are not valid here. (1:13) -> 1 | console.log(@deco class Foo {}) - | ^^^^^ - 2 | console.log(@deco class {}) - 3 |" -`; - exports[`arguments.js - {"semi":false} [acorn] format 1`] = ` "Unexpected character '@' (1:13) > 1 | console.log(@deco class Foo {}) @@ -56,14 +48,6 @@ exports[`arguments.js - {"semi":false} [flow] format 1`] = ` 3 |" `; -exports[`arguments.js - {"semi":false} [typescript] format 1`] = ` -"Decorators are not valid here. (1:13) -> 1 | console.log(@deco class Foo {}) - | ^^^^^ - 2 | console.log(@deco class {}) - 3 |" -`; - exports[`arguments.js - {"semi":false} format 1`] = ` ====================================options===================================== parsers: ["babel", "flow", "typescript"] @@ -144,15 +128,6 @@ exports[`class-expression.js [flow] format 1`] = ` 4 | (@deco class Foo {});" `; -exports[`class-expression.js [typescript] format 1`] = ` -"Decorators are not valid here. (1:13) -> 1 | const a1 = (@deco class Foo {}); - | ^^^^^ - 2 | const a2 = (@deco class {}); - 3 | - 4 | (@deco class Foo {});" -`; - exports[`class-expression.js - {"semi":false} [acorn] format 1`] = ` "Unexpected character '@' (1:13) > 1 | const a1 = (@deco class Foo {}); @@ -180,15 +155,6 @@ exports[`class-expression.js - {"semi":false} [flow] format 1`] = ` 4 | (@deco class Foo {});" `; -exports[`class-expression.js - {"semi":false} [typescript] format 1`] = ` -"Decorators are not valid here. (1:13) -> 1 | const a1 = (@deco class Foo {}); - | ^^^^^ - 2 | const a2 = (@deco class {}); - 3 | - 4 | (@deco class Foo {});" -`; - exports[`class-expression.js - {"semi":false} format 1`] = ` ====================================options===================================== parsers: ["babel", "flow", "typescript"] @@ -336,14 +302,6 @@ exports[`member-expression.js [flow] format 1`] = ` 3 |" `; -exports[`member-expression.js [typescript] format 1`] = ` -"Decorators are not valid here. (1:2) -> 1 | (@deco class Foo {}).name; - | ^^^^^ - 2 | (@deco class {}).name; - 3 |" -`; - exports[`member-expression.js - {"semi":false} [acorn] format 1`] = ` "Unexpected character '@' (1:2) > 1 | (@deco class Foo {}).name; @@ -368,14 +326,6 @@ exports[`member-expression.js - {"semi":false} [flow] format 1`] = ` 3 |" `; -exports[`member-expression.js - {"semi":false} [typescript] format 1`] = ` -"Decorators are not valid here. (1:2) -> 1 | (@deco class Foo {}).name; - | ^^^^^ - 2 | (@deco class {}).name; - 3 |" -`; - exports[`member-expression.js - {"semi":false} format 1`] = ` ====================================options===================================== parsers: ["babel", "flow", "typescript"] @@ -448,15 +398,6 @@ exports[`super-class.js [flow] format 1`] = ` 4 |" `; -exports[`super-class.js [typescript] format 1`] = ` -"Decorators are not valid here. (1:20) -> 1 | class Foo extends (@deco class Foo {}){} - | ^^^^^ - 2 | - 3 | class Foo extends (@deco class {}){} - 4 |" -`; - exports[`super-class.js - {"semi":false} [acorn] format 1`] = ` "Unexpected character '@' (1:20) > 1 | class Foo extends (@deco class Foo {}){} @@ -484,15 +425,6 @@ exports[`super-class.js - {"semi":false} [flow] format 1`] = ` 4 |" `; -exports[`super-class.js - {"semi":false} [typescript] format 1`] = ` -"Decorators are not valid here. (1:20) -> 1 | class Foo extends (@deco class Foo {}){} - | ^^^^^ - 2 | - 3 | class Foo extends (@deco class {}){} - 4 |" -`; - exports[`super-class.js - {"semi":false} format 1`] = ` ====================================options===================================== parsers: ["babel", "flow", "typescript"] diff --git a/tests/format/js/decorators/class-expression/jsfmt.spec.js b/tests/format/js/decorators/class-expression/jsfmt.spec.js index b7c768416..028ed3fa8 100644 --- a/tests/format/js/decorators/class-expression/jsfmt.spec.js +++ b/tests/format/js/decorators/class-expression/jsfmt.spec.js @@ -1,6 +1,5 @@ const errors = { flow: true, - typescript: true, acorn: true, espree: true, }; diff --git a/tests/format/typescript/decorators/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/decorators/__snapshots__/jsfmt.spec.js.snap index 97157ebd0..501c86771 100644 --- a/tests/format/typescript/decorators/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/decorators/__snapshots__/jsfmt.spec.js.snap @@ -497,6 +497,48 @@ class MyContainerComponent { ================================================================================ `; +exports[`legacy.ts format 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +[ + @decorator() class {}, + @decorator() class A {}, +]; + +class A { + @decorator() accessor #field; +} + +class B { + @decorator() #field () {} +} + +=====================================output===================================== +[ + ( + @decorator() + class {} + ), + ( + @decorator() + class A {} + ), +]; + +class A { + @decorator() accessor #field; +} + +class B { + @decorator() #field() {} +} + +================================================================================ +`; + exports[`mobx.ts format 1`] = ` ====================================options===================================== parsers: ["typescript"] diff --git a/tests/format/typescript/decorators/legacy.ts b/tests/format/typescript/decorators/legacy.ts new file mode 100644 index 000000000..450e40cbc --- /dev/null +++ b/tests/format/typescript/decorators/legacy.ts @@ -0,0 +1,12 @@ +[ + @decorator() class {}, + @decorator() class A {}, +]; + +class A { + @decorator() accessor #field; +} + +class B { + @decorator() #field () {} +} diff --git a/tests/format/typescript/type-arguments-bit-shift-left-like/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/type-arguments-bit-shift-left-like/__snapshots__/jsfmt.spec.js.snap index 8c1278a79..409110a22 100644 --- a/tests/format/typescript/type-arguments-bit-shift-left-like/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/type-arguments-bit-shift-left-like/__snapshots__/jsfmt.spec.js.snap @@ -49,7 +49,7 @@ exports[`3.ts [typescript] format 1`] = ` 2 |" `; -exports[`4.ts [babel-ts] format 1`] = ` +exports[`4.ts format 1`] = ` ====================================options===================================== parsers: ["typescript"] printWidth: 80 @@ -66,13 +66,6 @@ printWidth: 80 ================================================================================ `; -exports[`4.ts [typescript] format 1`] = ` -"Decorators are not valid here. (1:2) -> 1 | (@f<(v: T) => void>() class {}); - | ^^^^^^^^^^^^^^^^^^^^^^^ - 2 |" -`; - exports[`5.tsx [babel-ts] format 1`] = ` ====================================options===================================== parsers: ["typescript"] diff --git a/tests/format/typescript/type-arguments-bit-shift-left-like/jsfmt.spec.js b/tests/format/typescript/type-arguments-bit-shift-left-like/jsfmt.spec.js index 2775ae278..b3acebe65 100644 --- a/tests/format/typescript/type-arguments-bit-shift-left-like/jsfmt.spec.js +++ b/tests/format/typescript/type-arguments-bit-shift-left-like/jsfmt.spec.js @@ -1,3 +1,3 @@ run_spec(__dirname, ["typescript"], { - errors: { typescript: ["3.ts", "4.ts", "5.tsx"] }, + errors: { typescript: ["3.ts", "5.tsx"] }, }); From 10fe1ed6230c6dcdce879104b69cfb2274728c86 Mon Sep 17 00:00:00 2001 From: fisker Date: Tue, 21 Mar 2023 16:14:26 +0800 Subject: [PATCH 2/5] Release 2.8.6 --- .github/ISSUE_TEMPLATE/formatting.md | 2 +- .github/ISSUE_TEMPLATE/integration.md | 2 +- CHANGELOG.md | 29 ++++++++++++++++++ docs/browser.md | 30 +++++++++---------- package.json | 2 +- .../versioned_docs/version-stable/browser.md | 30 +++++++++---------- 6 files changed, 62 insertions(+), 33 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/formatting.md b/.github/ISSUE_TEMPLATE/formatting.md index e02c53586..dde56b359 100644 --- a/.github/ISSUE_TEMPLATE/formatting.md +++ b/.github/ISSUE_TEMPLATE/formatting.md @@ -26,7 +26,7 @@ Don't fill the form below manually! Let a program create a report for you: --> -**Prettier 2.8.5** +**Prettier 2.8.6** [Playground link](https://prettier.io/playground/#.....) ```sh diff --git a/.github/ISSUE_TEMPLATE/integration.md b/.github/ISSUE_TEMPLATE/integration.md index 12e84bc26..e3aba155c 100644 --- a/.github/ISSUE_TEMPLATE/integration.md +++ b/.github/ISSUE_TEMPLATE/integration.md @@ -20,7 +20,7 @@ BEFORE SUBMITTING AN ISSUE: **Environments:** -- Prettier Version: 2.8.5 +- Prettier Version: 2.8.6 - Running Prettier via: - Runtime: - Operating System: diff --git a/CHANGELOG.md b/CHANGELOG.md index 889c02730..e07428f2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# 2.8.6 + +[diff](https://github.com/prettier/prettier/compare/2.8.5...2.8.6) + +#### Allow decorators on private members and class expressions ([#14548](https://github.com/prettier/prettier/pull/14548) by [@fisker](https://github.com/fisker)) + + +```ts +// Input +class A { + @decorator() + #privateMethod () {} +} + +// Prettier 2.8.5 +SyntaxError: Decorators are not valid here. (2:3) + 1 | class A { +> 2 | @decorator() + | ^^^^^^^^^^^^ + 3 | #privateMethod () {} + 4 | } + +// Prettier 2.8.6 +class A { + @decorator() + #privateMethod() {} +} +``` + # 2.8.5 [diff](https://github.com/prettier/prettier/compare/2.8.4...2.8.5) diff --git a/docs/browser.md b/docs/browser.md index e196886d9..3300c8337 100644 --- a/docs/browser.md +++ b/docs/browser.md @@ -20,8 +20,8 @@ Required options: - **`plugins`**: Unlike the `format` function from the [Node.js-based API](api.md#prettierformatsource--options), this function doesn’t load plugins automatically. The `plugins` option is required because all the parsers included in the Prettier package come as plugins (for reasons of file size). These plugins are files named - - `parser-*.js` in and - - `parser-*.mjs` in + - `parser-*.js` in and + - `parser-*.mjs` in You need to load the ones that you’re going to use and pass them to `prettier.format` using the `plugins` option. @@ -32,8 +32,8 @@ See below for examples. ### Global ```html - - + + - + +