diff --git a/crates/oxc_formatter/src/ast_nodes/iterator.rs b/crates/oxc_formatter/src/ast_nodes/iterator.rs index 6e5cad0a43573..009c772d9df29 100644 --- a/crates/oxc_formatter/src/ast_nodes/iterator.rs +++ b/crates/oxc_formatter/src/ast_nodes/iterator.rs @@ -160,7 +160,6 @@ impl_ast_node_vec!(ObjectPropertyKind<'a>); impl_ast_node_vec!(TemplateElement<'a>); impl_ast_node_vec!(Argument<'a>); impl_ast_node_vec!(AssignmentTargetProperty<'a>); -impl_ast_node_vec!(Directive<'a>); impl_ast_node_vec!(VariableDeclarator<'a>); impl_ast_node_vec!(SwitchCase<'a>); impl_ast_node_vec!(BindingProperty<'a>); @@ -276,3 +275,86 @@ impl<'a> IntoIterator for &AstNode<'a, Vec<'a, Statement<'a>>> { } } } + +fn get_following_span_for_directive_parent(parent: &AstNodes<'_>) -> Option { + match parent { + AstNodes::Program(program) => program.body().first().map(GetSpan::span), + AstNodes::FunctionBody(function_body) => { + function_body.statements().first().map(GetSpan::span) + } + AstNodes::TSModuleBlock(ts_module_block) => { + ts_module_block.body().first().map(GetSpan::span) + } + _ => None, + } +} + +// Manual implementation for Vec because we need to handle +// following_span for the last directive in Program.body. +impl<'a> AstNode<'a, Vec<'a, Directive<'a>>> { + pub fn iter(&self) -> AstNodeIterator<'a, Directive<'a>> { + AstNodeIterator { + inner: self.inner.iter().peekable(), + parent: self.parent, + allocator: self.allocator, + } + } + pub fn first(&self) -> Option<&'a AstNode<'a, Directive<'a>>> { + let mut inner_iter = self.inner.iter(); + self.allocator + .alloc(inner_iter.next().map(|inner| { + AstNode { + inner, + parent: self.parent, + allocator: self.allocator, + following_span: inner_iter + .next() + .map(GetSpan::span) + .or_else(|| get_following_span_for_directive_parent(self.parent)), + } + })) + .as_ref() + } + pub fn last(&self) -> Option<&'a AstNode<'a, Directive<'a>>> { + self.allocator + .alloc(self.inner.last().map(|inner| AstNode { + inner, + parent: self.parent, + allocator: self.allocator, + following_span: get_following_span_for_directive_parent(self.parent), + })) + .as_ref() + } +} +impl<'a> Iterator for AstNodeIterator<'a, Directive<'a>> { + type Item = &'a AstNode<'a, Directive<'a>>; + fn next(&mut self) -> Option { + let allocator = self.allocator; + allocator + .alloc(self.inner.next().map(|inner| { + AstNode { + parent: self.parent, + inner, + allocator, + following_span: self + .inner + .peek() + .copied() + .map(GetSpan::span) + .or_else(|| get_following_span_for_directive_parent(self.parent)), + } + })) + .as_ref() + } +} +impl<'a> IntoIterator for &AstNode<'a, Vec<'a, Directive<'a>>> { + type Item = &'a AstNode<'a, Directive<'a>>; + type IntoIter = AstNodeIterator<'a, Directive<'a>>; + fn into_iter(self) -> Self::IntoIter { + AstNodeIterator::> { + inner: self.inner.iter().peekable(), + parent: self.parent, + allocator: self.allocator, + } + } +} diff --git a/crates/oxc_formatter/tests/fixtures/ts/directives/issue-16192.ts b/crates/oxc_formatter/tests/fixtures/ts/directives/issue-16192.ts new file mode 100644 index 0000000000000..15d2ae6c3c84e --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/ts/directives/issue-16192.ts @@ -0,0 +1,12 @@ +/******/ 'use strict' /**/ +/******/ a; + +function func() { + /******/ 'use strict' // + /******/ b; +} + +namespace M { + /******/ 'use strict' + /******/ export const a = 1; +} \ No newline at end of file diff --git a/crates/oxc_formatter/tests/fixtures/ts/directives/issue-16192.ts.snap b/crates/oxc_formatter/tests/fixtures/ts/directives/issue-16192.ts.snap new file mode 100644 index 0000000000000..10e49d383c8cd --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/ts/directives/issue-16192.ts.snap @@ -0,0 +1,50 @@ +--- +source: crates/oxc_formatter/tests/fixtures/mod.rs +--- +==================== Input ==================== +/******/ 'use strict' /**/ +/******/ a; + +function func() { + /******/ 'use strict' // + /******/ b; +} + +namespace M { + /******/ 'use strict' + /******/ export const a = 1; +} +==================== Output ==================== +------------------ +{ printWidth: 80 } +------------------ +/******/ "use strict"; /**/ +/******/ a; + +function func() { + /******/ "use strict"; // + /******/ b; +} + +namespace M { + /******/ "use strict"; + /******/ export const a = 1; +} + +------------------- +{ printWidth: 100 } +------------------- +/******/ "use strict"; /**/ +/******/ a; + +function func() { + /******/ "use strict"; // + /******/ b; +} + +namespace M { + /******/ "use strict"; + /******/ export const a = 1; +} + +===================== End =====================