Skip to content

Use first @jsx/@jsxfrag pragma to match tsc behavior#4157

Open
Copilot wants to merge 3 commits into
mainfrom
copilot/fix-jsx-pragmas-issue
Open

Use first @jsx/@jsxfrag pragma to match tsc behavior#4157
Copilot wants to merge 3 commits into
mainfrom
copilot/fix-jsx-pragmas-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 1, 2026

When a file contains multiple /** @jsx */ factory pragmas, tsc resolves to the first one but tsgo resolved to the last, producing divergent emit:

/** @jsx h */
/** @jsx g */
declare const h: any, g: any;
export const x = <div/>;
// tsc:  export const x = h("div", null);
// tsgo: export const x = g("div", null);  // before this change

The previous ast.GetPragmaFromSourceFile was "last one wins", which is correct for jsximportsource/jsxruntime (TypeScript reads length - 1) but wrong for the jsx/jsxfrag factory pragmas, where TypeScript reads index 0.

Changes

  • internal/ast/utilities.go: add GetFirstPragmaFromSourceFile (first matching pragma, matching tsc pragmas[0]) and rename GetPragmaFromSourceFile to GetLastPragmaFromSourceFile (last matching pragma, matching tsc pragmas[length - 1]) for consistent naming, each documenting the tsc indexing it mirrors.
  • internal/checker/jsx.go: use GetFirstPragmaFromSourceFile for all jsx/jsxfrag reads — factory resolution in getLocalJsxNamespace, the fragment branch of getJsxNamespace, and getJsxFragmentFactoryEntity, plus the checkJsxFragment existence checks. jsximportsource/jsxruntime in getJSXImplicitImportBase use GetLastPragmaFromSourceFile. Every call site was cross-checked against tsc behavior.
  • testdata/tests/cases/compiler/jsxMultiplePragmas.tsx: regression test (the repro above) with baselines.

Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix handling of multiple JSX pragmas in single file Use first @jsx/@jsxfrag pragma to match tsc behavior Jun 1, 2026
Copilot AI requested a review from jakebailey June 1, 2026 18:09
Comment thread internal/ast/utilities.go Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@copilot Now we have two funcs that do the same thing but differently. Their naming needs to be made consistent, and also both funcs and all users need to be cross-checked to make sure they match tsc behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Renamed for consistency: GetFirstPragmaFromSourceFile (first wins) and GetLastPragmaFromSourceFile (formerly GetPragmaFromSourceFile, last wins), each documenting the matching tsc indexing (pragmas[0] vs pragmas[length-1]). Cross-checked every call site against tsc: jsx/jsxfrag reads (factory resolution in getLocalJsxNamespace/getJsxNamespace/getJsxFragmentFactoryEntity, plus the checkJsxFragment existence checks) now use first; jsximportsource/jsxruntime in getJSXImplicitImportBase use last. (00f0c8c was the rename commit)

…sistency

Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Copilot AI requested a review from jakebailey June 1, 2026 19:31
Copy link
Copy Markdown
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.

weird but ok

@jakebailey jakebailey marked this pull request as ready for review June 1, 2026 19:47
Copilot AI review requested due to automatic review settings June 1, 2026 19:47
Copy link
Copy Markdown
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

Aligns tsgo’s JSX pragma resolution with TypeScript/tsc by making @jsx/@jsxfrag use the first matching pragma in a file (instead of “last one wins”), while keeping @jsximportsource/@jsxruntime as last matching pragma. This reduces emit divergence for files containing multiple JSX factory pragmas.

Changes:

  • Added GetFirstPragmaFromSourceFile and renamed the prior “last one wins” helper to GetLastPragmaFromSourceFile for explicit, behavior-revealing naming.
  • Updated JSX checking/namespace/factory resolution to read @jsx/@jsxfrag via the “first pragma wins” helper.
  • Added a compiler regression test plus reference baselines proving the emitted factory matches tsc (h(...) from the first pragma).

Reviewed changes

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

Show a summary per file
File Description
internal/ast/utilities.go Introduces explicit first/last pragma helpers and uses “last” for jsximportsource/jsxruntime lookups.
internal/checker/jsx.go Switches jsx/jsxfrag resolution and related checks to use the first pragma for tsc parity.
testdata/tests/cases/compiler/jsxMultiplePragmas.tsx New regression test covering multiple @jsx pragmas in one file.
testdata/baselines/reference/compiler/jsxMultiplePragmas.js Reference emit baseline demonstrating factory uses the first pragma (h).
testdata/baselines/reference/compiler/jsxMultiplePragmas.types Reference type baseline for the new test case.
testdata/baselines/reference/compiler/jsxMultiplePragmas.symbols Reference symbol baseline for the new test case.
testdata/baselines/reference/compiler/jsxMultiplePragmas.errors.txt Reference error baseline for the new test case.

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.

With multiple /** @jsx */ pragmas in one file, tsc uses the first and tsgo uses the last

3 participants