Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 2, 2025

  • Explore the codebase and understand the issue
  • Examine existing tsx test (jsxUnicodeEscapeSequence.tsx)
  • Create test case based on the issue (jsxNestedIndentation.tsx)
  • Reproduce the bug and confirm TypeScript's expected output
  • Accept initial test baselines (shows bug: all on one line)
  • Fix the bug in printer.go by uncommenting EFStartOnNewLine handling
  • Re-run tests and accept new baselines (4488 → 37 .diff files!)
  • Run full test suite to ensure no regressions
  • Verify linting and formatting pass
  • Fix regression in commentsOnJSXExpressionsArePreserved test
    • Fixed inJsxChild flag state management for nested transforms
    • Fixed children newline logic to check count after filtering
    • All tests now pass with 3 more .diff files removed
Original prompt

This section details on the original issue you should resolve

<issue_title>Indentation removed in JavaScript output for JSX (minor)</issue_title>
<issue_description>## Steps to reproduce

import * as React from 'react'
declare function Child(props: { children?: React.ReactNode }): React.ReactNode;
function Test() {
    return <Child>
        <Child>
            <Child></Child>
        </Child>
    </Child>
}

Behavior with [email protected]

The output preserves the newlines and indentation of the tags:

function Test() {
    return React.createElement(Child, null,
        React.createElement(Child, null,
            React.createElement(Child, null)));
}

Behavior with tsgo

The output collapses everything on a single line:

function Test() {
    return React.createElement(Child, null, React.createElement(Child, null, React.createElement(Child, null)));
}

</issue_description>

<agent_instructions>Examine an existing tsx test first. Then create a test based on the issue and accept its baselines. Then fix the bug.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

Fixes #1791

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 3 commits October 2, 2025 16:14
Uncommented the code in printer.go that respects the EFStartOnNewLine
emit flag. This flag is set by the JSX transformer when transforming
JSX elements with multiple children, and causes the printer to emit
each child on a new line with proper indentation.

This fix brings tsgo's JSX output into alignment with TypeScript's
reference implementation, reducing the number of .diff files from
4488 to 37, and properly formatting nested JSX as:

React.createElement(Child, null,
    React.createElement(Child, null,
        React.createElement(Child, null)));

instead of:

React.createElement(Child, null, React.createElement(Child, null, React.createElement(Child, null)));

Co-authored-by: jakebailey <[email protected]>
Copilot AI changed the title [WIP] Indentation removed in JavaScript output for JSX (minor) Fix JSX indentation in JavaScript output Oct 2, 2025
Copilot AI requested a review from jakebailey October 2, 2025 16:28
Copilot finished work on behalf of jakebailey October 2, 2025 16:28
The previous fix incorrectly cleared the inJsxChild flag when transforming
nested JSX children. When transforming a nested child element, the defer
statement would set inJsxChild back to false, causing the parent element
to not receive the EFStartOnNewLine flag.

Fixed by saving and restoring the previous value of inJsxChild instead of
unconditionally setting it to false. This ensures that the flag state is
properly maintained across nested transformations.

Also fixed the logic for adding EFStartOnNewLine to children: the flag
should only be added when there are multiple children AFTER filtering out
null/whitespace children, not before. This matches TypeScript's behavior
where a single child expression does not get a newline, but multiple
children do.

These fixes resolve the regression in commentsOnJSXExpressionsArePreserved
tests, where a single JSX expression child was incorrectly getting a newline.

Co-authored-by: jakebailey <[email protected]>
Copilot AI requested a review from jakebailey October 2, 2025 17:13
Copilot finished work on behalf of jakebailey October 2, 2025 17:13
Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is good?

@jakebailey jakebailey marked this pull request as ready for review October 2, 2025 17:33
Copilot AI review requested due to automatic review settings October 2, 2025 17:33
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes JSX indentation in JavaScript output to match TypeScript's behavior. The issue was that nested JSX elements were being collapsed to a single line instead of preserving proper indentation and newlines.

  • Fixed JSX indentation by enabling the EFStartOnNewLine flag handling in the printer
  • Improved state management for JSX child transformations to prevent context corruption
  • Updated newline logic to check children count after filtering out null children

