mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 20:28:58 +00:00
parent
445603444f
commit
cfcef241db
10 changed files with 141 additions and 144 deletions
|
|
@ -1004,6 +1004,7 @@ pub enum TSModuleDeclarationBody<'a> {
|
|||
pub struct TSModuleBlock<'a> {
|
||||
#[cfg_attr(feature = "serialize", serde(flatten))]
|
||||
pub span: Span,
|
||||
pub directives: Vec<'a, Directive<'a>>,
|
||||
pub body: Vec<'a, Statement<'a>>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1761,9 +1761,10 @@ impl<'a> AstBuilder<'a> {
|
|||
pub fn ts_module_block(
|
||||
self,
|
||||
span: Span,
|
||||
directives: Vec<'a, Directive<'a>>,
|
||||
body: Vec<'a, Statement<'a>>,
|
||||
) -> Box<'a, TSModuleBlock<'a>> {
|
||||
self.alloc(TSModuleBlock { span, body })
|
||||
self.alloc(TSModuleBlock { span, directives, body })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -51,19 +51,10 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for Program<'a> {
|
|||
if let Some(hashbang) = &self.hashbang {
|
||||
hashbang.gen(p, ctx);
|
||||
}
|
||||
print_directives_and_statements(p, &self.directives, &self.body, ctx);
|
||||
p.print_directives_and_statements(Some(&self.directives), &self.body, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn print_directives_and_statements<const MINIFY: bool>(
|
||||
p: &mut Codegen<{ MINIFY }>,
|
||||
directives: &[Directive],
|
||||
statements: &[Statement<'_>],
|
||||
ctx: Context,
|
||||
) {
|
||||
p.print_directives_and_statements(Some(directives), statements, ctx);
|
||||
}
|
||||
|
||||
impl<'a, const MINIFY: bool> Gen<MINIFY> for Hashbang<'a> {
|
||||
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
|
||||
p.print_str(b"#!");
|
||||
|
|
@ -3300,11 +3291,9 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for TSModuleDeclarationName<'a> {
|
|||
|
||||
impl<'a, const MINIFY: bool> Gen<MINIFY> for TSModuleBlock<'a> {
|
||||
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
|
||||
p.print_curly_braces(self.span, self.body.is_empty(), |p| {
|
||||
for item in &self.body {
|
||||
p.print_semicolon_if_needed();
|
||||
item.gen(p, ctx);
|
||||
}
|
||||
let is_empty = self.directives.is_empty() && self.body.is_empty();
|
||||
p.print_curly_braces(self.span, is_empty, |p| {
|
||||
p.print_directives_and_statements(Some(&self.directives), &self.body, ctx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
|||
self.scope.enter_scope(ScopeFlags::TsModuleBlock);
|
||||
let stmts = self.transform_statements_on_demand(&block.body);
|
||||
self.scope.leave_scope();
|
||||
self.ast.ts_module_block(SPAN, stmts)
|
||||
self.ast.ts_module_block(SPAN, self.ast.new_vec(), stmts)
|
||||
}
|
||||
|
||||
pub fn transform_ts_module_declaration(
|
||||
|
|
|
|||
|
|
@ -37,52 +37,30 @@ impl<'a> ParserImpl<'a> {
|
|||
|
||||
let mut expecting_directives = true;
|
||||
while !self.at(Kind::Eof) {
|
||||
match self.cur_kind() {
|
||||
Kind::RCurly if !is_top_level => break,
|
||||
Kind::Import if !matches!(self.peek_kind(), Kind::Dot | Kind::LParen) => {
|
||||
let stmt = self.parse_import_declaration()?;
|
||||
statements.push(stmt);
|
||||
expecting_directives = false;
|
||||
}
|
||||
Kind::Export => {
|
||||
let stmt = self.parse_export_declaration()?;
|
||||
statements.push(stmt);
|
||||
expecting_directives = false;
|
||||
}
|
||||
Kind::At => {
|
||||
self.eat_decorators()?;
|
||||
expecting_directives = false;
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
let stmt = self.parse_statement_list_item(StatementContext::StatementList)?;
|
||||
|
||||
// Section 11.2.1 Directive Prologue
|
||||
// The only way to get a correct directive is to parse the statement first and check if it is a string literal.
|
||||
// All other method are flawed, see test cases in [babel](https://github.com/babel/babel/blob/main/packages/babel-parser/test/fixtures/core/categorized/not-directive/input.js)
|
||||
if expecting_directives {
|
||||
if let Statement::ExpressionStatement(expr) = &stmt {
|
||||
if let Expression::StringLiteral(string) = &expr.expression {
|
||||
// span start will mismatch if they are parenthesized when `preserve_parens = false`
|
||||
if expr.span.start == string.span.start {
|
||||
let src = &self.source_text[string.span.start as usize + 1
|
||||
..string.span.end as usize - 1];
|
||||
let directive = self.ast.directive(
|
||||
expr.span,
|
||||
(*string).clone(),
|
||||
Atom::from(src),
|
||||
);
|
||||
directives.push(directive);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if !is_top_level && self.at(Kind::RCurly) {
|
||||
break;
|
||||
}
|
||||
let stmt = self.parse_statement_list_item(StatementContext::StatementList)?;
|
||||
// Section 11.2.1 Directive Prologue
|
||||
// The only way to get a correct directive is to parse the statement first and check if it is a string literal.
|
||||
// All other method are flawed, see test cases in [babel](https://github.com/babel/babel/blob/main/packages/babel-parser/test/fixtures/core/categorized/not-directive/input.js)
|
||||
if expecting_directives {
|
||||
if let Statement::ExpressionStatement(expr) = &stmt {
|
||||
if let Expression::StringLiteral(string) = &expr.expression {
|
||||
// span start will mismatch if they are parenthesized when `preserve_parens = false`
|
||||
if expr.span.start == string.span.start {
|
||||
let src = &self.source_text
|
||||
[string.span.start as usize + 1..string.span.end as usize - 1];
|
||||
let directive =
|
||||
self.ast.directive(expr.span, (*string).clone(), Atom::from(src));
|
||||
directives.push(directive);
|
||||
continue;
|
||||
}
|
||||
expecting_directives = false;
|
||||
}
|
||||
|
||||
statements.push(stmt);
|
||||
}
|
||||
};
|
||||
expecting_directives = false;
|
||||
}
|
||||
statements.push(stmt);
|
||||
}
|
||||
|
||||
Ok((directives, statements))
|
||||
|
|
|
|||
|
|
@ -492,7 +492,7 @@ mod test {
|
|||
let sources = [
|
||||
("import x from 'foo'; 'use strict';", 2),
|
||||
("export {x} from 'foo'; 'use strict';", 2),
|
||||
("@decorator 'use strict';", 1),
|
||||
(";'use strict';", 2),
|
||||
];
|
||||
for (source, body_length) in sources {
|
||||
let ret = Parser::new(&allocator, source, source_type).parse();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
js::{FunctionKind, VariableDeclarationContext, VariableDeclarationParent},
|
||||
lexer::Kind,
|
||||
list::{NormalList, SeparatedList},
|
||||
ParserImpl, StatementContext,
|
||||
ParserImpl,
|
||||
};
|
||||
|
||||
impl<'a> ParserImpl<'a> {
|
||||
|
|
@ -217,21 +217,11 @@ impl<'a> ParserImpl<'a> {
|
|||
|
||||
fn parse_ts_module_block(&mut self) -> Result<Box<'a, TSModuleBlock<'a>>> {
|
||||
let span = self.start_span();
|
||||
|
||||
let mut statements = self.ast.new_vec();
|
||||
|
||||
self.expect(Kind::LCurly)?;
|
||||
|
||||
while !self.eat(Kind::RCurly) && !self.at(Kind::Eof) {
|
||||
let stmt = self.parse_ts_module_item()?;
|
||||
statements.push(stmt);
|
||||
}
|
||||
|
||||
Ok(self.ast.ts_module_block(self.end_span(span), statements))
|
||||
}
|
||||
|
||||
fn parse_ts_module_item(&mut self) -> Result<Statement<'a>> {
|
||||
self.parse_statement_list_item(StatementContext::StatementList)
|
||||
let (directives, statements) =
|
||||
self.parse_directives_and_statements(/* is_top_level */ false)?;
|
||||
self.expect(Kind::RCurly)?;
|
||||
Ok(self.ast.ts_module_block(self.end_span(span), directives, statements))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_ts_namespace_or_module_declaration_body(
|
||||
|
|
|
|||
|
|
@ -141,8 +141,15 @@ impl<'a> TypeScript<'a> {
|
|||
let symbol_id = ctx.generate_uid(&real_name, scope_id, SymbolFlags::FunctionScopedVariable);
|
||||
let name = self.ctx.ast.new_atom(ctx.symbols().get_name(symbol_id));
|
||||
|
||||
let namespace_top_level = match body {
|
||||
TSModuleDeclarationBody::TSModuleBlock(block) => block.unbox().body,
|
||||
let directives;
|
||||
let namespace_top_level;
|
||||
|
||||
match body {
|
||||
TSModuleDeclarationBody::TSModuleBlock(block) => {
|
||||
let block = block.unbox();
|
||||
directives = block.directives;
|
||||
namespace_top_level = block.body;
|
||||
}
|
||||
// We handle `namespace X.Y {}` as if it was
|
||||
// namespace X {
|
||||
// export namespace Y {}
|
||||
|
|
@ -152,9 +159,10 @@ impl<'a> TypeScript<'a> {
|
|||
let export_named_decl =
|
||||
self.ctx.ast.plain_export_named_declaration_declaration(SPAN, declaration);
|
||||
let stmt = Statement::ExportNamedDeclaration(export_named_decl);
|
||||
self.ctx.ast.new_vec_single(stmt)
|
||||
directives = self.ctx.ast.new_vec();
|
||||
namespace_top_level = self.ctx.ast.new_vec_single(stmt);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut new_stmts = self.ctx.ast.new_vec();
|
||||
|
||||
|
|
@ -256,7 +264,15 @@ impl<'a> TypeScript<'a> {
|
|||
return None;
|
||||
}
|
||||
|
||||
Some(self.transform_namespace(name, real_name, new_stmts, parent_export, scope_id, ctx))
|
||||
Some(self.transform_namespace(
|
||||
name,
|
||||
real_name,
|
||||
new_stmts,
|
||||
directives,
|
||||
parent_export,
|
||||
scope_id,
|
||||
ctx,
|
||||
))
|
||||
}
|
||||
|
||||
// `namespace Foo { }` -> `let Foo; (function (_Foo) { })(Foo || (Foo = {}));`
|
||||
|
|
@ -280,35 +296,17 @@ impl<'a> TypeScript<'a> {
|
|||
|
||||
// `namespace Foo { }` -> `let Foo; (function (_Foo) { })(Foo || (Foo = {}));`
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
#[allow(clippy::needless_pass_by_value, clippy::too_many_arguments)]
|
||||
fn transform_namespace(
|
||||
&self,
|
||||
arg_name: Atom<'a>,
|
||||
real_name: Atom<'a>,
|
||||
mut stmts: Vec<'a, Statement<'a>>,
|
||||
stmts: Vec<'a, Statement<'a>>,
|
||||
directives: Vec<'a, Directive<'a>>,
|
||||
parent_export: Option<Expression<'a>>,
|
||||
scope_id: ScopeId,
|
||||
ctx: &mut TraverseCtx,
|
||||
) -> Statement<'a> {
|
||||
let mut directives = self.ctx.ast.new_vec();
|
||||
|
||||
// Check if the namespace has a `use strict` directive
|
||||
if stmts.first().is_some_and(|stmt| {
|
||||
matches!(stmt, Statement::ExpressionStatement(es) if
|
||||
matches!(&es.expression, Expression::StringLiteral(literal) if
|
||||
literal.value == "use strict")
|
||||
)
|
||||
}) {
|
||||
stmts.remove(0);
|
||||
let directive = self.ctx.ast.new_atom("use strict");
|
||||
let directive = Directive {
|
||||
span: SPAN,
|
||||
expression: StringLiteral::new(SPAN, directive.clone()),
|
||||
directive,
|
||||
};
|
||||
directives.push(directive);
|
||||
}
|
||||
|
||||
// `(function (_N) { var x; })(N || (N = {}))`;
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
let callee = {
|
||||
|
|
|
|||
|
|
@ -281,46 +281,47 @@ pub(crate) enum AncestorType {
|
|||
TSTypePredicateTypeAnnotation = 249,
|
||||
TSModuleDeclarationId = 250,
|
||||
TSModuleDeclarationBody = 251,
|
||||
TSModuleBlockBody = 252,
|
||||
TSTypeLiteralMembers = 253,
|
||||
TSInferTypeTypeParameter = 254,
|
||||
TSTypeQueryExprName = 255,
|
||||
TSTypeQueryTypeParameters = 256,
|
||||
TSImportTypeArgument = 257,
|
||||
TSImportTypeQualifier = 258,
|
||||
TSImportTypeAttributes = 259,
|
||||
TSImportTypeTypeParameters = 260,
|
||||
TSImportAttributesElements = 261,
|
||||
TSImportAttributeName = 262,
|
||||
TSImportAttributeValue = 263,
|
||||
TSFunctionTypeThisParam = 264,
|
||||
TSFunctionTypeParams = 265,
|
||||
TSFunctionTypeReturnType = 266,
|
||||
TSFunctionTypeTypeParameters = 267,
|
||||
TSConstructorTypeParams = 268,
|
||||
TSConstructorTypeReturnType = 269,
|
||||
TSConstructorTypeTypeParameters = 270,
|
||||
TSMappedTypeTypeParameter = 271,
|
||||
TSMappedTypeNameType = 272,
|
||||
TSMappedTypeTypeAnnotation = 273,
|
||||
TSTemplateLiteralTypeQuasis = 274,
|
||||
TSTemplateLiteralTypeTypes = 275,
|
||||
TSAsExpressionExpression = 276,
|
||||
TSAsExpressionTypeAnnotation = 277,
|
||||
TSSatisfiesExpressionExpression = 278,
|
||||
TSSatisfiesExpressionTypeAnnotation = 279,
|
||||
TSTypeAssertionExpression = 280,
|
||||
TSTypeAssertionTypeAnnotation = 281,
|
||||
TSImportEqualsDeclarationId = 282,
|
||||
TSImportEqualsDeclarationModuleReference = 283,
|
||||
TSExternalModuleReferenceExpression = 284,
|
||||
TSNonNullExpressionExpression = 285,
|
||||
DecoratorExpression = 286,
|
||||
TSExportAssignmentExpression = 287,
|
||||
TSNamespaceExportDeclarationId = 288,
|
||||
TSInstantiationExpressionExpression = 289,
|
||||
TSInstantiationExpressionTypeParameters = 290,
|
||||
JSDocNullableTypeTypeAnnotation = 291,
|
||||
TSModuleBlockDirectives = 252,
|
||||
TSModuleBlockBody = 253,
|
||||
TSTypeLiteralMembers = 254,
|
||||
TSInferTypeTypeParameter = 255,
|
||||
TSTypeQueryExprName = 256,
|
||||
TSTypeQueryTypeParameters = 257,
|
||||
TSImportTypeArgument = 258,
|
||||
TSImportTypeQualifier = 259,
|
||||
TSImportTypeAttributes = 260,
|
||||
TSImportTypeTypeParameters = 261,
|
||||
TSImportAttributesElements = 262,
|
||||
TSImportAttributeName = 263,
|
||||
TSImportAttributeValue = 264,
|
||||
TSFunctionTypeThisParam = 265,
|
||||
TSFunctionTypeParams = 266,
|
||||
TSFunctionTypeReturnType = 267,
|
||||
TSFunctionTypeTypeParameters = 268,
|
||||
TSConstructorTypeParams = 269,
|
||||
TSConstructorTypeReturnType = 270,
|
||||
TSConstructorTypeTypeParameters = 271,
|
||||
TSMappedTypeTypeParameter = 272,
|
||||
TSMappedTypeNameType = 273,
|
||||
TSMappedTypeTypeAnnotation = 274,
|
||||
TSTemplateLiteralTypeQuasis = 275,
|
||||
TSTemplateLiteralTypeTypes = 276,
|
||||
TSAsExpressionExpression = 277,
|
||||
TSAsExpressionTypeAnnotation = 278,
|
||||
TSSatisfiesExpressionExpression = 279,
|
||||
TSSatisfiesExpressionTypeAnnotation = 280,
|
||||
TSTypeAssertionExpression = 281,
|
||||
TSTypeAssertionTypeAnnotation = 282,
|
||||
TSImportEqualsDeclarationId = 283,
|
||||
TSImportEqualsDeclarationModuleReference = 284,
|
||||
TSExternalModuleReferenceExpression = 285,
|
||||
TSNonNullExpressionExpression = 286,
|
||||
DecoratorExpression = 287,
|
||||
TSExportAssignmentExpression = 288,
|
||||
TSNamespaceExportDeclarationId = 289,
|
||||
TSInstantiationExpressionExpression = 290,
|
||||
TSInstantiationExpressionTypeParameters = 291,
|
||||
JSDocNullableTypeTypeAnnotation = 292,
|
||||
}
|
||||
|
||||
/// Ancestor type used in AST traversal.
|
||||
|
|
@ -781,6 +782,8 @@ pub enum Ancestor<'a> {
|
|||
AncestorType::TSModuleDeclarationId as u16,
|
||||
TSModuleDeclarationBody(TSModuleDeclarationWithoutBody<'a>) =
|
||||
AncestorType::TSModuleDeclarationBody as u16,
|
||||
TSModuleBlockDirectives(TSModuleBlockWithoutDirectives<'a>) =
|
||||
AncestorType::TSModuleBlockDirectives as u16,
|
||||
TSModuleBlockBody(TSModuleBlockWithoutBody<'a>) = AncestorType::TSModuleBlockBody as u16,
|
||||
TSTypeLiteralMembers(TSTypeLiteralWithoutMembers<'a>) =
|
||||
AncestorType::TSTypeLiteralMembers as u16,
|
||||
|
|
@ -1681,7 +1684,7 @@ impl<'a> Ancestor<'a> {
|
|||
|
||||
#[inline]
|
||||
pub fn is_ts_module_block(&self) -> bool {
|
||||
matches!(self, Self::TSModuleBlockBody(_))
|
||||
matches!(self, Self::TSModuleBlockDirectives(_) | Self::TSModuleBlockBody(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -10752,8 +10755,28 @@ impl<'a> TSModuleDeclarationWithoutBody<'a> {
|
|||
}
|
||||
|
||||
pub(crate) const OFFSET_TS_MODULE_BLOCK_SPAN: usize = offset_of!(TSModuleBlock, span);
|
||||
pub(crate) const OFFSET_TS_MODULE_BLOCK_DIRECTIVES: usize = offset_of!(TSModuleBlock, directives);
|
||||
pub(crate) const OFFSET_TS_MODULE_BLOCK_BODY: usize = offset_of!(TSModuleBlock, body);
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct TSModuleBlockWithoutDirectives<'a>(pub(crate) *const TSModuleBlock<'a>);
|
||||
|
||||
impl<'a> TSModuleBlockWithoutDirectives<'a> {
|
||||
#[inline]
|
||||
pub fn span(&self) -> &Span {
|
||||
unsafe { &*((self.0 as *const u8).add(OFFSET_TS_MODULE_BLOCK_SPAN) as *const Span) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn body(&self) -> &Vec<'a, Statement<'a>> {
|
||||
unsafe {
|
||||
&*((self.0 as *const u8).add(OFFSET_TS_MODULE_BLOCK_BODY)
|
||||
as *const Vec<'a, Statement<'a>>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct TSModuleBlockWithoutBody<'a>(pub(crate) *const TSModuleBlock<'a>);
|
||||
|
|
@ -10763,6 +10786,14 @@ impl<'a> TSModuleBlockWithoutBody<'a> {
|
|||
pub fn span(&self) -> &Span {
|
||||
unsafe { &*((self.0 as *const u8).add(OFFSET_TS_MODULE_BLOCK_SPAN) as *const Span) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn directives(&self) -> &Vec<'a, Directive<'a>> {
|
||||
unsafe {
|
||||
&*((self.0 as *const u8).add(OFFSET_TS_MODULE_BLOCK_DIRECTIVES)
|
||||
as *const Vec<'a, Directive<'a>>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const OFFSET_TS_TYPE_LITERAL_SPAN: usize = offset_of!(TSTypeLiteral, span);
|
||||
|
|
|
|||
|
|
@ -4903,7 +4903,16 @@ pub(crate) unsafe fn walk_ts_module_block<'a, Tr: Traverse<'a>>(
|
|||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
traverser.enter_ts_module_block(&mut *node, ctx);
|
||||
ctx.push_stack(Ancestor::TSModuleBlockBody(ancestor::TSModuleBlockWithoutBody(node)));
|
||||
ctx.push_stack(Ancestor::TSModuleBlockDirectives(ancestor::TSModuleBlockWithoutDirectives(
|
||||
node,
|
||||
)));
|
||||
for item in (*((node as *mut u8).add(ancestor::OFFSET_TS_MODULE_BLOCK_DIRECTIVES)
|
||||
as *mut Vec<Directive>))
|
||||
.iter_mut()
|
||||
{
|
||||
walk_directive(traverser, item as *mut _, ctx);
|
||||
}
|
||||
ctx.retag_stack(AncestorType::TSModuleBlockBody);
|
||||
walk_statements(
|
||||
traverser,
|
||||
(node as *mut u8).add(ancestor::OFFSET_TS_MODULE_BLOCK_BODY) as *mut Vec<Statement>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue