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

Commit

Permalink
feat(rome_js_parser): EcmaScript @decorators #4252 (#4392)
Browse files Browse the repository at this point in the history
feat(rome_js_parser): EcmaScript @decorators #4252
  • Loading branch information
denbezrukov committed Apr 26, 2023
1 parent be3c71c commit c1b73f6
Show file tree
Hide file tree
Showing 122 changed files with 3,219 additions and 981 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,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

0 comments on commit c1b73f6

Please sign in to comment.