Reviewed Changes

Copilot reviewed 91 out of 91 changed files in this pull request and generated no comments.

Show a summary per file
File Description
testdata/tests/cases/compiler/jsxNestedIndentation.tsx New test case to verify JSX nested indentation behavior
testdata/tests/cases/compiler/jsxNestedIndentation.js Expected JavaScript output with proper indentation
internal/transformers/jsxtransforms/jsx.go Fixed JSX child state management and newline logic for proper indentation
internal/printer/printer.go Enabled EFStartOnNewLine flag handling to support multiline JSX output
Various baseline files Updated test baselines showing the fix applied across many JSX tests

@jakebailey jakebailey requested a review from weswigham October 2, 2025 18:23
Copy link
Member

@weswigham weswigham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably better, since it makes sense to count the result nodes instead of the input nodes. However, in strada we did just always set the startOnNewLine flag on jsx elements which were children of other jsx elements, irrespective of sibling node count, so we're probably still different (we did, however, not set it on jsx text children - only tag-like children). In any case, we might need a followup change that swaps len(newChildren) > 1 to something like len(core.Filter(newChildren,isNotWhitespaceOnlyJsxText)) > 1 so whitespace only jsx text doesn't count as a "real" child element for the purpose of indentation - this is all different from strada, though, so it's kinda... opinion-y?

I dunno - maybe more diffs go away if we align with strada more instead and only set the flag on tag-like children irrespective of sibling count.

@jakebailey
Copy link
Member

Yeah, this all seems like style. I tried doing:

if tx.inJsxChild {
	tx.EmitContext().AddEmitFlags(res, printer.EFStartOnNewLine)
}

And it seems to create more differences, but maybe that's not what you mean.

I did try out redoing the tag-like-children thing and only got more diffs, so I must be doing something wrong here. I'll just merge this as a slight improvement for now.

But I agree, this is all sort of style opiniony.

@jakebailey
Copy link
Member

Er, well, need another approval to merge but oh well.

Copy link
Member

@weswigham weswigham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking moreso replacing

	// Add StartOnNewLine flag only if there are multiple actual children (after filtering)
	if len(newChildren) > 1 {
		for _, child := range newChildren {
			tx.EmitContext().AddEmitFlags(child, printer.EFStartOnNewLine)
		}
	}

with

for _, child := range newChildren {
    if !(ast.IsJsxElement(child) || ast.IsJsxSelfClosingElement(child) || ast.IsJsxFragment(child)) continue;
	tx.EmitContext().AddEmitFlags(child, printer.EFStartOnNewLine)
}

which probably more closely matches the strada condition for indentation.

@jakebailey
Copy link
Member

Just tried it, and that's seemingly worse than this PR too. Very weird....

