mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 12:51:57 +00:00
feat(parser): parse import attributes in TSImportType (#2436)
close: #239464d2eeea7b/src/compiler/types.ts (L2177-L2185)The corresponding test cases were skipped, so I manually added some cases to miscf5db48237f/tasks/coverage/src/typescript.rs (L118-L121)
This commit is contained in:
parent
5bd2ce6ecf
commit
60db720fa6
9 changed files with 157 additions and 7 deletions
|
|
@ -778,9 +778,37 @@ pub struct TSImportType<'a> {
|
||||||
pub is_type_of: bool,
|
pub is_type_of: bool,
|
||||||
pub argument: TSType<'a>,
|
pub argument: TSType<'a>,
|
||||||
pub qualifier: Option<TSTypeName<'a>>,
|
pub qualifier: Option<TSTypeName<'a>>,
|
||||||
|
pub attributes: Option<TSImportAttributes<'a>>,
|
||||||
pub type_parameters: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
|
pub type_parameters: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "camelCase"))]
|
||||||
|
#[cfg_attr(all(feature = "serde", feature = "wasm"), derive(tsify::Tsify))]
|
||||||
|
pub struct TSImportAttributes<'a> {
|
||||||
|
#[cfg_attr(feature = "serde", serde(flatten))]
|
||||||
|
pub span: Span,
|
||||||
|
pub elements: Vec<'a, TSImportAttribute<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "camelCase"))]
|
||||||
|
#[cfg_attr(all(feature = "serde", feature = "wasm"), derive(tsify::Tsify))]
|
||||||
|
pub struct TSImportAttribute<'a> {
|
||||||
|
#[cfg_attr(feature = "serde", serde(flatten))]
|
||||||
|
pub span: Span,
|
||||||
|
pub name: TSImportAttributeName,
|
||||||
|
pub value: Expression<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))]
|
||||||
|
#[cfg_attr(all(feature = "serde", feature = "wasm"), derive(tsify::Tsify))]
|
||||||
|
pub enum TSImportAttributeName {
|
||||||
|
Identifier(IdentifierName),
|
||||||
|
StringLiteral(StringLiteral),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "camelCase"))]
|
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "camelCase"))]
|
||||||
#[cfg_attr(all(feature = "serde", feature = "wasm"), derive(tsify::Tsify))]
|
#[cfg_attr(all(feature = "serde", feature = "wasm"), derive(tsify::Tsify))]
|
||||||
|
|
|
||||||
|
|
@ -1652,6 +1652,7 @@ impl<'a> AstBuilder<'a> {
|
||||||
is_type_of: bool,
|
is_type_of: bool,
|
||||||
argument: TSType<'a>,
|
argument: TSType<'a>,
|
||||||
qualifier: Option<TSTypeName<'a>>,
|
qualifier: Option<TSTypeName<'a>>,
|
||||||
|
attributes: Option<TSImportAttributes<'a>>,
|
||||||
type_parameters: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
|
type_parameters: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
|
||||||
) -> TSType<'a> {
|
) -> TSType<'a> {
|
||||||
TSType::TSImportType(self.alloc(TSImportType {
|
TSType::TSImportType(self.alloc(TSImportType {
|
||||||
|
|
@ -1659,6 +1660,7 @@ impl<'a> AstBuilder<'a> {
|
||||||
is_type_of,
|
is_type_of,
|
||||||
argument,
|
argument,
|
||||||
qualifier,
|
qualifier,
|
||||||
|
attributes,
|
||||||
type_parameters,
|
type_parameters,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -159,3 +159,35 @@ impl<'a> SeparatedList<'a> for TSTypeArgumentList<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TSImportAttributeList<'a> {
|
||||||
|
pub elements: Vec<'a, TSImportAttribute<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SeparatedList<'a> for TSImportAttributeList<'a> {
|
||||||
|
fn new(p: &ParserImpl<'a>) -> Self {
|
||||||
|
Self { elements: p.ast.new_vec() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&self) -> Kind {
|
||||||
|
Kind::LCurly
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&self) -> Kind {
|
||||||
|
Kind::RCurly
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> {
|
||||||
|
let span = p.start_span();
|
||||||
|
let name = match p.cur_kind() {
|
||||||
|
Kind::Str => TSImportAttributeName::StringLiteral(p.parse_literal_string()?),
|
||||||
|
_ => TSImportAttributeName::Identifier(p.parse_identifier_name()?),
|
||||||
|
};
|
||||||
|
|
||||||
|
p.expect(Kind::Colon)?;
|
||||||
|
let value = p.parse_expression()?;
|
||||||
|
let element = TSImportAttribute { span: p.end_span(span), name, value };
|
||||||
|
self.elements.push(element);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ use crate::{
|
||||||
js::list::{ArrayPatternList, ObjectPatternProperties},
|
js::list::{ArrayPatternList, ObjectPatternProperties},
|
||||||
lexer::Kind,
|
lexer::Kind,
|
||||||
list::{NormalList, SeparatedList},
|
list::{NormalList, SeparatedList},
|
||||||
|
ts::list::TSImportAttributeList,
|
||||||
Context, ParserImpl,
|
Context, ParserImpl,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -763,6 +764,8 @@ impl<'a> ParserImpl<'a> {
|
||||||
self.expect(Kind::Import)?;
|
self.expect(Kind::Import)?;
|
||||||
self.expect(Kind::LParen)?;
|
self.expect(Kind::LParen)?;
|
||||||
let argument = self.parse_ts_type()?;
|
let argument = self.parse_ts_type()?;
|
||||||
|
let attributes =
|
||||||
|
if self.eat(Kind::Comma) { Some(self.parse_ts_import_attributes()?) } else { None };
|
||||||
self.expect(Kind::RParen)?;
|
self.expect(Kind::RParen)?;
|
||||||
|
|
||||||
let qualifier = if self.eat(Kind::Dot) { Some(self.parse_ts_type_name()?) } else { None };
|
let qualifier = if self.eat(Kind::Dot) { Some(self.parse_ts_type_name()?) } else { None };
|
||||||
|
|
@ -774,10 +777,22 @@ impl<'a> ParserImpl<'a> {
|
||||||
is_type_of,
|
is_type_of,
|
||||||
argument,
|
argument,
|
||||||
qualifier,
|
qualifier,
|
||||||
|
attributes,
|
||||||
type_parameters,
|
type_parameters,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_ts_import_attributes(&mut self) -> Result<TSImportAttributes<'a>> {
|
||||||
|
let span = self.start_span();
|
||||||
|
// { with:
|
||||||
|
self.expect(Kind::LCurly)?;
|
||||||
|
self.expect(Kind::With)?;
|
||||||
|
self.expect(Kind::Colon)?;
|
||||||
|
let elements = TSImportAttributeList::parse(self)?.elements;
|
||||||
|
self.expect(Kind::RCurly)?;
|
||||||
|
Ok(TSImportAttributes { span, elements })
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_ts_constructor_type(&mut self) -> Result<TSType<'a>> {
|
fn parse_ts_constructor_type(&mut self) -> Result<TSType<'a>> {
|
||||||
let span = self.start_span();
|
let span = self.start_span();
|
||||||
let r#abstract = self.eat(Kind::Abstract);
|
let r#abstract = self.eat(Kind::Abstract);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
codegen_misc Summary:
|
codegen_misc Summary:
|
||||||
AST Parsed : 10/10 (100.00%)
|
AST Parsed : 11/11 (100.00%)
|
||||||
Positive Passed: 10/10 (100.00%)
|
Positive Passed: 11/11 (100.00%)
|
||||||
|
|
|
||||||
62
tasks/coverage/misc/fail/oxc-2394.ts
Normal file
62
tasks/coverage/misc/fail/oxc-2394.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
// copy from https://github.com/microsoft/TypeScript/blob/main/tests/cases/conformance/node/nodeModulesImportAttributesTypeModeDeclarationEmitErrors.ts#L72
|
||||||
|
|
||||||
|
// @filename: /node_modules/pkg/import.d.ts
|
||||||
|
export interface ImportInterface {}
|
||||||
|
|
||||||
|
// @filename: /node_modules/pkg/require.d.ts
|
||||||
|
export interface RequireInterface {}
|
||||||
|
|
||||||
|
// @filename: /index.ts
|
||||||
|
export type LocalInterface =
|
||||||
|
& import("pkg", { with: {"resolution-mode": "foobar"} }).RequireInterface
|
||||||
|
& import("pkg", { with: {"resolution-mode": "import"} }).ImportInterface;
|
||||||
|
|
||||||
|
export const a = (null as any as import("pkg", { with: {"resolution-mode": "foobar"} }).RequireInterface);
|
||||||
|
export const b = (null as any as import("pkg", { with: {"resolution-mode": "import"} }).ImportInterface);
|
||||||
|
|
||||||
|
// @filename: /other.ts
|
||||||
|
// missing with:
|
||||||
|
export type LocalInterface =
|
||||||
|
& import("pkg", {"resolution-mode": "require"}).RequireInterface
|
||||||
|
& import("pkg", {"resolution-mode": "import"}).ImportInterface;
|
||||||
|
|
||||||
|
export const a = (null as any as import("pkg", {"resolution-mode": "require"}).RequireInterface);
|
||||||
|
export const b = (null as any as import("pkg", {"resolution-mode": "import"}).ImportInterface);
|
||||||
|
|
||||||
|
// @filename: /other2.ts
|
||||||
|
// wrong attribute key
|
||||||
|
export type LocalInterface =
|
||||||
|
& import("pkg", { with: {"bad": "require"} }).RequireInterface
|
||||||
|
& import("pkg", { with: {"bad": "import"} }).ImportInterface;
|
||||||
|
|
||||||
|
export const a = (null as any as import("pkg", { with: {"bad": "require"} }).RequireInterface);
|
||||||
|
export const b = (null as any as import("pkg", { with: {"bad": "import"} }).ImportInterface);
|
||||||
|
|
||||||
|
// @filename: /other3.ts
|
||||||
|
// Array instead of object-y thing
|
||||||
|
export type LocalInterface =
|
||||||
|
& import("pkg", [ {"resolution-mode": "require"} ]).RequireInterface
|
||||||
|
& import("pkg", [ {"resolution-mode": "import"} ]).ImportInterface;
|
||||||
|
|
||||||
|
export const a = (null as any as import("pkg", [ {"resolution-mode": "require"} ]).RequireInterface);
|
||||||
|
export const b = (null as any as import("pkg", [ {"resolution-mode": "import"} ]).ImportInterface);
|
||||||
|
|
||||||
|
// @filename: /other4.ts
|
||||||
|
// Indirected attribute objecty-thing - not allowed
|
||||||
|
type Attribute1 = { with: {"resolution-mode": "require"} };
|
||||||
|
type Attribute2 = { with: {"resolution-mode": "import"} };
|
||||||
|
|
||||||
|
export type LocalInterface =
|
||||||
|
& import("pkg", Attribute1).RequireInterface
|
||||||
|
& import("pkg", Attribute2).ImportInterface;
|
||||||
|
|
||||||
|
export const a = (null as any as import("pkg", Attribute1).RequireInterface);
|
||||||
|
export const b = (null as any as import("pkg", Attribute2).ImportInterface);
|
||||||
|
|
||||||
|
// @filename: /other5.ts
|
||||||
|
export type LocalInterface =
|
||||||
|
& import("pkg", { with: {} }).RequireInterface
|
||||||
|
& import("pkg", { with: {} }).ImportInterface;
|
||||||
|
|
||||||
|
export const a = (null as any as import("pkg", { with: {} }).RequireInterface);
|
||||||
|
export const b = (null as any as import("pkg", { with: {} }).ImportInterface);
|
||||||
1
tasks/coverage/misc/pass/oxc-2394.ts
Normal file
1
tasks/coverage/misc/pass/oxc-2394.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
type A = import("foo", {with: {type: "json"}})
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
parser_misc Summary:
|
parser_misc Summary:
|
||||||
AST Parsed : 10/10 (100.00%)
|
AST Parsed : 11/11 (100.00%)
|
||||||
Positive Passed: 10/10 (100.00%)
|
Positive Passed: 11/11 (100.00%)
|
||||||
Negative Passed: 7/7 (100.00%)
|
Negative Passed: 8/8 (100.00%)
|
||||||
|
|
||||||
× Unexpected token
|
× Unexpected token
|
||||||
╭─[fail/oxc-169.js:1:1]
|
╭─[fail/oxc-169.js:1:1]
|
||||||
|
|
@ -90,6 +90,15 @@ Negative Passed: 7/7 (100.00%)
|
||||||
· ─────────
|
· ─────────
|
||||||
╰────
|
╰────
|
||||||
|
|
||||||
|
× Expected `with` but found `string`
|
||||||
|
╭─[fail/oxc-2394.ts:20:22]
|
||||||
|
19 │ export type LocalInterface =
|
||||||
|
20 │ & import("pkg", {"resolution-mode": "require"}).RequireInterface
|
||||||
|
· ────────┬────────
|
||||||
|
· ╰── `with` expected
|
||||||
|
21 │ & import("pkg", {"resolution-mode": "import"}).ImportInterface;
|
||||||
|
╰────
|
||||||
|
|
||||||
× The keyword 'let' is reserved
|
× The keyword 'let' is reserved
|
||||||
╭─[fail/oxc.js:1:1]
|
╭─[fail/oxc.js:1:1]
|
||||||
1 │ let.a = 1;
|
1 │ let.a = 1;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
prettier_misc Summary:
|
prettier_misc Summary:
|
||||||
AST Parsed : 10/10 (100.00%)
|
AST Parsed : 11/11 (100.00%)
|
||||||
Positive Passed: 6/10 (60.00%)
|
Positive Passed: 6/11 (54.55%)
|
||||||
Expect to Parse: "pass/oxc-1740.tsx"
|
Expect to Parse: "pass/oxc-1740.tsx"
|
||||||
Expect to Parse: "pass/oxc-2087.ts"
|
Expect to Parse: "pass/oxc-2087.ts"
|
||||||
|
Expect to Parse: "pass/oxc-2394.ts"
|
||||||
Expect to Parse: "pass/swc-1627.js"
|
Expect to Parse: "pass/swc-1627.js"
|
||||||
Expect to Parse: "pass/swc-8243.tsx"
|
Expect to Parse: "pass/swc-8243.tsx"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue