Skip to content

Commit 6b37d15

Browse files
Attempted fix for JSX comment duplication but needs refinement
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
1 parent d5803f1 commit 6b37d15

2 files changed

Lines changed: 68 additions & 11 deletions

File tree

.github/copilot-questions.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
Questions I have that I think the developers of this project can help me with:
2-
* How does control flow analysis represent a circular graph? I checked the documentation server for "cfa" and "control flow"
3-
* How do I tell if a symbol is in the global scope? I checked the documentation server for topics referencing "symbol" and "global"
4-
* What is an `EscapedName`, exactly?
1+
Questions I have that I think the developers of this project can help me with:
2+
* How does control flow analysis represent a circular graph? I checked the documentation server for "cfa" and "control flow"
3+
* How do I tell if a symbol is in the global scope? I checked the documentation server for topics referencing "symbol" and "global"
4+
* What is an `EscapedName`, exactly?
5+
* How can I distinguish between trivia comments and JSX text content containing comments to prevent JSX comment duplication? I searched the documentation for "jsx" and "comment" but need to understand the emission order and when JSX text content vs trivia should take precedence.

src/compiler/emitter.ts

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,12 @@ import {
213213
isInJsonFile,
214214
isJSDocLikeText,
215215
isJsonSourceFile,
216-
isJsxClosingElement,
217-
isJsxNamespacedName,
218-
isJsxOpeningElement,
216+
isJsxClosingElement,
217+
isJsxElement,
218+
isJsxFragment,
219+
isJsxNamespacedName,
220+
isJsxOpeningElement,
221+
isJsxText,
219222
isKeyword,
220223
isLet,
221224
isLiteralExpression,
@@ -3176,7 +3179,12 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
31763179
if (needsIndent) {
31773180
increaseIndent();
31783181
}
3179-
emitLeadingCommentsOfPosition(startPos);
3182+
const isJsxExprContext = contextNode.kind === SyntaxKind.JsxExpression;
3183+
// Skip emitting leading comments for JSX expressions when emitting JSX children
3184+
// to prevent duplication with JSX text content
3185+
if (!(isJsxExprContext && isEmittingJsxChildren)) {
3186+
emitLeadingCommentsOfPosition(startPos);
3187+
}
31803188
if (needsIndent) {
31813189
decreaseIndent();
31823190
}
@@ -3195,13 +3203,61 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
31953203

31963204
if (isSimilarNode && contextNode.end !== pos) {
31973205
const isJsxExprContext = contextNode.kind === SyntaxKind.JsxExpression;
3198-
emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ !isJsxExprContext, /*forceNoNewline*/ isJsxExprContext);
3206+
// Skip emitting trailing comments for JSX expressions when emitting JSX children
3207+
// to prevent duplication with JSX text content
3208+
if (!(isJsxExprContext && isEmittingJsxChildren)) {
3209+
emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ !isJsxExprContext, /*forceNoNewline*/ isJsxExprContext);
3210+
}
31993211
}
32003212
return pos;
32013213
}
32023214

3203-
function commentWillEmitNewLine(node: CommentRange) {
3204-
return node.kind === SyntaxKind.SingleLineCommentTrivia || !!node.hasTrailingNewLine;
3215+
function commentWillEmitNewLine(node: CommentRange) {
3216+
return node.kind === SyntaxKind.SingleLineCommentTrivia || !!node.hasTrailingNewLine;
3217+
}
3218+
3219+
function jsxExpressionCommentsOverlapWithJsxText(jsxExpr: JsxExpression): boolean {
3220+
if (!isEmittingJsxChildren || !currentSourceFile) return false;
3221+
3222+
// Check if this JSX expression is a child of a JSX element
3223+
const parent = jsxExpr.parent;
3224+
if (!parent || (!isJsxElement(parent) && !isJsxFragment(parent))) {
3225+
return false;
3226+
}
3227+
3228+
const children = parent.children;
3229+
const exprIndex = children.indexOf(jsxExpr);
3230+
if (exprIndex === -1) return false;
3231+
3232+
// Check trailing comments that might overlap with next JSX text
3233+
const trailingComments = getTrailingCommentRanges(currentSourceFile.text, jsxExpr.end);
3234+
if (trailingComments && exprIndex + 1 < children.length) {
3235+
const nextChild = children[exprIndex + 1];
3236+
if (isJsxText(nextChild)) {
3237+
// Check if any trailing comment overlaps with the next JSX text node
3238+
for (const comment of trailingComments) {
3239+
if (comment.pos >= nextChild.pos && comment.end <= nextChild.end) {
3240+
return true;
3241+
}
3242+
}
3243+
}
3244+
}
3245+
3246+
// Check leading comments that might overlap with previous JSX text
3247+
const leadingComments = getLeadingCommentRanges(currentSourceFile.text, jsxExpr.pos);
3248+
if (leadingComments && exprIndex > 0) {
3249+
const prevChild = children[exprIndex - 1];
3250+
if (isJsxText(prevChild)) {
3251+
// Check if any leading comment overlaps with the previous JSX text node
3252+
for (const comment of leadingComments) {
3253+
if (comment.pos >= prevChild.pos && comment.end <= prevChild.end) {
3254+
return true;
3255+
}
3256+
}
3257+
}
3258+
}
3259+
3260+
return false;
32053261
}
32063262

32073263
function willEmitLeadingNewLine(node: Expression): boolean {

0 commit comments

Comments
 (0)