From 985f0cad06b9de5f9e98bed3ad62769e0f3c7528 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sat, 29 Apr 2023 21:26:13 +0800 Subject: [PATCH] fix(es/parser): Parse decorators after `export` (#7340) --- .../fixture/issues-6xxx/6984/1/input/.swcrc | 25 ++++++++++++++++ .../fixture/issues-6xxx/6984/1/input/index.js | 12 ++++++++ .../issues-6xxx/6984/1/output/index.js | 30 +++++++++++++++++++ crates/swc_ecma_parser/src/error.rs | 4 +++ .../src/parser/stmt/module_item.rs | 24 ++++++++++++++- .../file3.ts | 4 +++ .../file3.ts.swc-stderr | 7 +++++ .../file6.ts | 5 ++++ .../file6.ts.swc-stderr | 7 +++++ .../file7.ts | 4 +++ .../file7.ts.swc-stderr | 7 +++++ .../class-decorators/output.ts | 3 +- 12 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 crates/swc/tests/fixture/issues-6xxx/6984/1/input/.swcrc create mode 100644 crates/swc/tests/fixture/issues-6xxx/6984/1/input/index.js create mode 100644 crates/swc/tests/fixture/issues-6xxx/6984/1/output/index.js create mode 100644 crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file3.ts create mode 100644 crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file3.ts.swc-stderr create mode 100644 crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file6.ts create mode 100644 crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file6.ts.swc-stderr create mode 100644 crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file7.ts create mode 100644 crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file7.ts.swc-stderr diff --git a/crates/swc/tests/fixture/issues-6xxx/6984/1/input/.swcrc b/crates/swc/tests/fixture/issues-6xxx/6984/1/input/.swcrc new file mode 100644 index 00000000000..98c5126e092 --- /dev/null +++ b/crates/swc/tests/fixture/issues-6xxx/6984/1/input/.swcrc @@ -0,0 +1,25 @@ +{ + "jsc": { + "externalHelpers": true, + "parser": { + "syntax": "ecmascript", + "jsx": true, + "decorators": true, + "dynamicImport": true, + "decoratorsBeforeExport": true + }, + "transform": { + "legacyDecorator": true, + "decoratorMetadata": true + }, + "minify": { + "compress": false + } + }, + "isModule": "unknown", + "env": { + "mode": "usage", + "coreJs": "3", + "dynamicImport": true + } +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-6xxx/6984/1/input/index.js b/crates/swc/tests/fixture/issues-6xxx/6984/1/input/index.js new file mode 100644 index 00000000000..82062f1984b --- /dev/null +++ b/crates/swc/tests/fixture/issues-6xxx/6984/1/input/index.js @@ -0,0 +1,12 @@ + +import React from 'react' +import { withRouter } from 'react-router-dom' + +export default +@withRouter +class App extends React.Component { + render() { + console.log(this.props) + return
134
+ } +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-6xxx/6984/1/output/index.js b/crates/swc/tests/fixture/issues-6xxx/6984/1/output/index.js new file mode 100644 index 00000000000..4bd3d30fd99 --- /dev/null +++ b/crates/swc/tests/fixture/issues-6xxx/6984/1/output/index.js @@ -0,0 +1,30 @@ +import { _ as _class_call_check } from "@swc/helpers/_/_class_call_check"; +import { _ as _create_class } from "@swc/helpers/_/_create_class"; +import { _ as _inherits } from "@swc/helpers/_/_inherits"; +import { _ as _create_super } from "@swc/helpers/_/_create_super"; +import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate"; +import React from "react"; +import { withRouter } from "react-router-dom"; +var App = function(_React_Component) { + "use strict"; + _inherits(App, _React_Component); + var _super = _create_super(App); + function App() { + _class_call_check(this, App); + return _super.apply(this, arguments); + } + _create_class(App, [ + { + key: "render", + value: function render() { + console.log(this.props); + return React.createElement("div", null, "134"); + } + } + ]); + return App; +}(React.Component); +App = _ts_decorate([ + withRouter +], App); +export { App as default }; diff --git a/crates/swc_ecma_parser/src/error.rs b/crates/swc_ecma_parser/src/error.rs index aa32d4208e4..6ea6f96708d 100644 --- a/crates/swc_ecma_parser/src/error.rs +++ b/crates/swc_ecma_parser/src/error.rs @@ -270,6 +270,7 @@ pub enum SyntaxError { TS2499, TS2703, TS4112, + TS8038, TSTypeAnnotationAfterAssign, TsNonNullAssertionNotAllowed(JsWord), @@ -691,6 +692,9 @@ impl SyntaxError { SyntaxError::TS4112 => "This member cannot have an 'override' modifier because its \ containing class does not extend another class." .into(), + SyntaxError::TS8038 => "Decorators may not appear after `export` or `export default` \ + if they also appear before `export`." + .into(), SyntaxError::TSTypeAnnotationAfterAssign => { "Type annotations must come before default assignments".into() } diff --git a/crates/swc_ecma_parser/src/parser/stmt/module_item.rs b/crates/swc_ecma_parser/src/parser/stmt/module_item.rs index d32acd01eb1..d2b2003f2da 100644 --- a/crates/swc_ecma_parser/src/parser/stmt/module_item.rs +++ b/crates/swc_ecma_parser/src/parser/stmt/module_item.rs @@ -327,7 +327,7 @@ impl Parser { Ok(self.with_ctx(ctx).parse_binding_ident()?.id) } - fn parse_export(&mut self, decorators: Vec) -> PResult { + fn parse_export(&mut self, mut decorators: Vec) -> PResult { if !self.ctx().module { // Switch to module mode let ctx = Context { @@ -417,6 +417,17 @@ impl Parser { let mut export_default = None; if !type_only && eat!(self, "default") { + if is!(self, '@') { + let start = cur_pos!(self); + let after_decorators = self.parse_decorators(false)?; + + if !decorators.is_empty() { + syntax_error!(self, span!(self, start), SyntaxError::TS8038); + } + + decorators = after_decorators; + } + if self.input.syntax().typescript() { if is!(self, "abstract") && peeked_is!(self, "class") @@ -477,6 +488,17 @@ impl Parser { } } + if is!(self, '@') { + let start = cur_pos!(self); + let after_decorators = self.parse_decorators(false)?; + + if !decorators.is_empty() { + syntax_error!(self, span!(self, start), SyntaxError::TS8038); + } + + decorators = after_decorators; + } + let decl = if !type_only && is!(self, "class") { let class_start = cur_pos!(self); self.parse_class_decl(start, class_start, decorators, false)? diff --git a/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file3.ts b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file3.ts new file mode 100644 index 00000000000..09184b6b16f --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file3.ts @@ -0,0 +1,4 @@ +// @filename: file3.js + +// error +export @dec default class C3 {} diff --git a/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file3.ts.swc-stderr b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file3.ts.swc-stderr new file mode 100644 index 00000000000..99edb01feea --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file3.ts.swc-stderr @@ -0,0 +1,7 @@ + + x Expected '{', got 'default' + ,-[$DIR/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file3.ts:3:1] + 3 | // error + 4 | export @dec default class C3 {} + : ^^^^^^^ + `---- diff --git a/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file6.ts b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file6.ts new file mode 100644 index 00000000000..19263ffddb5 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file6.ts @@ -0,0 +1,5 @@ +// @filename: file6.js + +// error +@dec export @dec class C6 {} + diff --git a/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file6.ts.swc-stderr b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file6.ts.swc-stderr new file mode 100644 index 00000000000..7f1019d5c84 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file6.ts.swc-stderr @@ -0,0 +1,7 @@ + + x Decorators may not appear after `export` or `export default` if they also appear before `export`. + ,-[$DIR/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file6.ts:3:1] + 3 | // error + 4 | @dec export @dec class C6 {} + : ^^^^ + `---- diff --git a/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file7.ts b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file7.ts new file mode 100644 index 00000000000..ce21b253d24 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file7.ts @@ -0,0 +1,4 @@ +// @filename: file7.js + +// error +@dec export default @dec class C7 {} diff --git a/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file7.ts.swc-stderr b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file7.ts.swc-stderr new file mode 100644 index 00000000000..137a45a6ec1 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file7.ts.swc-stderr @@ -0,0 +1,7 @@ + + x Decorators may not appear after `export` or `export default` if they also appear before `export`. + ,-[$DIR/tests/typescript-errors/esDecorators/classDeclaration/esDecorators-classDeclaration-exportModifier/file7.ts:3:1] + 3 | // error + 4 | @dec export default @dec class C7 {} + : ^^^^ + `---- diff --git a/crates/swc_ecma_transforms/tests/fixture/legacy-only/decl-to-expression/class-decorators/output.ts b/crates/swc_ecma_transforms/tests/fixture/legacy-only/decl-to-expression/class-decorators/output.ts index c58f8a3dab2..f41d99f64d5 100644 --- a/crates/swc_ecma_transforms/tests/fixture/legacy-only/decl-to-expression/class-decorators/output.ts +++ b/crates/swc_ecma_transforms/tests/fixture/legacy-only/decl-to-expression/class-decorators/output.ts @@ -1,4 +1,4 @@ -export default class A { +let A = class A { }; A = _ts_decorate([ dec @@ -8,3 +8,4 @@ let B = class B { B = _ts_decorate([ dec ], B); +export { A as default };