Skip to content

Commit

Permalink
feat(biome_js_parser): support ts resolution-mode import (#3462)
Browse files Browse the repository at this point in the history
  • Loading branch information
fireairforce authored Oct 17, 2024
1 parent 2a05cd4 commit 30a016b
Show file tree
Hide file tree
Showing 19 changed files with 964 additions and 155 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,18 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b

Contributed by @fireairforce

- Add support for parsing typescript's `resolution-mode` in Import Types([#2115](https://github.com/biomejs/biome/issues/2115))

```ts
export type Fs = typeof import('fs', { with: { 'resolution-mode': 'import' } });
export type TypeFromRequire =
import("pkg", { with: { "resolution-mode": "require" } }).TypeFromRequire;
export type TypeFromImport =
import("pkg", { with: { "resolution-mode": "import" } }).TypeFromImport;
```

Contributed by @fireairforce

#### Bug Fixes

- The CSS parser now accepts more emoji in identifiers ([#3627](https://github.com/biomejs/biome/issues/3627#issuecomment-2392388022)).
Expand Down
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type Fs = typeof import('fs', { with: { 'resolution-mode': 'import' } });
export type TypeFromRequire =
import("pkg", { with: { "resolution-mode": "require" } }).TypeFromRequire;
export type TypeFromImport =
import("pkg", { with: { "resolution-mode": "import" } }).TypeFromImport;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
source: crates/biome_formatter_test/src/snapshot_builder.rs
info: ts/type/import_type_with_resolution_mode.ts
---
# Input

```ts
export type Fs = typeof import('fs', { with: { 'resolution-mode': 'import' } });
export type TypeFromRequire =
import("pkg", { with: { "resolution-mode": "require" } }).TypeFromRequire;
export type TypeFromImport =
import("pkg", { with: { "resolution-mode": "import" } }).TypeFromImport;
```


=============================

# Outputs

## Output 1

-----
Indent style: Tab
Indent width: 2
Line ending: LF
Line width: 80
Quote style: Double Quotes
JSX quote style: Double Quotes
Quote properties: As needed
Trailing commas: All
Semicolons: Always
Arrow parentheses: Always
Bracket spacing: true
Bracket same line: false
Attribute Position: Auto
-----

```ts
export type Fs = typeof import("fs", { with: { "resolution-mode": "import" } });
export type TypeFromRequire = import("pkg", {
with: { "resolution-mode": "require" },
}).TypeFromRequire;
export type TypeFromImport = import("pkg", {
with: { "resolution-mode": "import" },
}).TypeFromImport;
```
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
49 changes: 45 additions & 4 deletions crates/biome_js_parser/src/syntax/typescript/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ 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_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;
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,
Expand All @@ -29,6 +31,7 @@ use crate::syntax::typescript::ts_parse_error::{
ts_in_out_modifier_cannot_appear_on_a_type_parameter,
};
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 +1153,11 @@ 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" } });
// type G = import("test", { with: { "resolution-mode": "import" } }).TypeFromImport;
// type H = import("test", { with: { "resolution-mode": "import" } })<string>;
// type I = import("test", { with: { "resolution-mode": "require" } }).C<string>;
// type J = typeof import("test", { with: { "resolution-mode": "require" } }).a.b.c.d.e.f;
fn parse_ts_import_type(p: &mut JsParser, context: TypeContext) -> ParsedSyntax {
if !p.at(T![typeof]) && !p.at(T![import]) {
return Absent;
Expand All @@ -1158,9 +1166,42 @@ fn parse_ts_import_type(p: &mut JsParser, context: TypeContext) -> ParsedSyntax
let m = p.start();
p.eat(T![typeof]);
p.expect(T![import]);
let args = p.start();
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![')']);
args.complete(p, JS_CALL_ARGUMENTS);

if p.at(T![.]) {
let qualifier = p.start();
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_js_parser/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ fn diagnostics_print_correctly() {
#[test]
pub fn quick_test() {
let code = r#"
import defer * as yNamespace from "y";
type A = typeof import("test");
"#;
let root = parse(
code,
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")
Loading

0 comments on commit 30a016b

Please sign in to comment.