Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

feat(rome_js_parser): EcmaScript @decorators #4252 #4392

Merged
merged 11 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ the code action is not formatted.
- import "module" assert {}
+ import "module" with {}
```

- Allow decorators before `export` and `export default`. [#4252](https://github.com/rome/tools/issues/4252)

### VSCode
### JavaScript APIs
Expand Down
7 changes: 6 additions & 1 deletion crates/rome_js_factory/src/generated/node_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion crates/rome_js_factory/src/generated/syntax_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions crates/rome_js_formatter/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rome_formatter::{
write,
};
use rome_js_syntax::suppression::parse_suppression_comment;
use rome_js_syntax::JsSyntaxKind::JS_EXPORT;
use rome_js_syntax::{
AnyJsClass, AnyJsName, AnyJsRoot, AnyJsStatement, JsArrayHole, JsArrowFunctionExpression,
JsBlockStatement, JsCallArguments, JsCatchClause, JsEmptyStatement, JsFinallyClause,
Expand Down Expand Up @@ -369,6 +370,29 @@ fn handle_class_comment(comment: DecoratedComment<JsLanguage>) -> CommentPlaceme
return CommentPlacement::Default(comment);
}

// ```javascript
// @decorator
// // comment
// class Foo {}
// ```
if (AnyJsClass::can_cast(comment.enclosing_node().kind())
&& comment
.following_token()
.map_or(false, |token| token.kind() == JsSyntaxKind::CLASS_KW))
// ```javascript
// @decorator
// // comment
// export class Foo {}
// ```
|| comment.enclosing_node().kind() == JS_EXPORT
{
if let Some(preceding) = comment.preceding_node() {
if preceding.kind() == JsSyntaxKind::JS_DECORATOR {
return CommentPlacement::trailing(preceding.clone(), comment);
}
}
}

let first_member = if let Some(class) = AnyJsClass::cast_ref(comment.enclosing_node()) {
class.members().first().map(AstNode::into_syntax)
} else if let Some(interface) = TsInterfaceDeclaration::cast_ref(comment.enclosing_node()) {
Expand Down
12 changes: 9 additions & 3 deletions crates/rome_js_formatter/src/js/auxiliary/decorator.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
use crate::prelude::*;
use rome_js_syntax::JsDecorator;
use rome_rowan::AstNode;
use rome_formatter::write;
use rome_js_syntax::{JsDecorator, JsDecoratorFields};

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatJsDecorator;
impl FormatNodeRule<JsDecorator> for FormatJsDecorator {
fn fmt_fields(&self, node: &JsDecorator, f: &mut JsFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let JsDecoratorFields {
at_token,
expression,
} = node.as_fields();

write![f, [at_token.format(), expression.format()]]
}
}
11 changes: 10 additions & 1 deletion crates/rome_js_formatter/src/js/module/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,19 @@ pub(crate) struct FormatJsExport;
impl FormatNodeRule<JsExport> for FormatJsExport {
fn fmt_fields(&self, node: &JsExport, f: &mut JsFormatter) -> FormatResult<()> {
let JsExportFields {
decorators,
export_token,
export_clause,
} = node.as_fields();

write![f, [export_token.format(), space(), export_clause.format()]]
write![
f,
[
decorators.format(),
export_token.format(),
space(),
export_clause.format()
]
]
}
}
2 changes: 2 additions & 0 deletions crates/rome_js_formatter/src/syntax_rewriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ impl JsFormatSyntaxRewriter {

// Keep parentheses around unknown expressions. Rome can't know the precedence.
if inner.kind().is_bogus()
// Don't remove parentheses if the expression is a decorator
|| inner.grand_parent().map_or(false, |node| node.kind() == JsSyntaxKind::JS_DECORATOR)
// Don't remove parentheses if they have skipped trivia. We don't know for certain what the intended syntax is.
// Nor if there's a leading type cast comment
|| has_type_cast_comment_or_skipped(&l_paren.leading_trivia())
Expand Down
22 changes: 16 additions & 6 deletions crates/rome_js_formatter/tests/quick_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ mod language {
// use this test check if your snippet prints as you wish, without using a snapshot
fn quick_test() {
let src = r#"
const foo = @deco class {}
const bar =
(
@deco
class {
//
}
);

"#;
let syntax = SourceType::tsx();
let tree = parse(src, syntax);
Expand All @@ -31,11 +38,14 @@ const foo = @deco class {}

assert_eq!(
result.as_code(),
r#"[
5,
7234932436,
// comment 3
];
r#"
// A
@Foo()
// B
@Bar()
// C
export class Bar{}

"#
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ export default @decorator class {}

-export default
-@decorator
-class {}
+export default (@decorator
+class {});
+export default @decorator
class {}
```

# Output
Expand All @@ -37,8 +36,8 @@ export default @decorator class {}
export @decorator
class Foo {}

export default (@decorator
class {});
export default @decorator
class {}
```


Original file line number Diff line number Diff line change
Expand Up @@ -82,31 +82,4 @@ class {
};
```

# Errors
```
classes.js:3:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators are not valid here.

1 │ @deco class Foo {}
2 │
> 3 │ @deco export class Bar {}
│ ^^^^^
4 │
5 │ @deco export default class Baz {}

classes.js:5:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators are not valid here.

3 │ @deco export class Bar {}
4 │
> 5 │ @deco export default class Baz {}
│ ^^^^^
6 │
7 │ const foo = @deco class {


```


This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ multiple.js:7:3 parse ━━━━━━━━━━━━━━━━━━━
10 │ eyes: 2
11 │ };

i Decorators are only valid on class declarations, class expressions, and class methods.

multiple.js:10:7 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Expected a semicolon or an implicit semicolon after a statement, but found none
Expand Down
Loading