diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 7fb3a332..64291eb4 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -8047,6 +8047,15 @@ fn gen_node_with_separator<'a>(value: Node<'a>, generated_separator: PrintItems, PrintItems::new() }; + // comments that sit between the node and the comma on a subsequent line — + // the default trailing-comments path only emits same-line comments, so these + // would otherwise be dropped (issue #684). + let between_node_and_comma = if let Some(comma_token) = comma_token { + gen_comments_between_node_and_comma(value, comma_token, context) + } else { + PrintItems::new() + }; + // if the current node is ignored and already has a semi-colon, then skip adding a separator let is_ignored_with_semi_colon = value.text_fast(context.program).ends_with(';') && get_has_ignore_comment(&value.leading_comments_fast(context.program), value, context); @@ -8054,9 +8063,11 @@ fn gen_node_with_separator<'a>(value: Node<'a>, generated_separator: PrintItems, items.extend(gen_node(value, context)); } else { let generated_separator = generated_separator.into_rc_path(); + let between_node_and_comma = between_node_and_comma.into_rc_path(); items.extend(gen_node_with_inner_gen(value, context, move |mut items, _| { // this Rc clone is necessary because we can't move the captured generated_separator out of this closure items.push_optional_path(generated_separator); + items.push_optional_path(between_node_and_comma); items })); } @@ -8073,6 +8084,38 @@ fn gen_node_with_separator<'a>(value: Node<'a>, generated_separator: PrintItems, } } +fn gen_comments_between_node_and_comma<'a>(value: Node<'a>, comma_token: &TokenAndSpan, context: &mut Context<'a>) -> PrintItems { + // collect comments between the node end and the comma — these would be classified + // as trailing comments of the node, but the existing path only emits ones on the + // same line, dropping anything that sits on a subsequent line. + let node_end = value.range().end; + let node_end_line = node_end.end_line_fast(context.program); + let mut comments_to_emit = Vec::new(); + for comment in comma_token.range().start.leading_comments_fast(context.program) { + if context.has_handled_comment(&comment) { + continue; + } + if comment.start() < node_end { + continue; + } + // skip same-line trailing comments — those flow through the regular trailing path + if comment.start_line_fast(context.program) <= node_end_line { + continue; + } + comments_to_emit.push(comment); + } + + if comments_to_emit.is_empty() { + return PrintItems::new(); + } + + let comma_range = comma_token.range(); + let mut items = PrintItems::new(); + items.push_signal(Signal::NewLine); + items.extend(gen_comment_collection(comments_to_emit.into_iter(), None, Some(&comma_range), context)); + items +} + /// Some nodes don't have a TsTypeAnn, but instead a Box fn gen_type_ann_with_colon_if_exists_for_type<'a>(type_ann: Option>, context: &mut Context<'a>) -> PrintItems { if let Some(type_ann) = type_ann { diff --git a/tests/specs/issues/issue0684.txt b/tests/specs/issues/issue0684.txt new file mode 100644 index 00000000..b91111c9 --- /dev/null +++ b/tests/specs/issues/issue0684.txt @@ -0,0 +1,71 @@ +== should preserve comment between argument and following comma (issue #684) == +const x = useMemo(() => + doSomething() + // eslint-disable-next-line react-hooks/exhaustive-deps + , [dep]); + +[expect] +const x = useMemo( + () => doSomething(), + // eslint-disable-next-line react-hooks/exhaustive-deps + [dep], +); + +== should preserve multiple comments between argument and comma == +foo( + a + // c1 + /* c2 */ + // c3 + , b); + +[expect] +foo( + a, + // c1 + /* c2 */ + // c3 + b, +); + +== should preserve comment before comma in array literal == +[ + 1 + // c + , 2 +]; + +[expect] +[ + 1, + // c + 2, +]; + +== should preserve comment before comma in object literal == +({ + a: 1 + // c + , b: 2 +}); + +[expect] +({ + a: 1, + // c + b: 2, +}); + +== should preserve comment before comma in function parameters == +function f( + a + // c + , b +) {} + +[expect] +function f( + a, + // c + b, +) {}