fix(rslint_parser): TS typed import equals
Adds support for parsing `import type X = require("./test");` syntax.
MODIFY
crates/rslint_parser/src/ast/generated/nodes.rs
crates/rslint_parser/src/ast/generated/nodes.rs
@@ -6130,6 +6130,7 @@ impl TsImportEqualsDeclaration {
pub fn as_fields(&self) -> TsImportEqualsDeclarationFields {
TsImportEqualsDeclarationFields {
import_token: self.import_token(),
+ type_token: self.type_token(),
id: self.id(),
eq_token: self.eq_token(),
module_reference: self.module_reference(),
@@ -6139,17 +6140,19 @@ impl TsImportEqualsDeclaration {
pub fn import_token(&self) -> SyntaxResult<SyntaxToken> {
support::required_token(&self.syntax, 0usize)
}
- pub fn id(&self) -> SyntaxResult<JsAnyBinding> { support::required_node(&self.syntax, 1usize) }
+ pub fn type_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, 1usize) }
+ pub fn id(&self) -> SyntaxResult<JsAnyBinding> { support::required_node(&self.syntax, 2usize) }
pub fn eq_token(&self) -> SyntaxResult<SyntaxToken> {
- support::required_token(&self.syntax, 2usize)
+ support::required_token(&self.syntax, 3usize)
}
pub fn module_reference(&self) -> SyntaxResult<TsAnyModuleReference> {
- support::required_node(&self.syntax, 3usize)
+ support::required_node(&self.syntax, 4usize)
}
- pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, 4usize) }
+ pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, 5usize) }
}
pub struct TsImportEqualsDeclarationFields {
pub import_token: SyntaxResult<SyntaxToken>,
+ pub type_token: Option<SyntaxToken>,
pub id: SyntaxResult<JsAnyBinding>,
pub eq_token: SyntaxResult<SyntaxToken>,
pub module_reference: SyntaxResult<TsAnyModuleReference>,
@@ -14284,6 +14287,10 @@ impl std::fmt::Debug for TsImportEqualsDeclaration {
"import_token",
&support::DebugSyntaxResult(self.import_token()),
)
+ .field(
+ "type_token",
+ &support::DebugOptionalElement(self.type_token()),
+ )
.field("id", &support::DebugSyntaxResult(self.id()))
.field("eq_token", &support::DebugSyntaxResult(self.eq_token()))
.field(

MODIFY
crates/rslint_parser/src/ast/generated/syntax_factory.rs
crates/rslint_parser/src/ast/generated/syntax_factory.rs
@@ -6438,7 +6438,7 @@ impl SyntaxFactory for JsSyntaxFactory {
}
TS_IMPORT_EQUALS_DECLARATION => {
let mut elements = (&children).into_iter();
- let mut slots: RawNodeSlots<5usize> = RawNodeSlots::default();
+ let mut slots: RawNodeSlots<6usize> = RawNodeSlots::default();
let mut current_element = elements.next();
if let Some(element) = &current_element {
if element.kind() == T![import] {
@@ -6447,6 +6447,13 @@ impl SyntaxFactory for JsSyntaxFactory {
}
}
slots.next_slot();
+ if let Some(element) = &current_element {
+ if element.kind() == T![type] {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ }
+ slots.next_slot();
if let Some(element) = &current_element {
if JsAnyBinding::can_cast(element.kind()) {
slots.mark_present();

MODIFY
crates/rslint_parser/src/syntax/module.rs
crates/rslint_parser/src/syntax/module.rs
@@ -132,7 +132,7 @@ pub(crate) fn parse_import_or_import_equals_declaration(p: &mut Parser) -> Parse
debug_assert!(p.state.name_map.is_empty());
p.state.duplicate_binding_parent = Some("import");
- let statement = if is_at_identifier_binding(p) && p.nth_at(1, T![=]) {
+ let statement = if is_at_identifier_binding(p) && (p.nth_at(1, T![=]) || p.nth_at(2, T![=])) {
let import_equals = parse_ts_import_equals_declaration_rest(p, import, start);
TypeScript.exclusive_syntax(p, import_equals, |p, decl| {
ts_only_syntax_error(p, "'import =' declarations", decl.range(p))

MODIFY
crates/rslint_parser/src/syntax/typescript.rs
crates/rslint_parser/src/syntax/typescript.rs
@@ -108,7 +108,11 @@ fn expect_ts_type_list(p: &mut Parser, clause_name: &str) -> CompletedMarker {
fn parse_ts_name_with_type_arguments(p: &mut Parser) -> ParsedSyntax {
parse_ts_name(p).map(|name| {
let m = name.precede(p);
+
+ if !p.has_linebreak_before_n(0) {
parse_ts_type_arguments(p).ok();
+ }
+
m.complete(p, TS_NAME_WITH_TYPE_ARGUMENTS)
})
}

MODIFY
crates/rslint_parser/src/syntax/typescript/statement.rs
crates/rslint_parser/src/syntax/typescript/statement.rs
@@ -516,6 +516,7 @@ fn parse_ts_global_declaration(p: &mut Parser) -> ParsedSyntax {
// namespace a.b {}
// import y = a;
// import z = a.b;
+// import type A = require("./a");
// export import n = a;
/// Parses everything after the `import` of an import equals declaration
@@ -524,6 +525,10 @@ pub(crate) fn parse_ts_import_equals_declaration_rest(
m: Marker,
stmt_start_pos: usize,
) -> CompletedMarker {
+ if is_nth_at_identifier_binding(p, 1) {
+ eat_contextual_keyword(p, "type", T![type]);
+ }
+
parse_identifier_binding(p).or_add_diagnostic(p, expected_identifier);
p.expect(T![=]);

MODIFY
crates/rslint_parser/test_data/inline/ok/ts_import_equals_declaration.rast
crates/rslint_parser/test_data/inline/ok/ts_import_equals_declaration.rast
@@ -4,6 +4,7 @@ JsModule {
items: JsModuleItemList [
TsImportEqualsDeclaration {
import_token: IMPORT_KW@0..7 "import" [] [Whitespace(" ")],
+ type_token: missing (optional),
id: JsIdentifierBinding {
name_token: IDENT@7..9 "x" [] [Whitespace(" ")],
},
@@ -37,6 +38,7 @@ JsModule {
},
TsImportEqualsDeclaration {
import_token: IMPORT_KW@46..54 "import" [Newline("\n")] [Whitespace(" ")],
+ type_token: missing (optional),
id: JsIdentifierBinding {
name_token: IDENT@54..56 "y" [] [Whitespace(" ")],
},
@@ -48,6 +50,7 @@ JsModule {
},
TsImportEqualsDeclaration {
import_token: IMPORT_KW@60..68 "import" [Newline("\n")] [Whitespace(" ")],
+ type_token: missing (optional),
id: JsIdentifierBinding {
name_token: IDENT@68..70 "z" [] [Whitespace(" ")],
},
@@ -63,40 +66,59 @@ JsModule {
},
semicolon_token: SEMICOLON@75..76 ";" [] [],
},
+ TsImportEqualsDeclaration {
+ import_token: IMPORT_KW@76..84 "import" [Newline("\n")] [Whitespace(" ")],
+ type_token: TYPE_KW@84..89 "type" [] [Whitespace(" ")],
+ id: JsIdentifierBinding {
+ name_token: IDENT@89..91 "A" [] [Whitespace(" ")],
+ },
+ eq_token: EQ@91..93 "=" [] [Whitespace(" ")],
+ module_reference: TsExternalModuleReference {
+ require_token: REQUIRE_KW@93..100 "require" [] [],
+ l_paren_token: L_PAREN@100..101 "(" [] [],
+ source: JsModuleSource {
+ value_token: JS_STRING_LITERAL@101..106 "\"./a\"" [] [],
+ },
+ r_paren_token: R_PAREN@106..107 ")" [] [],
+ },
+ semicolon_token: SEMICOLON@107..108 ";" [] [],
+ },
JsExport {
- export_token: EXPORT_KW@76..84 "export" [Newline("\n")] [Whitespace(" ")],
+ export_token: EXPORT_KW@108..116 "export" [Newline("\n")] [Whitespace(" ")],
export_clause: TsImportEqualsDeclaration {
- import_token: IMPORT_KW@84..91 "import" [] [Whitespace(" ")],
+ import_token: IMPORT_KW@116..123 "import" [] [Whitespace(" ")],
+ type_token: missing (optional),
id: JsIdentifierBinding {
- name_token: IDENT@91..93 "n" [] [Whitespace(" ")],
+ name_token: IDENT@123..125 "n" [] [Whitespace(" ")],
},
- eq_token: EQ@93..95 "=" [] [Whitespace(" ")],
+ eq_token: EQ@125..127 "=" [] [Whitespace(" ")],
module_reference: JsReferenceIdentifier {
- value_token: IDENT@95..96 "a" [] [],
+ value_token: IDENT@127..128 "a" [] [],
},
- semicolon_token: SEMICOLON@96..97 ";" [] [],
+ semicolon_token: SEMICOLON@128..129 ";" [] [],
},
},
],
- eof_token: EOF@97..98 "" [Newline("\n")] [],
+ eof_token: EOF@129..130 "" [Newline("\n")] [],
}
-0: JS_MODULE@0..98
+0: JS_MODULE@0..130
0: (empty)
1: JS_DIRECTIVE_LIST@0..0
- 2: JS_MODULE_ITEM_LIST@0..97
+ 2: JS_MODULE_ITEM_LIST@0..129
0: TS_IMPORT_EQUALS_DECLARATION@0..29
0: IMPORT_KW@0..7 "import" [] [Whitespace(" ")]
- 1: JS_IDENTIFIER_BINDING@7..9
+ 1: (empty)
+ 2: JS_IDENTIFIER_BINDING@7..9
0: IDENT@7..9 "x" [] [Whitespace(" ")]
- 2: EQ@9..11 "=" [] [Whitespace(" ")]
- 3: TS_EXTERNAL_MODULE_REFERENCE@11..28
+ 3: EQ@9..11 "=" [] [Whitespace(" ")]
+ 4: TS_EXTERNAL_MODULE_REFERENCE@11..28
0: REQUIRE_KW@11..18 "require" [] []
1: L_PAREN@18..19 "(" [] []
2: JS_MODULE_SOURCE@19..27
0: JS_STRING_LITERAL@19..27 "\"./test\"" [] []
3: R_PAREN@27..28 ")" [] []
- 4: SEMICOLON@28..29 ";" [] []
+ 5: SEMICOLON@28..29 ";" [] []
1: TS_MODULE_DECLARATION@29..46
0: NAMESPACE_KW@29..40 "namespace" [Newline("\n")] [Whitespace(" ")]
1: TS_QUALIFIED_MODULE_NAME@40..44
@@ -111,32 +133,48 @@ JsModule {
2: R_CURLY@45..46 "}" [] []
2: TS_IMPORT_EQUALS_DECLARATION@46..60
0: IMPORT_KW@46..54 "import" [Newline("\n")] [Whitespace(" ")]
- 1: JS_IDENTIFIER_BINDING@54..56
+ 1: (empty)
+ 2: JS_IDENTIFIER_BINDING@54..56
0: IDENT@54..56 "y" [] [Whitespace(" ")]
- 2: EQ@56..58 "=" [] [Whitespace(" ")]
- 3: JS_REFERENCE_IDENTIFIER@58..59
+ 3: EQ@56..58 "=" [] [Whitespace(" ")]
+ 4: JS_REFERENCE_IDENTIFIER@58..59
0: IDENT@58..59 "a" [] []
- 4: SEMICOLON@59..60 ";" [] []
+ 5: SEMICOLON@59..60 ";" [] []
3: TS_IMPORT_EQUALS_DECLARATION@60..76
0: IMPORT_KW@60..68 "import" [Newline("\n")] [Whitespace(" ")]
- 1: JS_IDENTIFIER_BINDING@68..70
+ 1: (empty)
+ 2: JS_IDENTIFIER_BINDING@68..70
0: IDENT@68..70 "z" [] [Whitespace(" ")]
- 2: EQ@70..72 "=" [] [Whitespace(" ")]
- 3: TS_QUALIFIED_NAME@72..75
+ 3: EQ@70..72 "=" [] [Whitespace(" ")]
+ 4: TS_QUALIFIED_NAME@72..75
0: JS_REFERENCE_IDENTIFIER@72..73
0: IDENT@72..73 "a" [] []
1: DOT@73..74 "." [] []
2: JS_NAME@74..75
0: IDENT@74..75 "b" [] []
- 4: SEMICOLON@75..76 ";" [] []
- 4: JS_EXPORT@76..97
- 0: EXPORT_KW@76..84 "export" [Newline("\n")] [Whitespace(" ")]
- 1: TS_IMPORT_EQUALS_DECLARATION@84..97
- 0: IMPORT_KW@84..91 "import" [] [Whitespace(" ")]
- 1: JS_IDENTIFIER_BINDING@91..93
- 0: IDENT@91..93 "n" [] [Whitespace(" ")]
- 2: EQ@93..95 "=" [] [Whitespace(" ")]
- 3: JS_REFERENCE_IDENTIFIER@95..96
- 0: IDENT@95..96 "a" [] []
- 4: SEMICOLON@96..97 ";" [] []
- 3: EOF@97..98 "" [Newline("\n")] []
+ 5: SEMICOLON@75..76 ";" [] []
+ 4: TS_IMPORT_EQUALS_DECLARATION@76..108
+ 0: IMPORT_KW@76..84 "import" [Newline("\n")] [Whitespace(" ")]
+ 1: TYPE_KW@84..89 "type" [] [Whitespace(" ")]
+ 2: JS_IDENTIFIER_BINDING@89..91
+ 0: IDENT@89..91 "A" [] [Whitespace(" ")]
+ 3: EQ@91..93 "=" [] [Whitespace(" ")]
+ 4: TS_EXTERNAL_MODULE_REFERENCE@93..107
+ 0: REQUIRE_KW@93..100 "require" [] []
+ 1: L_PAREN@100..101 "(" [] []
+ 2: JS_MODULE_SOURCE@101..106
+ 0: JS_STRING_LITERAL@101..106 "\"./a\"" [] []
+ 3: R_PAREN@106..107 ")" [] []
+ 5: SEMICOLON@107..108 ";" [] []
+ 5: JS_EXPORT@108..129
+ 0: EXPORT_KW@108..116 "export" [Newline("\n")] [Whitespace(" ")]
+ 1: TS_IMPORT_EQUALS_DECLARATION@116..129
+ 0: IMPORT_KW@116..123 "import" [] [Whitespace(" ")]
+ 1: (empty)
+ 2: JS_IDENTIFIER_BINDING@123..125
+ 0: IDENT@123..125 "n" [] [Whitespace(" ")]
+ 3: EQ@125..127 "=" [] [Whitespace(" ")]
+ 4: JS_REFERENCE_IDENTIFIER@127..128
+ 0: IDENT@127..128 "a" [] []
+ 5: SEMICOLON@128..129 ";" [] []
+ 3: EOF@129..130 "" [Newline("\n")] []

MODIFY
crates/rslint_parser/test_data/inline/ok/ts_import_equals_declaration.ts
crates/rslint_parser/test_data/inline/ok/ts_import_equals_declaration.ts
@@ -2,4 +2,5 @@ import x = require("./test");
namespace a.b {}
import y = a;
import z = a.b;
+import type A = require("./a");
export import n = a;

MODIFY
xtask/codegen/js.ungram
xtask/codegen/js.ungram
@@ -1598,6 +1598,7 @@ TsEnumMember =
TsImportEqualsDeclaration =
'import'
+ 'type'?
id: JsAnyBinding
'='
module_reference: TsAnyModuleReference

fix(rslint_parser): TS typed import equals (#2154)

Adds support for parsing `import type X = require("./test");` syntax.