From fbaed54eadd65346edbac63494e1b9ffae5c73af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 04:36:59 +0000 Subject: [PATCH 1/8] Initial plan From f4f38c0172f5ce226f9e27f8d0faefa43b41c8d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 04:42:06 +0000 Subject: [PATCH 2/8] Initial investigation of JSX comment duplication issue Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- tests/cases/compiler/jsxCommentDuplication.tsx | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/cases/compiler/jsxCommentDuplication.tsx diff --git a/tests/cases/compiler/jsxCommentDuplication.tsx b/tests/cases/compiler/jsxCommentDuplication.tsx new file mode 100644 index 0000000000000..fe13875536038 --- /dev/null +++ b/tests/cases/compiler/jsxCommentDuplication.tsx @@ -0,0 +1,3 @@ +// @jsx: preserve +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; \ No newline at end of file From 2aa5757d064c8e62e31a9aa714beb80ba95607ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 04:53:12 +0000 Subject: [PATCH 3/8] Fix attempt for JSX comment duplication - not working yet Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 61 +++++++++++++++---- .../compiler/jsxCommentDuplicationDebug.tsx | 3 + 2 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 tests/cases/compiler/jsxCommentDuplicationDebug.tsx diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c60fd92787275..d679fafabbad0 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -4713,9 +4713,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri * * NOTE: You probably don't want to call this directly and should be using `emitList` or `emitExpressionList` instead. */ - function emitNodeListItems(emit: EmitFunction, parentNode: Node | undefined, children: readonly Child[], format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start: number, count: number, hasTrailingComma: boolean, childrenTextRange: TextRange | undefined) { - // Write the opening line terminator or leading whitespace. - const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; + function emitNodeListItems(emit: EmitFunction, parentNode: Node | undefined, children: readonly Child[], format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start: number, count: number, hasTrailingComma: boolean, childrenTextRange: TextRange | undefined) { + // Write the opening line terminator or leading whitespace. + const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; let shouldEmitInterveningComments = mayEmitInterveningComments; const leadingLineTerminatorCount = getLeadingLineTerminatorCount(parentNode, children[start], format); @@ -4772,9 +4772,25 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri shouldDecreaseIndentAfterEmit = true; } - if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); + if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { + const commentRange = getCommentRange(child); + + // Check if this is a variable declaration with JSX content that would duplicate comments + let shouldEmitComments = true; + if (child.kind === SyntaxKind.VariableDeclaration && (child as any as VariableDeclaration).initializer) { + const initializer = (child as any as VariableDeclaration).initializer!; + if (initializer.kind === SyntaxKind.JsxElement || + initializer.kind === SyntaxKind.JsxSelfClosingElement || + initializer.kind === SyntaxKind.JsxFragment) { + // @ts-ignore + console.log(`DEBUG: Skipping DelimitersMask comment emission for JSX variable declaration`); + shouldEmitComments = false; + } + } + + if (shouldEmitComments) { + emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); + } } writeLine(separatingLineTerminatorCount); @@ -4785,13 +4801,32 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } } - // Emit this child. - if (shouldEmitInterveningComments) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos); - } - else { - shouldEmitInterveningComments = mayEmitInterveningComments; + // Emit this child. + if (shouldEmitInterveningComments) { + const commentRange = getCommentRange(child); + + // Check if this is a variable declaration with JSX content that would duplicate comments + let shouldEmitComments = true; + if (child.kind === SyntaxKind.VariableDeclaration && (child as any as VariableDeclaration).initializer) { + const initializer = (child as any as VariableDeclaration).initializer!; + // @ts-ignore + console.log(`DEBUG: VariableDeclaration initializer kind: ${initializer.kind}, SyntaxKind.JsxElement: ${SyntaxKind.JsxElement}`); + // If the initializer is a JSX element, the JSX emitter will handle its own comments + if (initializer.kind === SyntaxKind.JsxElement || + initializer.kind === SyntaxKind.JsxSelfClosingElement || + initializer.kind === SyntaxKind.JsxFragment) { + // @ts-ignore + console.log(`DEBUG: Skipping comment emission for JSX variable declaration`); + shouldEmitComments = false; + } + } + + if (shouldEmitComments) { + emitTrailingCommentsOfPosition(commentRange.pos); + } + } + else { + shouldEmitInterveningComments = mayEmitInterveningComments; } nextListElementPos = child.pos; diff --git a/tests/cases/compiler/jsxCommentDuplicationDebug.tsx b/tests/cases/compiler/jsxCommentDuplicationDebug.tsx new file mode 100644 index 0000000000000..e18a78c5279bb --- /dev/null +++ b/tests/cases/compiler/jsxCommentDuplicationDebug.tsx @@ -0,0 +1,3 @@ +// @jsx: preserve +function App() {} +const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; \ No newline at end of file From 804dddcbf8c77eb697cd085565a4f25a03c83c6d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 05:04:11 +0000 Subject: [PATCH 4/8] Fix JSX comment duplication issue Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 93 ++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 53 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d679fafabbad0..9c86ad24e5778 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1245,13 +1245,15 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri var mostRecentlyAddedSourceMapSource: SourceMapSource; var mostRecentlyAddedSourceMapSourceIndex = -1; - // Comments - var containerPos = -1; - var containerEnd = -1; - var declarationListContainerEnd = -1; - var currentLineMap: readonly number[] | undefined; - var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; - var hasWrittenComment = false; + // Comments + var containerPos = -1; + var containerEnd = -1; + var declarationListContainerEnd = -1; + // Track JSX elements to prevent duplicate comment emission + var currentJsxElement: JsxElement | undefined; + var currentLineMap: readonly number[] | undefined; + var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; + var hasWrittenComment = false; var commentsDisabled = !!printerOptions.removeComments; var lastSubstitution: Node | undefined; var currentParenthesizerRule: ParenthesizerRule | undefined; @@ -3857,10 +3859,15 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // JSX // - function emitJsxElement(node: JsxElement) { - emit(node.openingElement); - emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); - emit(node.closingElement); + function emitJsxElement(node: JsxElement) { + const savedJsxElement = currentJsxElement; + currentJsxElement = node; + + emit(node.openingElement); + emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); + emit(node.closingElement); + + currentJsxElement = savedJsxElement; } function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { @@ -4774,23 +4781,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { const commentRange = getCommentRange(child); - - // Check if this is a variable declaration with JSX content that would duplicate comments - let shouldEmitComments = true; - if (child.kind === SyntaxKind.VariableDeclaration && (child as any as VariableDeclaration).initializer) { - const initializer = (child as any as VariableDeclaration).initializer!; - if (initializer.kind === SyntaxKind.JsxElement || - initializer.kind === SyntaxKind.JsxSelfClosingElement || - initializer.kind === SyntaxKind.JsxFragment) { - // @ts-ignore - console.log(`DEBUG: Skipping DelimitersMask comment emission for JSX variable declaration`); - shouldEmitComments = false; - } - } - - if (shouldEmitComments) { - emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); - } + emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); } writeLine(separatingLineTerminatorCount); @@ -4804,26 +4795,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // Emit this child. if (shouldEmitInterveningComments) { const commentRange = getCommentRange(child); - - // Check if this is a variable declaration with JSX content that would duplicate comments - let shouldEmitComments = true; - if (child.kind === SyntaxKind.VariableDeclaration && (child as any as VariableDeclaration).initializer) { - const initializer = (child as any as VariableDeclaration).initializer!; - // @ts-ignore - console.log(`DEBUG: VariableDeclaration initializer kind: ${initializer.kind}, SyntaxKind.JsxElement: ${SyntaxKind.JsxElement}`); - // If the initializer is a JSX element, the JSX emitter will handle its own comments - if (initializer.kind === SyntaxKind.JsxElement || - initializer.kind === SyntaxKind.JsxSelfClosingElement || - initializer.kind === SyntaxKind.JsxFragment) { - // @ts-ignore - console.log(`DEBUG: Skipping comment emission for JSX variable declaration`); - shouldEmitComments = false; - } - } - - if (shouldEmitComments) { - emitTrailingCommentsOfPosition(commentRange.pos); - } + emitTrailingCommentsOfPosition(commentRange.pos); } else { shouldEmitInterveningComments = mayEmitInterveningComments; @@ -6136,11 +6108,26 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } } - function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { - // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments - if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { - forEachTrailingCommentRange(currentSourceFile.text, end, cb); - } + function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { + // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments + + // Check if this position is within a JSX element that contains comment-only text children + // If so, skip emission as the JSX processor will handle these comments + if (currentJsxElement && end >= currentJsxElement.pos && end <= currentJsxElement.end) { + // Check if any of the JSX children are comment-only text nodes + const hasCommentOnlyText = currentJsxElement.children.some(child => + child.kind === SyntaxKind.JsxText && + (child as JsxText).text.trim().startsWith('/*') && + (child as JsxText).text.trim().endsWith('*/') + ); + if (hasCommentOnlyText) { + return; // Skip comment emission - will be handled by JSX processing + } + } + + if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { + forEachTrailingCommentRange(currentSourceFile.text, end, cb); + } } function hasDetachedComments(pos: number) { From 68b447519446d1263a8db8ff7c3c0c3ab1ddef20 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 05:13:03 +0000 Subject: [PATCH 5/8] Final cleanup and lint fixes for JSX comment duplication fix Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 103 +++++++++--------- .../reference/jsxCommentDuplication.js | 9 ++ .../reference/jsxCommentDuplication.symbols | 11 ++ .../reference/jsxCommentDuplication.types | 17 +++ .../reference/jsxCommentDuplicationDebug.js | 9 ++ .../jsxCommentDuplicationDebug.symbols | 11 ++ .../jsxCommentDuplicationDebug.types | 17 +++ .../compiler/jsxCommentDuplicationDebug.tsx | 3 - 8 files changed, 126 insertions(+), 54 deletions(-) create mode 100644 tests/baselines/reference/jsxCommentDuplication.js create mode 100644 tests/baselines/reference/jsxCommentDuplication.symbols create mode 100644 tests/baselines/reference/jsxCommentDuplication.types create mode 100644 tests/baselines/reference/jsxCommentDuplicationDebug.js create mode 100644 tests/baselines/reference/jsxCommentDuplicationDebug.symbols create mode 100644 tests/baselines/reference/jsxCommentDuplicationDebug.types delete mode 100644 tests/cases/compiler/jsxCommentDuplicationDebug.tsx diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 9c86ad24e5778..ec7e918daa048 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1245,15 +1245,15 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri var mostRecentlyAddedSourceMapSource: SourceMapSource; var mostRecentlyAddedSourceMapSourceIndex = -1; - // Comments - var containerPos = -1; - var containerEnd = -1; - var declarationListContainerEnd = -1; - // Track JSX elements to prevent duplicate comment emission - var currentJsxElement: JsxElement | undefined; - var currentLineMap: readonly number[] | undefined; - var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; - var hasWrittenComment = false; + // Comments + var containerPos = -1; + var containerEnd = -1; + var declarationListContainerEnd = -1; + // Track JSX elements to prevent duplicate comment emission + var currentJsxElement: JsxElement | undefined; + var currentLineMap: readonly number[] | undefined; + var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; + var hasWrittenComment = false; var commentsDisabled = !!printerOptions.removeComments; var lastSubstitution: Node | undefined; var currentParenthesizerRule: ParenthesizerRule | undefined; @@ -3859,15 +3859,15 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // JSX // - function emitJsxElement(node: JsxElement) { - const savedJsxElement = currentJsxElement; - currentJsxElement = node; - - emit(node.openingElement); - emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); - emit(node.closingElement); - - currentJsxElement = savedJsxElement; + function emitJsxElement(node: JsxElement) { + const savedJsxElement = currentJsxElement; + currentJsxElement = node; + + emit(node.openingElement); + emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); + emit(node.closingElement); + + currentJsxElement = savedJsxElement; } function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { @@ -4720,9 +4720,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri * * NOTE: You probably don't want to call this directly and should be using `emitList` or `emitExpressionList` instead. */ - function emitNodeListItems(emit: EmitFunction, parentNode: Node | undefined, children: readonly Child[], format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start: number, count: number, hasTrailingComma: boolean, childrenTextRange: TextRange | undefined) { - // Write the opening line terminator or leading whitespace. - const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; + function emitNodeListItems(emit: EmitFunction, parentNode: Node | undefined, children: readonly Child[], format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start: number, count: number, hasTrailingComma: boolean, childrenTextRange: TextRange | undefined) { + // Write the opening line terminator or leading whitespace. + const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; let shouldEmitInterveningComments = mayEmitInterveningComments; const leadingLineTerminatorCount = getLeadingLineTerminatorCount(parentNode, children[start], format); @@ -4779,9 +4779,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri shouldDecreaseIndentAfterEmit = true; } - if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); + if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { + const commentRange = getCommentRange(child); + emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); } writeLine(separatingLineTerminatorCount); @@ -4792,13 +4792,13 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } } - // Emit this child. - if (shouldEmitInterveningComments) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos); - } - else { - shouldEmitInterveningComments = mayEmitInterveningComments; + // Emit this child. + if (shouldEmitInterveningComments) { + const commentRange = getCommentRange(child); + emitTrailingCommentsOfPosition(commentRange.pos); + } + else { + shouldEmitInterveningComments = mayEmitInterveningComments; } nextListElementPos = child.pos; @@ -6108,26 +6108,27 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } } - function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { - // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments - - // Check if this position is within a JSX element that contains comment-only text children - // If so, skip emission as the JSX processor will handle these comments - if (currentJsxElement && end >= currentJsxElement.pos && end <= currentJsxElement.end) { - // Check if any of the JSX children are comment-only text nodes - const hasCommentOnlyText = currentJsxElement.children.some(child => - child.kind === SyntaxKind.JsxText && - (child as JsxText).text.trim().startsWith('/*') && - (child as JsxText).text.trim().endsWith('*/') - ); - if (hasCommentOnlyText) { - return; // Skip comment emission - will be handled by JSX processing - } - } - - if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { - forEachTrailingCommentRange(currentSourceFile.text, end, cb); - } + function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { + // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments + + // Check if this position is within a JSX element that contains comment-only text children + // If so, skip emission as the JSX processor will handle these comments + if (currentJsxElement && end >= currentJsxElement.pos && end <= currentJsxElement.end) { + // Check if any of the JSX children are comment-only text nodes + const hasCommentOnlyText = currentJsxElement.children.some(child => { + if (child.kind === SyntaxKind.JsxText) { + return child.text.trim().startsWith("/*") && child.text.trim().endsWith("*/"); + } + return false; + }); + if (hasCommentOnlyText) { + return; // Skip comment emission - will be handled by JSX processing + } + } + + if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { + forEachTrailingCommentRange(currentSourceFile.text, end, cb); + } } function hasDetachedComments(pos: number) { diff --git a/tests/baselines/reference/jsxCommentDuplication.js b/tests/baselines/reference/jsxCommentDuplication.js new file mode 100644 index 0000000000000..42cf5304a7a95 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication.js @@ -0,0 +1,9 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +//// [jsxCommentDuplication.tsx] +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + +//// [jsxCommentDuplication.jsx] +function App() { } +var jsx = /* no */{123}/* no */; diff --git a/tests/baselines/reference/jsxCommentDuplication.symbols b/tests/baselines/reference/jsxCommentDuplication.symbols new file mode 100644 index 0000000000000..328bd5542845d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication.symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplication.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplication.types b/tests/baselines/reference/jsxCommentDuplication.types new file mode 100644 index 0000000000000..5a4fbfe5078c4 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication.types @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : error +>/* no */{/* 1 */ 123 /* 2 */}/* no */ : error +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/baselines/reference/jsxCommentDuplicationDebug.js b/tests/baselines/reference/jsxCommentDuplicationDebug.js new file mode 100644 index 0000000000000..cd1898a0501c6 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplicationDebug.js @@ -0,0 +1,9 @@ +//// [tests/cases/compiler/jsxCommentDuplicationDebug.tsx] //// + +//// [jsxCommentDuplicationDebug.tsx] +function App() {} +const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; + +//// [jsxCommentDuplicationDebug.jsx] +function App() { } +var jsx = /* before */{123}/* after */; diff --git a/tests/baselines/reference/jsxCommentDuplicationDebug.symbols b/tests/baselines/reference/jsxCommentDuplicationDebug.symbols new file mode 100644 index 0000000000000..a5801dee31379 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplicationDebug.symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplicationDebug.tsx] //// + +=== jsxCommentDuplicationDebug.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplicationDebug.tsx, 0, 0)) + +const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplicationDebug.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplicationDebug.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplicationDebug.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplicationDebug.types b/tests/baselines/reference/jsxCommentDuplicationDebug.types new file mode 100644 index 0000000000000..84f6d3b529b0f --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplicationDebug.types @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/jsxCommentDuplicationDebug.tsx] //// + +=== jsxCommentDuplicationDebug.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; +>jsx : error +>/* before */{/* 1 */ 123 /* 2 */}/* after */ : error +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/cases/compiler/jsxCommentDuplicationDebug.tsx b/tests/cases/compiler/jsxCommentDuplicationDebug.tsx deleted file mode 100644 index e18a78c5279bb..0000000000000 --- a/tests/cases/compiler/jsxCommentDuplicationDebug.tsx +++ /dev/null @@ -1,3 +0,0 @@ -// @jsx: preserve -function App() {} -const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; \ No newline at end of file From 0cb049ffc7e0b1231cebfbb9e1f29706003bbde4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 07:23:21 +0000 Subject: [PATCH 6/8] Fix JSX comment duplication by excluding JSX text ranges from comment emission Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 58 +++++++++++-------- .../reference/jsxCommentDuplication.js | 2 +- .../reference/tsxAttributeResolution14.js | 9 +-- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ec7e918daa048..dee6758ef1559 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1249,12 +1249,12 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri var containerPos = -1; var containerEnd = -1; var declarationListContainerEnd = -1; - // Track JSX elements to prevent duplicate comment emission - var currentJsxElement: JsxElement | undefined; var currentLineMap: readonly number[] | undefined; var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; var hasWrittenComment = false; var commentsDisabled = !!printerOptions.removeComments; + // Track JSX text ranges to prevent them from being treated as comments + var jsxTextRanges: { start: number; end: number; }[] = []; var lastSubstitution: Node | undefined; var currentParenthesizerRule: ParenthesizerRule | undefined; var { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment"); @@ -1389,11 +1389,22 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri currentSourceFile = sourceFile; currentLineMap = undefined; detachedCommentsInfo = undefined; + jsxTextRanges = []; // Clear JSX text ranges for new source file + + // Pre-collect all JSX text ranges before emission starts if (sourceFile) { + collectJsxTextRanges(sourceFile); setSourceMapSource(sourceFile); } } + function collectJsxTextRanges(node: Node) { + if (node.kind === SyntaxKind.JsxText) { + jsxTextRanges.push({ start: node.pos, end: node.end }); + } + forEachChild(node, collectJsxTextRanges); + } + function setWriter(_writer: EmitTextWriter | undefined, _sourceMapGenerator: SourceMapGenerator | undefined) { if (_writer && printerOptions.omitTrailingSemicolon) { _writer = getTrailingSemicolonDeferringWriter(_writer); @@ -3860,14 +3871,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // function emitJsxElement(node: JsxElement) { - const savedJsxElement = currentJsxElement; - currentJsxElement = node; - emit(node.openingElement); emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); emit(node.closingElement); - - currentJsxElement = savedJsxElement; } function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { @@ -6103,31 +6109,27 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri forEachLeadingCommentWithoutDetachedComments(cb); } else { - forEachLeadingCommentRange(currentSourceFile.text, pos, cb, /*state*/ pos); + forEachLeadingCommentRange(currentSourceFile.text, pos, (commentPos, commentEnd, kind, hasTrailingNewLine, rangePos) => { + // Check if this comment position falls within any JSX text range + const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); + if (!isWithinJsxText) { + cb(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); + } + }, /*state*/ pos); } } } function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments - - // Check if this position is within a JSX element that contains comment-only text children - // If so, skip emission as the JSX processor will handle these comments - if (currentJsxElement && end >= currentJsxElement.pos && end <= currentJsxElement.end) { - // Check if any of the JSX children are comment-only text nodes - const hasCommentOnlyText = currentJsxElement.children.some(child => { - if (child.kind === SyntaxKind.JsxText) { - return child.text.trim().startsWith("/*") && child.text.trim().endsWith("*/"); + if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { + forEachTrailingCommentRange(currentSourceFile.text, end, (commentPos, commentEnd, kind, hasTrailingNewLine) => { + // Check if this comment position falls within any JSX text range + const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); + if (!isWithinJsxText) { + cb(commentPos, commentEnd, kind, hasTrailingNewLine); } - return false; }); - if (hasCommentOnlyText) { - return; // Skip comment emission - will be handled by JSX processing - } - } - - if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { - forEachTrailingCommentRange(currentSourceFile.text, end, cb); } } @@ -6146,7 +6148,13 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri detachedCommentsInfo = undefined; } - forEachLeadingCommentRange(currentSourceFile.text, pos, cb, /*state*/ pos); + forEachLeadingCommentRange(currentSourceFile.text, pos, (commentPos, commentEnd, kind, hasTrailingNewLine, rangePos) => { + // Check if this comment position falls within any JSX text range + const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); + if (!isWithinJsxText) { + cb(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); + } + }, /*state*/ pos); } function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) { diff --git a/tests/baselines/reference/jsxCommentDuplication.js b/tests/baselines/reference/jsxCommentDuplication.js index 42cf5304a7a95..15426054ad301 100644 --- a/tests/baselines/reference/jsxCommentDuplication.js +++ b/tests/baselines/reference/jsxCommentDuplication.js @@ -6,4 +6,4 @@ const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; //// [jsxCommentDuplication.jsx] function App() { } -var jsx = /* no */{123}/* no */; +var jsx = /* no */{/* 1 */123 /* 2 */}/* no */; diff --git a/tests/baselines/reference/tsxAttributeResolution14.js b/tests/baselines/reference/tsxAttributeResolution14.js index 26633239d3918..1d20896a73dd7 100644 --- a/tests/baselines/reference/tsxAttributeResolution14.js +++ b/tests/baselines/reference/tsxAttributeResolution14.js @@ -35,11 +35,8 @@ function VerticalNavMenuItem(prop) { } function VerticalNav() { return (
- // error - // error - // ok - // ok - // error - // error + // error + // ok + // error
); } From 381423f459117fdd67b6fec0aa334a32246da81f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 07:35:59 +0000 Subject: [PATCH 7/8] Add question about JSX comment handling to documentation Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- .github/copilot-questions.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/copilot-questions.md b/.github/copilot-questions.md index 250d4bf2d776d..d9c9c74a0afa8 100644 --- a/.github/copilot-questions.md +++ b/.github/copilot-questions.md @@ -1,4 +1,5 @@ -Questions I have that I think the developers of this project can help me with: - * How does control flow analysis represent a circular graph? I checked the documentation server for "cfa" and "control flow" - * How do I tell if a symbol is in the global scope? I checked the documentation server for topics referencing "symbol" and "global" - * What is an `EscapedName`, exactly? +Questions I have that I think the developers of this project can help me with: + * How does control flow analysis represent a circular graph? I checked the documentation server for "cfa" and "control flow" + * How do I tell if a symbol is in the global scope? I checked the documentation server for topics referencing "symbol" and "global" + * What is an `EscapedName`, exactly? + * How does TypeScript distinguish between comments inside JSX text vs regular TypeScript comments? I searched for "JSX text comments emitter forEachTrailingCommentRange JSX text ranges" - The issue was that comment iteration doesn't understand JSX context and treats JSX text content as regular comments. From 93c7172440813d8050b01f3ff583dd4067b79fd2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:49:35 +0000 Subject: [PATCH 8/8] Improve JSX comment duplication fix - use targeted node traversal instead of expensive pre-walk Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 63 ++++++++++++------- .../jsxCommentDuplication(jsx=preserve).js | 9 +++ ...sxCommentDuplication(jsx=preserve).symbols | 11 ++++ .../jsxCommentDuplication(jsx=preserve).types | 17 +++++ ...sxCommentDuplication(jsx=react).errors.txt | 8 +++ .../jsxCommentDuplication(jsx=react).js | 12 ++++ .../jsxCommentDuplication(jsx=react).symbols | 11 ++++ .../jsxCommentDuplication(jsx=react).types | 19 ++++++ ...mmentDuplication(jsx=react-jsx).errors.txt | 8 +++ .../jsxCommentDuplication(jsx=react-jsx).js | 12 ++++ ...xCommentDuplication(jsx=react-jsx).symbols | 11 ++++ ...jsxCommentDuplication(jsx=react-jsx).types | 19 ++++++ .../cases/compiler/jsxCommentDuplication.tsx | 2 +- 13 files changed, 180 insertions(+), 22 deletions(-) create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=preserve).js create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=preserve).symbols create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=preserve).types create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react).errors.txt create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react).js create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react).symbols create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react).types create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).errors.txt create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).js create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).symbols create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).types diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index dee6758ef1559..e6c55e11e45d8 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1253,8 +1253,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; var hasWrittenComment = false; var commentsDisabled = !!printerOptions.removeComments; - // Track JSX text ranges to prevent them from being treated as comments - var jsxTextRanges: { start: number; end: number; }[] = []; + var jsxTextPositionCache: Map | undefined; var lastSubstitution: Node | undefined; var currentParenthesizerRule: ParenthesizerRule | undefined; var { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment"); @@ -1389,22 +1388,13 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri currentSourceFile = sourceFile; currentLineMap = undefined; detachedCommentsInfo = undefined; - jsxTextRanges = []; // Clear JSX text ranges for new source file + jsxTextPositionCache = undefined; // Clear cache for new source file - // Pre-collect all JSX text ranges before emission starts if (sourceFile) { - collectJsxTextRanges(sourceFile); setSourceMapSource(sourceFile); } } - function collectJsxTextRanges(node: Node) { - if (node.kind === SyntaxKind.JsxText) { - jsxTextRanges.push({ start: node.pos, end: node.end }); - } - forEachChild(node, collectJsxTextRanges); - } - function setWriter(_writer: EmitTextWriter | undefined, _sourceMapGenerator: SourceMapGenerator | undefined) { if (_writer && printerOptions.omitTrailingSemicolon) { _writer = getTrailingSemicolonDeferringWriter(_writer); @@ -1415,6 +1405,40 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri sourceMapsDisabled = !writer || !sourceMapGenerator; } + function isPositionInJsxText(pos: number): boolean { + if (!currentSourceFile) return false; + + // Use cache if available + if (!jsxTextPositionCache) { + jsxTextPositionCache = new Map(); + } + + if (jsxTextPositionCache.has(pos)) { + return jsxTextPositionCache.get(pos)!; + } + + // Walk the AST to find if this position is within a JSX text node + let found = false; + + function visitNode(node: Node): void { + if (found) return; + + if (node.kind === SyntaxKind.JsxText && pos >= node.pos && pos < node.end) { + found = true; + return; + } + + // Only continue traversing if this node might contain the position + if (pos >= node.pos && pos < node.end) { + forEachChild(node, visitNode); + } + } + + visitNode(currentSourceFile); + jsxTextPositionCache.set(pos, found); + return found; + } + function reset() { nodeIdToGeneratedName = []; nodeIdToGeneratedPrivateName = []; @@ -6110,9 +6134,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } else { forEachLeadingCommentRange(currentSourceFile.text, pos, (commentPos, commentEnd, kind, hasTrailingNewLine, rangePos) => { - // Check if this comment position falls within any JSX text range - const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); - if (!isWithinJsxText) { + // Skip comments that are within JSX text nodes + if (!isPositionInJsxText(commentPos)) { cb(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); } }, /*state*/ pos); @@ -6124,9 +6147,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { forEachTrailingCommentRange(currentSourceFile.text, end, (commentPos, commentEnd, kind, hasTrailingNewLine) => { - // Check if this comment position falls within any JSX text range - const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); - if (!isWithinJsxText) { + // Skip comments that are within JSX text nodes + if (!isPositionInJsxText(commentPos)) { cb(commentPos, commentEnd, kind, hasTrailingNewLine); } }); @@ -6149,9 +6171,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } forEachLeadingCommentRange(currentSourceFile.text, pos, (commentPos, commentEnd, kind, hasTrailingNewLine, rangePos) => { - // Check if this comment position falls within any JSX text range - const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); - if (!isWithinJsxText) { + // Skip comments that are within JSX text nodes + if (!isPositionInJsxText(commentPos)) { cb(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); } }, /*state*/ pos); diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).js b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).js new file mode 100644 index 0000000000000..15426054ad301 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).js @@ -0,0 +1,9 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +//// [jsxCommentDuplication.tsx] +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + +//// [jsxCommentDuplication.jsx] +function App() { } +var jsx = /* no */{/* 1 */123 /* 2 */}/* no */; diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).symbols b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).symbols new file mode 100644 index 0000000000000..328bd5542845d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplication.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).types b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).types new file mode 100644 index 0000000000000..5a4fbfe5078c4 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).types @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : error +>/* no */{/* 1 */ 123 /* 2 */}/* no */ : error +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react).errors.txt b/tests/baselines/reference/jsxCommentDuplication(jsx=react).errors.txt new file mode 100644 index 0000000000000..5902bb8f12f9e --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react).errors.txt @@ -0,0 +1,8 @@ +jsxCommentDuplication.tsx(2,14): error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found. + + +==== jsxCommentDuplication.tsx (1 errors) ==== + function App() {} + const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + ~~~ +!!! error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found. \ No newline at end of file diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react).js b/tests/baselines/reference/jsxCommentDuplication(jsx=react).js new file mode 100644 index 0000000000000..37cba9d576718 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react).js @@ -0,0 +1,12 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +//// [jsxCommentDuplication.tsx] +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + +//// [jsxCommentDuplication.js] +function App() { } +var jsx = React.createElement(App, null, + "/* no */", /* 1 */ + 123 /* 2 */, + "/* no */"); diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react).symbols b/tests/baselines/reference/jsxCommentDuplication(jsx=react).symbols new file mode 100644 index 0000000000000..328bd5542845d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react).symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplication.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react).types b/tests/baselines/reference/jsxCommentDuplication(jsx=react).types new file mode 100644 index 0000000000000..d0333134fdd1c --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react).types @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : any +> : ^^^ +>/* no */{/* 1 */ 123 /* 2 */}/* no */ : any +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).errors.txt b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).errors.txt new file mode 100644 index 0000000000000..a821a45616d2d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).errors.txt @@ -0,0 +1,8 @@ +jsxCommentDuplication.tsx(2,13): error TS2875: This JSX tag requires the module path 'react/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. + + +==== jsxCommentDuplication.tsx (1 errors) ==== + function App() {} + const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2875: This JSX tag requires the module path 'react/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. \ No newline at end of file diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).js b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).js new file mode 100644 index 0000000000000..b28abc2eb3933 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).js @@ -0,0 +1,12 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +//// [jsxCommentDuplication.tsx] +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + +//// [jsxCommentDuplication.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var jsx_runtime_1 = require("react/jsx-runtime"); +function App() { } +var jsx = (0, jsx_runtime_1.jsxs)(App, { children: ["/* no */", /* 1 */ 123 /* 2 */, "/* no */"] }); diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).symbols b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).symbols new file mode 100644 index 0000000000000..328bd5542845d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplication.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).types b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).types new file mode 100644 index 0000000000000..d0333134fdd1c --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).types @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : any +> : ^^^ +>/* no */{/* 1 */ 123 /* 2 */}/* no */ : any +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/cases/compiler/jsxCommentDuplication.tsx b/tests/cases/compiler/jsxCommentDuplication.tsx index fe13875536038..a46de1b2ac76b 100644 --- a/tests/cases/compiler/jsxCommentDuplication.tsx +++ b/tests/cases/compiler/jsxCommentDuplication.tsx @@ -1,3 +1,3 @@ -// @jsx: preserve +// @jsx: preserve,react,react-jsx function App() {} const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; \ No newline at end of file