Skip to content

Commit

Permalink
feat(biome_js_parser): support ts resolution-mode import
Browse files Browse the repository at this point in the history
  • Loading branch information
fireairforce committed Oct 14, 2024
1 parent 2342984 commit 4939375
Show file tree
Hide file tree
Showing 16 changed files with 290 additions and 5,372 deletions.
16 changes: 4 additions & 12 deletions crates/biome_js_factory/src/generated/node_factory.rs

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

18 changes: 2 additions & 16 deletions crates/biome_js_factory/src/generated/syntax_factory.rs

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

13 changes: 2 additions & 11 deletions crates/biome_js_formatter/src/ts/module/import_type.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use crate::prelude::*;
use crate::utils::{FormatLiteralStringToken, StringLiteralParentKind};

use biome_formatter::write;
use biome_js_syntax::TsImportType;
use biome_js_syntax::TsImportTypeFields;
Expand All @@ -13,9 +11,7 @@ impl FormatNodeRule<TsImportType> for FormatTsImportType {
let TsImportTypeFields {
typeof_token,
import_token,
l_paren_token,
argument_token,
r_paren_token,
arguments,
qualifier_clause,
type_arguments,
} = node.as_fields();
Expand All @@ -28,12 +24,7 @@ impl FormatNodeRule<TsImportType> for FormatTsImportType {
f,
[
import_token.format(),
l_paren_token.format(),
FormatLiteralStringToken::new(
&argument_token?,
StringLiteralParentKind::Expression
),
r_paren_token.format(),
arguments.format(),
qualifier_clause.format(),
type_arguments.format(),
]
Expand Down
5 changes: 3 additions & 2 deletions crates/biome_js_parser/src/syntax/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1450,12 +1450,13 @@ fn parse_primary_expression(p: &mut JsParser, context: ExpressionContext) -> Par
// test js import_call
// import("foo")
// import("foo", { assert: { type: 'json' } })
// import("foo", { with: { 'resolution-mode': 'import' } })

// test_err js import_invalid_args
// import()
// import(...["foo"])
// import("foo", { assert: { type: 'json' } }, "bar")

// import("foo", { with: { type: 'json' } }, "bar")
let args = p.start();
p.bump(T!['(']);
let args_list = p.start();
Expand Down Expand Up @@ -1834,7 +1835,7 @@ fn parse_array_expr(p: &mut JsParser) -> ParsedSyntax {
// test_err js spread
// [...]
/// A spread element consisting of three dots and an assignment expression such as `...foo`
fn parse_spread_element(p: &mut JsParser, context: ExpressionContext) -> ParsedSyntax {
pub(crate) fn parse_spread_element(p: &mut JsParser, context: ExpressionContext) -> ParsedSyntax {
if !p.at(T![...]) {
return Absent;
}
Expand Down
46 changes: 37 additions & 9 deletions crates/biome_js_parser/src/syntax/typescript/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,13 @@ use crate::parser::{RecoveryError, RecoveryResult};
use crate::prelude::*;
use crate::state::{EnterType, SignatureFlags};
use crate::syntax::expr::{
is_at_binary_operator, is_at_expression, is_at_identifier, is_nth_at_identifier,
is_nth_at_identifier_or_keyword, parse_big_int_literal_expression, parse_identifier,
parse_literal_expression, parse_name, parse_number_literal_expression,
parse_reference_identifier, parse_template_elements, ExpressionContext,
is_at_binary_operator, is_at_expression, is_at_identifier, is_nth_at_identifier, is_nth_at_identifier_or_keyword, parse_assignment_expression_or_higher, parse_big_int_literal_expression, parse_identifier, parse_literal_expression, parse_name, parse_number_literal_expression, parse_reference_identifier, parse_template_elements, ExpressionContext
};
use crate::syntax::function::{
parse_formal_parameter, parse_parameter_list, skip_parameter_start, ParameterContext,
};
use crate::syntax::js_parse_error::{
decorators_not_allowed, expected_identifier, expected_object_member_name, expected_parameter,
expected_parameters, expected_property_or_signature, modifier_already_seen,
modifier_must_precede_modifier,
decorators_not_allowed, expected_identifier, expected_object_member_name, expected_parameter, expected_parameters, expected_property_or_signature, modifier_already_seen, modifier_must_precede_modifier
};
use crate::syntax::metavariable::parse_metavariable;
use crate::syntax::object::{
Expand All @@ -28,7 +23,9 @@ use crate::syntax::typescript::ts_parse_error::{
ts_const_modifier_cannot_appear_on_a_type_parameter,
ts_in_out_modifier_cannot_appear_on_a_type_parameter,
};
use crate::syntax::js_parse_error;
use biome_parser::parse_lists::{ParseNodeList, ParseSeparatedList};
use biome_parser::ParserProgress;
use enumflags2::{bitflags, make_bitflags, BitFlags};
use smallvec::SmallVec;

Expand Down Expand Up @@ -1150,6 +1147,7 @@ fn parse_ts_mapped_type_optional_modifier_clause(p: &mut JsParser) -> ParsedSynt
// type C = typeof import("test").a.b.c.d.e.f;
// type D = import("test")<string>;
// type E = import("test").C<string>;
// type F = typeof import("test", { with: { 'resolution-mode': 'import' } });
fn parse_ts_import_type(p: &mut JsParser, context: TypeContext) -> ParsedSyntax {
if !p.at(T![typeof]) && !p.at(T![import]) {
return Absent;
Expand All @@ -1159,7 +1157,38 @@ fn parse_ts_import_type(p: &mut JsParser, context: TypeContext) -> ParsedSyntax
p.eat(T![typeof]);
p.expect(T![import]);
p.expect(T!['(']);
p.expect(JS_STRING_LITERAL);
let args_list = p.start();

let mut progress = ParserProgress::default();
let mut error_range_start = p.cur_range().start();
let mut args_count = 0;

while !p.at(EOF) && !p.at(T![')']) {
progress.assert_progressing(p);
args_count += 1;

if args_count == 3 {
error_range_start = p.cur_range().start();
}

parse_assignment_expression_or_higher(p, ExpressionContext::default())
.or_add_diagnostic(p, js_parse_error::expected_expression_assignment);

if p.at(T![,]) {
p.bump_any();
} else {
break;
}
}
args_list.complete(p, JS_CALL_ARGUMENT_LIST);

if args_count == 0 || args_count > 2 {
let err = p.err_builder(
"`typeof import()` requires exactly one or two arguments. ",
error_range_start..p.cur_range().end(),
);
p.error(err);
}
p.expect(T![')']);

if p.at(T![.]) {
Expand All @@ -1170,7 +1199,6 @@ fn parse_ts_import_type(p: &mut JsParser, context: TypeContext) -> ParsedSyntax
}

parse_ts_type_arguments(p, context).ok();

Present(m.complete(p, TS_IMPORT_TYPE))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import()
import(...["foo"])
import("foo", { assert: { type: 'json' } }, "bar")
import("foo", { with: { type: 'json' } }, "bar")
104 changes: 98 additions & 6 deletions crates/biome_js_parser/test_data/inline/err/import_invalid_args.rast
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,62 @@ JsModule {
},
semicolon_token: missing (optional),
},
JsExpressionStatement {
expression: JsImportCallExpression {
import_token: IMPORT_KW@78..85 "import" [Newline("\n")] [],
arguments: JsCallArguments {
l_paren_token: L_PAREN@85..86 "(" [] [],
args: JsCallArgumentList [
JsStringLiteralExpression {
value_token: JS_STRING_LITERAL@86..91 "\"foo\"" [] [],
},
COMMA@91..93 "," [] [Whitespace(" ")],
JsObjectExpression {
l_curly_token: L_CURLY@93..95 "{" [] [Whitespace(" ")],
members: JsObjectMemberList [
JsPropertyObjectMember {
name: JsLiteralMemberName {
value: IDENT@95..99 "with" [] [],
},
colon_token: COLON@99..101 ":" [] [Whitespace(" ")],
value: JsObjectExpression {
l_curly_token: L_CURLY@101..103 "{" [] [Whitespace(" ")],
members: JsObjectMemberList [
JsPropertyObjectMember {
name: JsLiteralMemberName {
value: IDENT@103..107 "type" [] [],
},
colon_token: COLON@107..109 ":" [] [Whitespace(" ")],
value: JsStringLiteralExpression {
value_token: JS_STRING_LITERAL@109..116 "'json'" [] [Whitespace(" ")],
},
},
],
r_curly_token: R_CURLY@116..118 "}" [] [Whitespace(" ")],
},
},
],
r_curly_token: R_CURLY@118..119 "}" [] [],
},
COMMA@119..121 "," [] [Whitespace(" ")],
JsStringLiteralExpression {
value_token: JS_STRING_LITERAL@121..126 "\"bar\"" [] [],
},
],
r_paren_token: R_PAREN@126..127 ")" [] [],
},
},
semicolon_token: missing (optional),
},
],
eof_token: EOF@78..79 "" [Newline("\n")] [],
eof_token: EOF@127..128 "" [Newline("\n")] [],
}

0: JS_MODULE@0..79
0: JS_MODULE@0..128
0: (empty)
1: (empty)
2: JS_DIRECTIVE_LIST@0..0
3: JS_MODULE_ITEM_LIST@0..78
3: JS_MODULE_ITEM_LIST@0..127
0: JS_EXPRESSION_STATEMENT@0..8
0: JS_IMPORT_CALL_EXPRESSION@0..8
0: IMPORT_KW@0..6 "import" [] []
Expand Down Expand Up @@ -158,7 +205,39 @@ JsModule {
0: JS_STRING_LITERAL@72..77 "\"bar\"" [] []
2: R_PAREN@77..78 ")" [] []
1: (empty)
4: EOF@78..79 "" [Newline("\n")] []
3: JS_EXPRESSION_STATEMENT@78..127
0: JS_IMPORT_CALL_EXPRESSION@78..127
0: IMPORT_KW@78..85 "import" [Newline("\n")] []
1: JS_CALL_ARGUMENTS@85..127
0: L_PAREN@85..86 "(" [] []
1: JS_CALL_ARGUMENT_LIST@86..126
0: JS_STRING_LITERAL_EXPRESSION@86..91
0: JS_STRING_LITERAL@86..91 "\"foo\"" [] []
1: COMMA@91..93 "," [] [Whitespace(" ")]
2: JS_OBJECT_EXPRESSION@93..119
0: L_CURLY@93..95 "{" [] [Whitespace(" ")]
1: JS_OBJECT_MEMBER_LIST@95..118
0: JS_PROPERTY_OBJECT_MEMBER@95..118
0: JS_LITERAL_MEMBER_NAME@95..99
0: IDENT@95..99 "with" [] []
1: COLON@99..101 ":" [] [Whitespace(" ")]
2: JS_OBJECT_EXPRESSION@101..118
0: L_CURLY@101..103 "{" [] [Whitespace(" ")]
1: JS_OBJECT_MEMBER_LIST@103..116
0: JS_PROPERTY_OBJECT_MEMBER@103..116
0: JS_LITERAL_MEMBER_NAME@103..107
0: IDENT@103..107 "type" [] []
1: COLON@107..109 ":" [] [Whitespace(" ")]
2: JS_STRING_LITERAL_EXPRESSION@109..116
0: JS_STRING_LITERAL@109..116 "'json'" [] [Whitespace(" ")]
2: R_CURLY@116..118 "}" [] [Whitespace(" ")]
2: R_CURLY@118..119 "}" [] []
3: COMMA@119..121 "," [] [Whitespace(" ")]
4: JS_STRING_LITERAL_EXPRESSION@121..126
0: JS_STRING_LITERAL@121..126 "\"bar\"" [] []
2: R_PAREN@126..127 ")" [] []
1: (empty)
4: EOF@127..128 "" [Newline("\n")] []
--
import_invalid_args.js:1:8 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Expand All @@ -178,7 +257,7 @@ import_invalid_args.js:2:8 parse ━━━━━━━━━━━━━━━
> 2 │ import(...["foo"])
│ ^^^^^^^^^^
3 │ import("foo", { assert: { type: 'json' } }, "bar")
4 │
4 │ import("foo", { with: { type: 'json' } }, "bar")

--
import_invalid_args.js:3:45 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Expand All @@ -189,9 +268,22 @@ import_invalid_args.js:3:45 parse ━━━━━━━━━━━━━━━
2 │ import(...["foo"])
> 3 │ import("foo", { assert: { type: 'json' } }, "bar")
│ ^^^^^^
4 │
4 │ import("foo", { with: { type: 'json' } }, "bar")
5 │

--
import_invalid_args.js:4:43 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× `import()` requires exactly one or two arguments.

2 │ import(...["foo"])
3 │ import("foo", { assert: { type: 'json' } }, "bar")
> 4 │ import("foo", { with: { type: 'json' } }, "bar")
│ ^^^^^^
5 │

--
import()
import(...["foo"])
import("foo", { assert: { type: 'json' } }, "bar")
import("foo", { with: { type: 'json' } }, "bar")
1 change: 1 addition & 0 deletions crates/biome_js_parser/test_data/inline/ok/import_call.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
import("foo")
import("foo", { assert: { type: 'json' } })
import("foo", { with: { 'resolution-mode': 'import' } })
Loading

0 comments on commit 4939375

Please sign in to comment.