fix(parser): hashbang comment should not keep the end newline char (#5844)

Previously it included a newline in the value

```
  "hashbang": {
    "type": "Hashbang",
    "start": 0,
    "end": 16,
    "value": "/usr/bin/node\n"
  },
```

This change will also make the lexer emit a `\n` token, which will make comment position detection correct.
This commit is contained in:
Boshen 2024-09-18 05:41:44 +00:00
parent 65a17346cd
commit 42dcadfccf
6 changed files with 29 additions and 8 deletions

View file

@ -52,6 +52,7 @@ impl<'a> Gen for Hashbang<'a> {
fn gen(&self, p: &mut Codegen, _ctx: Context) {
p.print_str("#!");
p.print_str(self.value.as_str());
p.print_hard_newline();
}
}

View file

@ -281,6 +281,11 @@ impl<'a> Codegen<'a> {
}
}
#[inline]
fn print_hard_newline(&mut self) {
self.print_char(b'\n');
}
#[inline]
fn print_semicolon(&mut self) {
self.print_char(b';');

View file

@ -176,10 +176,11 @@ impl<'a> Lexer<'a> {
/// Section 12.5 Hashbang Comments
pub(super) fn read_hashbang_comment(&mut self) -> Kind {
while let Some(c) = self.next_char().as_ref() {
while let Some(c) = self.peek_char().as_ref() {
if is_line_terminator(*c) {
break;
}
self.consume_char();
}
self.token.is_on_new_line = true;
Kind::HashbangComment

View file

@ -178,7 +178,7 @@ token
kind: CommentKind::Block,
position: CommentPosition::Leading,
attached_to: 36,
preceded_by_newline: false, // hashbang comment always end in newline
preceded_by_newline: true,
followed_by_newline: true,
}];
assert_eq!(comments, expected);

View file

@ -523,6 +523,15 @@ mod test {
}
}
#[test]
fn hashbang() {
let allocator = Allocator::default();
let source_type = SourceType::default();
let source = "#!/usr/bin/node\n;";
let ret = Parser::new(&allocator, source, source_type).parse();
assert_eq!(ret.program.hashbang.unwrap().value.as_str(), "/usr/bin/node");
}
#[test]
fn unambiguous() {
let allocator = Allocator::default();

View file

@ -30,7 +30,7 @@ use cow_utils::CowUtils;
use oxc_allocator::{Box, Vec};
use oxc_ast::{ast::*, AstKind};
use oxc_span::GetSpan;
use oxc_syntax::identifier::is_identifier_name;
use oxc_syntax::identifier::{is_identifier_name, is_line_terminator};
use self::{array::Array, object::ObjectLike, template_literal::TemplateLiteralPrinter};
use crate::{
@ -60,10 +60,6 @@ impl<'a> Format<'a> for Program<'a> {
let mut parts = p.vec();
if let Some(hashbang) = &self.hashbang {
parts.push(hashbang.format(p));
let c = p.source_text[..hashbang.span.end as usize].chars().last().unwrap();
if p.is_next_line_empty_after_index(hashbang.span.end - c.len_utf8() as u32) {
parts.extend(hardline!());
}
}
if let Some(doc) = block::print_block_body(
p,
@ -81,7 +77,16 @@ impl<'a> Format<'a> for Program<'a> {
impl<'a> Format<'a> for Hashbang<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
Doc::Str(self.span.source_text(p.source_text))
let mut parts = p.vec();
parts.push(ss!(self.span.source_text(p.source_text)));
parts.extend(hardline!());
// Preserve original newline
if let Some(c) = p.source_text[self.span.end as usize..].chars().nth(1) {
if is_line_terminator(c) {
parts.extend(hardline!());
}
}
Doc::Array(parts)
}
}