Skip to content

Commit d6d8785

Browse files
Fix JSX comment duplication by properly handling comments in JSX text scanning
Co-authored-by: RyanCavanaugh <[email protected]>
1 parent 24295d3 commit d6d8785

File tree

3 files changed

+41
-38
lines changed

3 files changed

+41
-38
lines changed

src/compiler/emitter.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3896,11 +3896,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
38963896
writePunctuation(">");
38973897
}
38983898

3899-
function emitJsxText(node: JsxText) {
3900-
// Strip comments from JSX text content to prevent duplication
3901-
// Comments like /* comment */ should not be treated as text content in JSX
3902-
const textWithoutComments = node.text.replace(/\/\*[\s\S]*?\*\//g, '');
3903-
writer.writeLiteral(textWithoutComments);
3899+
function emitJsxText(node: JsxText) {
3900+
writer.writeLiteral(node.text);
39043901
}
39053902

39063903
function emitJsxClosingElementOrFragment(node: JsxClosingElement | JsxClosingFragment) {

src/compiler/scanner.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3720,6 +3720,9 @@ export function createScanner(
37203720
// These initial values are special because the first line is:
37213721
// firstNonWhitespace = 0 to indicate that we want leading whitespace,
37223722

3723+
// Build the text content excluding comments
3724+
let textContent = "";
3725+
37233726
while (pos < end) {
37243727
char = charCodeUnchecked(pos);
37253728
if (char === CharacterCodes.openBrace) {
@@ -3739,6 +3742,21 @@ export function createScanner(
37393742
error(Diagnostics.Unexpected_token_Did_you_mean_or_rbrace, pos, 1);
37403743
}
37413744

3745+
// Handle comments - skip /* ... */ style comments
3746+
if (char === CharacterCodes.slash && charCodeUnchecked(pos + 1) === CharacterCodes.asterisk) {
3747+
pos += 2; // Skip /*
3748+
// Find the end of the comment
3749+
while (pos < end) {
3750+
if (charCodeUnchecked(pos) === CharacterCodes.asterisk && charCodeUnchecked(pos + 1) === CharacterCodes.slash) {
3751+
pos += 2; // Skip */
3752+
break;
3753+
}
3754+
pos++;
3755+
}
3756+
// Continue without adding comment content to textContent
3757+
continue;
3758+
}
3759+
37423760
// FirstNonWhitespace is 0, then we only see whitespaces so far. If we see a linebreak, we want to ignore that whitespaces.
37433761
// i.e (- : whitespace)
37443762
// <div>----
@@ -3754,13 +3772,16 @@ export function createScanner(
37543772
break;
37553773
}
37563774
else if (!isWhiteSpaceLike(char)) {
3757-
firstNonWhitespace = pos;
3775+
if (firstNonWhitespace === 0) {
3776+
firstNonWhitespace = textContent.length;
3777+
}
37583778
}
37593779

3780+
textContent += String.fromCharCode(char);
37603781
pos++;
37613782
}
37623783

3763-
tokenValue = text.substring(fullStartPos, pos);
3784+
tokenValue = textContent;
37643785

37653786
return firstNonWhitespace === -1 ? SyntaxKind.JsxTextAllWhiteSpaces : SyntaxKind.JsxText;
37663787
}

src/compiler/transformers/jsx.ts

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -554,37 +554,22 @@ export function transformJsx(context: TransformationContext): (x: SourceFile | B
554554
return fixed === undefined ? undefined : factory.createStringLiteral(fixed);
555555
}
556556

557-
/**
558-
* Remove comments from JSX text content.
559-
* Comments like slash-star comment star-slash should not be treated as text content in JSX.
560-
*/
561-
function stripCommentsFromJsxText(text: string): string {
562-
// Only strip comments when not in preserve mode
563-
if (compilerOptions.jsx === JsxEmit.Preserve) {
564-
return text;
565-
}
566-
// Remove /* ... */ style comments from JSX text
567-
return text.replace(/\/\*[\s\S]*?\*\//g, '');
568-
}
569-
570-
/**
571-
* JSX trims whitespace at the end and beginning of lines, except that the
572-
* start/end of a tag is considered a start/end of a line only if that line is
573-
* on the same line as the closing tag. See examples in
574-
* tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
575-
* See also https://www.w3.org/TR/html4/struct/text.html#h-9.1 and https://www.w3.org/TR/CSS2/text.html#white-space-model
576-
*
577-
* An equivalent algorithm would be:
578-
* - If there is only one line, return it.
579-
* - If there is only whitespace (but multiple lines), return `undefined`.
580-
* - Split the text into lines.
581-
* - 'trimRight' the first line, 'trimLeft' the last line, 'trim' middle lines.
582-
* - Decode entities on each line (individually).
583-
* - Remove empty lines and join the rest with " ".
584-
*/
585-
function fixupWhitespaceAndDecodeEntities(text: string): string | undefined {
586-
// First, strip comments from the text
587-
text = stripCommentsFromJsxText(text);
557+
/**
558+
* JSX trims whitespace at the end and beginning of lines, except that the
559+
* start/end of a tag is considered a start/end of a line only if that line is
560+
* on the same line as the closing tag. See examples in
561+
* tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
562+
* See also https://www.w3.org/TR/html4/struct/text.html#h-9.1 and https://www.w3.org/TR/CSS2/text.html#white-space-model
563+
*
564+
* An equivalent algorithm would be:
565+
* - If there is only one line, return it.
566+
* - If there is only whitespace (but multiple lines), return `undefined`.
567+
* - Split the text into lines.
568+
* - 'trimRight' the first line, 'trimLeft' the last line, 'trim' middle lines.
569+
* - Decode entities on each line (individually).
570+
* - Remove empty lines and join the rest with " ".
571+
*/
572+
function fixupWhitespaceAndDecodeEntities(text: string): string | undefined {
588573
let acc: string | undefined;
589574
// First non-whitespace character on this line.
590575
let firstNonWhitespace = 0;

0 commit comments

Comments
 (0)