@jakebailey jakebailey added this pull request to the merge queue Oct 22, 2025
Merged via the queue into main with commit fc800a7 Oct 22, 2025
42 checks passed
@jakebailey jakebailey deleted the copilot/fix-d7ff9038-c725-4834-ae27-52390ab7a09e branch October 22, 2025 19:10
nathanwhit added a commit to denoland/typescript-go that referenced this pull request Dec 2, 2025
* Port 'go to type definition' tests (microsoft#1883)

* Fix panic in `getTokenAtPosition` for JSDoc type assertions (microsoft#1846)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>
Co-authored-by: andrewbranch <[email protected]>
Co-authored-by: Andrew Branch <[email protected]>

* Don’t look in JSExportAssignment and CommonJSExport for nodes (microsoft#1886)

* Fix link in native preview platform packages (microsoft#1838)

* fix(1880): No error message for JSDoc type parsing (microsoft#1881)

* Add vscode editor issue template (microsoft#1893)

Co-authored-by: Ryan Cavanaugh <[email protected]>

* Add "Report Issue" button to TSGO status bar commands (microsoft#1889)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: DanielRosenwasser <[email protected]>
Co-authored-by: Daniel Rosenwasser <[email protected]>

* fix(1898): adjust location handling in find-refs (microsoft#1901)

* Fix panic of empty string in type reference directive (microsoft#1908)

* Consistently error on full circle of circular import aliases (microsoft#1904)

* Fix panic in textDocument/onTypeFormatting when tokenAtPosition is nil (microsoft#1845)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Update submodule (microsoft#1913)

* Disable create-cache.yml in forks (microsoft#1912)

* Forbid platform specific package uses in agnostic files (microsoft#1911)

* Fix JSDoc comment formatting with tab indentation (microsoft#1900)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Clear local baseline dir in hereby test (microsoft#1921)

* Unskip passing fourslash test (microsoft#1922)

* Support auto-import completion fourslash tests, fix bugs (microsoft#1917)

* Fix JSX indentation in JavaScript output (microsoft#1792)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Implement printAllHelp to fix `tsgo --all` producing no output (microsoft#1843)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Bump the github-actions group across 1 directory with 2 updates (microsoft#1909)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jake Bailey <[email protected]>

* Ensure os package is forbidden in lint (microsoft#1924)

* Speed up levenshteinWithMax by reusing buffers (microsoft#1823)

* Fix incorrect formatting for comments inside multi-line argument lists and method chains (microsoft#1929)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Handle nil end position in getMappedLocation (microsoft#1920)

* Fix formatter adding extra space at end of line without trailing newline (microsoft#1933)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Fix vscode issue template (microsoft#1934)

* userpreferences parsing/ls config handing (microsoft#1729)

* Plumb through TokenFlagsSingleQuote; use for auto import quote detection (microsoft#1937)

* Invalidate caches on batches of 1000+ watch changes (microsoft#1869)

* Create clickable links in quick info from @link JSDoc tags (microsoft#1935)

* Don't report errors on `{@link foo.bar}` references (microsoft#1941)

* Fix crash in `invocationErrorRecovery` function (microsoft#1944)

* Fix leading source file comment emit bugs (microsoft#1945)

* Implement selection ranges (microsoft#1939)

* Fix porting bug in isArgumentAndStartLineOverlapsExpressionBeingCalled (microsoft#1948)

* Add Range to Hover (microsoft#1489)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: DanielRosenwasser <[email protected]>
Co-authored-by: Jake Bailey <[email protected]>

* Properly handle hovering on `this` (microsoft#1953)

* Bump the github-actions group across 1 directory with 2 updates (microsoft#1959)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fixed document highlight for reference directive (microsoft#1951)

* Several fixes to JS typing of functions and methods (microsoft#1960)

* Update submodule, port 6.0 options defaults (microsoft#1961)

* Reapply microsoft#1951 and microsoft#1960 after bad merge (microsoft#1964)

Co-authored-by: Anders Hejlsberg <[email protected]>
Co-authored-by: John Favret <[email protected]>

* Update submodule with ES5 removals (microsoft#1963)

* Actually transform KindCommonJSExport in declaration emit (microsoft#1962)

* Quick Info fixes (microsoft#1971)

* Fix various named enum types (microsoft#1973)

* Move change tracker, converters, utils to separate packages (microsoft#1977)

* Consistent rules for mixing `@type`, `@param`, `@return`, `@template` (microsoft#1979)

* Check for identifier before obtaining text of name (microsoft#1984)

* Respect client capabilities for diagnostics (microsoft#1980)

* Store explicitly declared members ahead of inherited members (microsoft#1987)

* Add --checkers to control number of checkers per Program (microsoft#1985)

* Export all types referenced through other exported APIs, enforce (microsoft#1978)

* Switch custom runners from mariner-2.0 to azure-linux-3 (microsoft#1989)

* Use `LocationLink` in go to definition (microsoft#1884)

* Use a different set of commands to detect fourslash test updates (microsoft#1923)

* Skip erasableSyntaxOnly checks for JavaScript files (microsoft#1956)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Update submodule for new cherry-picks (microsoft#1996)

* Port TypeScript PR #62604: Propagate variance reliability (microsoft#1916)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: RyanCavanaugh <[email protected]>
Co-authored-by: Jake Bailey <[email protected]>

* Only export `@typedef` type aliases in modules (microsoft#1999)

* Bump github/codeql-action from 4.31.0 to 4.31.2 in the github-actions group across 1 directory (microsoft#2005)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Hoist @typedef and @import tags to containing scopes that permit them (microsoft#2003)

* Remove concept of "unsupported extensions", clean up test skips  (microsoft#2004)

* Update golangci-lint, fix issues, modernize (microsoft#1981)

* Implement more handling of client capabilities (microsoft#1998)

* Add docs to signature help (microsoft#2009)

Co-authored-by: Copilot <[email protected]>

* Fix unused identifier diags, LSP tag diags (microsoft#2007)

* fix(2015): abstract property created, overshadowing override (microsoft#2016)

* Always check refCount after acquiring lock (microsoft#1986)

* Delete resolver unit tests (microsoft#2008)

* Fix missing parent for `Expression` in `TypeParameterDeclaration` (microsoft#2017)

* Add missing nil check in `getCompletionItemActions` (microsoft#2018)

* Fix crash in find-all-refs on `exports.xxx` in .js file (microsoft#2023)

* Properly include JSX attributes in find-all-references (microsoft#2025)

* Fix crash by removing `getNameFromImportDeclaration` in favor of `Node.Name()` (microsoft#2027)

* Fix losing options from command line in watch mode (microsoft#2024)

* Add issue investigator agent (microsoft#2030)

* Switch 1ESPT pipelines to 1ESPT-AzureLinux3 (microsoft#2031)

* Port inlay hints (microsoft#1705)

* Split "use strict" into separate transformer, fix bugs with prologues (microsoft#2028)

Co-authored-by: Sheetal Nandi <[email protected]>

* Use a more cross-architecture-friendly devcontainer image. (microsoft#2034)

* Fix nil pointer dereference in range formatting (microsoft#1993)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: DanielRosenwasser <[email protected]>
Co-authored-by: jakebailey <[email protected]>
Co-authored-by: Daniel Rosenwasser <[email protected]>
Co-authored-by: Copilot <[email protected]>

* Port missing `checkJs` logic (microsoft#2046)

* Ignore reparsed nodes when determining external module indicator (microsoft#2044)

* Fix various fuzzer-caught crashes in the parser (microsoft#2038)

* Fix moduleDetection for node18, fix __esModule in detect=force (microsoft#2045)

* Fix panic in syncmap on loading nil (microsoft#2056)

* Use accessors on `Node` instead of casts and field accesses (microsoft#2052)

* Add locks on concurrent alias following checker accesses under incremental mode (microsoft#2051)

* Don't add `export` modifier to `JSTypeAliasDeclaration` from `@callback` (microsoft#2063)

* Introduce GetECMALineOfPosition to avoid unused rune counting (microsoft#2065)

* Don't add `export` modifier to `KindCommonJSExport` reparsed nodes (microsoft#2066)

* Fix panic in inlay hints for tuple types (microsoft#2040)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Accurately recognize fourslash test as submodule (microsoft#2068)

* Implement auto-import code actions, port tests and fix some bugs (microsoft#2053)

* Port tsc --init (microsoft#2033)

* Make CheckerPool iteration concurrent by default (microsoft#2070)

* Use Microsoft build of Go in CI (microsoft#2069)

* Detect Windows junctions with GetFileAttributesEx (microsoft#2013)

* Fix CI cache workflow (microsoft#2071)

* Use information from contextual type in hovers/quick info (microsoft#2073)

* fix(2074): No quick info on function and other similar tokens (microsoft#2078)

* Unify locks used on checkers between exclusive pool borrows and EmitResolver scopes (microsoft#2080)

* Port non-baseline diagnostics tests (microsoft#2079)

* Use SkipTrivia instead of GetRangeOfTokenAtPosition where possible (microsoft#2089)

* Move unreachable checks to checker, allowing more AST reuse (microsoft#2067)

* Fix scanning of valid surrogate pairs (microsoft#2032)

* Fix misplaced parentheses in `Checker.isIndirectCall` (microsoft#2093)

* Various agent mode updates (microsoft#2094)

* Handle configuration changes in LSP for 'typescript.*' options. (microsoft#2088)

* Fix nil pointer dereference in getAdjustedLocation for type-only exports (microsoft#2090)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Fix nil pointer dereference in code actions when diagnostic code is nil (microsoft#2091)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: jakebailey <[email protected]>

* Fully resolve LSP client caps to non-pointers, pass by context (microsoft#2095)

* Fix hover on `module.exports` (microsoft#2098)

* Accept and document jsdoc diffs, round 1 (microsoft#1426)

Co-authored-by: Copilot <[email protected]>
Co-authored-by: Jake Bailey <[email protected]>

* Port baseline diagnostics tests (microsoft#2097)

* Clean up disk space in CI before running (microsoft#2103)

Co-authored-by: Copilot <[email protected]>

* Add GOBIN to PATH in CI (microsoft#2105)

* Make client requests type safe, unmarshal (microsoft#2099)

* Display inherited JSDoc documentation in quick info (microsoft#2111)

* Sort failingTests and manualTests in en-US (microsoft#2113)

* fix(2047): Incomplete declaration emit of callback tag with no return tag (microsoft#2100)

* Fix canHaveSyntheticDefault (microsoft#2101)

* Misc fixes (microsoft#2112)

* chore: fix incorrect function name in comment (microsoft#2109)

Signed-off-by: weifangc <[email protected]>

* Fix typedef binding with CJS `exports=` (microsoft#826)

* Provide Program diagnostics as push diags in tsconfig.json (microsoft#2118)

Co-authored-by: Copilot <[email protected]>

* Update dependencies (microsoft#2116)

* Remove copilot-setup-steps env var (microsoft#2124)

* Fix panic on negative parameterIndex in type predicate flow analysis (microsoft#2122)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: RyanCavanaugh <[email protected]>
Co-authored-by: Ryan Cavanaugh <[email protected]>

* Port tests for go to implementation and diff definitions tests (microsoft#2130)

* Partially fix multi-checker diagnostics consistency (microsoft#2134)

* Implement reportStyleChecksAsWarnings (microsoft#2132)

* Include docs on resolved client caps (microsoft#2135)

* Fix dynamic import grammar check (microsoft#2138)

* Refine LSP with our own types, generate more stuff (microsoft#2141)

* Display all symbol meanings in quick info (microsoft#2144)

* Multiproject requests like find all refs, rename and workspace symbols (microsoft#1991)

* Add stringer-alike String methods to non-string LSP enums (microsoft#2148)

* Enable localization (microsoft#2123)

* Port workspace symbols tests (microsoft#2146)

* Update readme, issue template (microsoft#2140)

* Ignore config port (microsoft#1755)

* fix(2157): jsdocfunction param is inferred as implicit any when directly assigned to module.exports (microsoft#2158)

* fixes after merge

* some more fixes

* more errors

* builds

* fmt

* fix nil pointer deref

* fix error messages

---------

Signed-off-by: dependabot[bot] <[email protected]>
Signed-off-by: weifangc <[email protected]>
Co-authored-by: Gabriela Araujo Britto <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: jakebailey <[email protected]>
Co-authored-by: andrewbranch <[email protected]>
Co-authored-by: Andrew Branch <[email protected]>
Co-authored-by: Andrew Branch <[email protected]>
Co-authored-by: Oleksandr T. <[email protected]>
Co-authored-by: Matt Bierner <[email protected]>
Co-authored-by: Ryan Cavanaugh <[email protected]>
Co-authored-by: DanielRosenwasser <[email protected]>
Co-authored-by: Daniel Rosenwasser <[email protected]>
Co-authored-by: Twacqwq <[email protected]>
Co-authored-by: Anders Hejlsberg <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Isabel Duan <[email protected]>
Co-authored-by: John Favret <[email protected]>
Co-authored-by: Wesley Wigham <[email protected]>
Co-authored-by: RyanCavanaugh <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: xu0o0 <[email protected]>
Co-authored-by: Sheetal Nandi <[email protected]>
Co-authored-by: Nathan Shively-Sanders <[email protected]>
Co-authored-by: weifangc <[email protected]>
Co-authored-by: Ryan Cavanaugh <[email protected]>
Co-authored-by: Nathan Whitaker <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Indentation removed in JavaScript output for JSX (minor)

4 participants