mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +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 argument: TSType<'a>,
|
||||
pub qualifier: Option<TSTypeName<'a>>,
|
||||
pub attributes: Option<TSImportAttributes<'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)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "camelCase"))]
|
||||
#[cfg_attr(all(feature = "serde", feature = "wasm"), derive(tsify::Tsify))]
|
||||
|
|
|
|||
|
|
@ -1652,6 +1652,7 @@ impl<'a> AstBuilder<'a> {
|
|||
is_type_of: bool,
|
||||
argument: TSType<'a>,
|
||||
qualifier: Option<TSTypeName<'a>>,
|
||||
attributes: Option<TSImportAttributes<'a>>,
|
||||
type_parameters: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
|
||||
) -> TSType<'a> {
|
||||
TSType::TSImportType(self.alloc(TSImportType {
|
||||
|
|
@ -1659,6 +1660,7 @@ impl<'a> AstBuilder<'a> {
|
|||
is_type_of,
|
||||
argument,
|
||||
qualifier,
|
||||
attributes,
|
||||
type_parameters,
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,3 +159,35 @@ impl<'a> SeparatedList<'a> for TSTypeArgumentList<'a> {
|
|||
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},
|
||||
lexer::Kind,
|
||||
list::{NormalList, SeparatedList},
|
||||
ts::list::TSImportAttributeList,
|
||||
Context, ParserImpl,
|
||||
};
|
||||
|
||||
|
|
@ -763,6 +764,8 @@ impl<'a> ParserImpl<'a> {
|
|||
self.expect(Kind::Import)?;
|
||||
self.expect(Kind::LParen)?;
|
||||
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)?;
|
||||
|
||||
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,
|
||||
argument,
|
||||
qualifier,
|
||||
attributes,
|
||||
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>> {
|
||||
let span = self.start_span();
|
||||
let r#abstract = self.eat(Kind::Abstract);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
codegen_misc Summary:
|
||||
AST Parsed : 10/10 (100.00%)
|
||||
Positive Passed: 10/10 (100.00%)
|
||||
AST Parsed : 11/11 (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:
|
||||
AST Parsed : 10/10 (100.00%)
|
||||
Positive Passed: 10/10 (100.00%)
|
||||
Negative Passed: 7/7 (100.00%)
|
||||
AST Parsed : 11/11 (100.00%)
|
||||
Positive Passed: 11/11 (100.00%)
|
||||
Negative Passed: 8/8 (100.00%)
|
||||
|
||||
× Unexpected token
|
||||
╭─[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
|
||||
╭─[fail/oxc.js:1:1]
|
||||
1 │ let.a = 1;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
prettier_misc Summary:
|
||||
AST Parsed : 10/10 (100.00%)
|
||||
Positive Passed: 6/10 (60.00%)
|
||||
AST Parsed : 11/11 (100.00%)
|
||||
Positive Passed: 6/11 (54.55%)
|
||||
Expect to Parse: "pass/oxc-1740.tsx"
|
||||
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-8243.tsx"
|
||||
|
|
|
|||
Loading…
Reference in a new issue