Repository navigation
Improve diagnostic for JSX in script files - #65394
Asil Doğan Gümüş (asilgumus) wants to merge 1 commit into
Conversation
When a non-module (script) .tsx file uses an automatic or implicit JSX
transform, TypeScript attempted to resolve the implicit jsx-runtime
import even though script files cannot import modules, and reported
TS2875 "This JSX tag requires the module path '...' to exist, but none
could be found. Make sure you have types for the appropriate package
installed." This was misleading when the package was in fact installed
and resolvable in module mode; the actual problem is that a script
file cannot import the JSX runtime at all.
The checker now reports TS2884 when the implicit JSX runtime import
cannot be resolved from a non-module file:
"This JSX tag requires the module '{0}', which cannot be resolved in a
script file. Add an empty 'export {}' statement to make this file a
module."
Module files keep the existing TS2875 message. Fixed microsoft#64438.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Asil Doğan Gümüş (@asilgumus) please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
There was a problem hiding this comment.
🟡 Changes recommended
The JSX diagnostic incorrectly attributes missing-runtime resolution failures to script status and recommends module conversion that does not fix the absent dependency.
1 open issue
1 file not reviewed
tsc/internal/diagnostics/diagnostics_generated.go: Generated file
What changed in this PR
This PR aims to improve the native TypeScript compiler’s diagnostic for unresolved JSX runtime imports in script files, addressing #64438 with guidance to add export {}.
Changes:
- Selects the new TS2884 diagnostic for script files while retaining TS2875 for modules.
- Adds the diagnostic definition and generated message entries.
- Adds three compiler tests and baselines covering missing and resolvable runtimes, and updates two existing diagnostic baselines.
| File | Description |
|---|---|
tsc/testdata/tests/cases/compiler/jsxScriptFileResolvableImportSource.tsx |
Tests a script with available runtime declarations. |
tsc/testdata/tests/cases/compiler/jsxScriptFile.tsx |
Tests a script with a missing runtime. |
tsc/testdata/tests/cases/compiler/jsxImportSourceModuleMissing.tsx |
Tests a module with a missing runtime. |
tsc/testdata/baselines/reference/compiler/jsxScriptFileResolvableImportSource.types |
Records resolvable-script types. |
tsc/testdata/baselines/reference/compiler/jsxScriptFileResolvableImportSource.symbols |
Records resolved JSX symbols. |
tsc/testdata/baselines/reference/compiler/jsxScriptFileResolvableImportSource.js |
Records resolvable-script emit. |
tsc/testdata/baselines/reference/compiler/jsxScriptFile.types |
Records missing-runtime script types. |
tsc/testdata/baselines/reference/compiler/jsxScriptFile.symbols |
Records missing-runtime script symbols. |
tsc/testdata/baselines/reference/compiler/jsxScriptFile.js |
Records missing-runtime script emit. |
tsc/testdata/baselines/reference/compiler/jsxScriptFile.errors.txt |
Expects TS2884 and TS7026. |
tsc/testdata/baselines/reference/compiler/jsxImportSourceModuleMissing.types |
Records missing-runtime module types. |
tsc/testdata/baselines/reference/compiler/jsxImportSourceModuleMissing.symbols |
Records missing-runtime module symbols. |
tsc/testdata/baselines/reference/compiler/jsxImportSourceModuleMissing.js |
Records module emit and runtime import. |
tsc/testdata/baselines/reference/compiler/jsxImportSourceModuleMissing.errors.txt |
Preserves TS2875 and TS7026 expectations. |
tsc/testdata/baselines/reference/compiler/commentsOnJSXExpressionsArePreserved(jsx=react-jsxdev,module=commonjs,moduledetection=legacy).errors.txt |
Updates development-runtime errors to TS2884. |
tsc/testdata/baselines/reference/compiler/commentsOnJSXExpressionsArePreserved(jsx=react-jsx,module=commonjs,moduledetection=legacy).errors.txt |
Updates automatic-runtime errors to TS2884. |
tsc/internal/diagnostics/diagnostics_generated.go |
Registers the generated TS2884 message. |
tsc/internal/diagnostics/diagnosticMessages.json |
Defines TS2884 and its guidance. |
tsc/internal/diagnostics/diagnosticMessages.generated.json |
Adds the generated message entry. |
tsc/internal/checker/jsx.go |
Selects the diagnostic based on module status. |
Review effort: Balanced
| if !ast.IsExternalOrCommonJSModule(file) { | ||
| errorMessage = diagnostics.This_JSX_tag_requires_the_module_0_which_cannot_be_resolved_in_a_script_file_Add_an_empty_export_statement_to_make_this_file_a_module |
|
Why did you choose to fix this specific issue? |

Problem
When a
.tsxfile is a script file (non-module) — i.e. it contains no top-levelimport/export(orimport.meta), and it is not forced to be a module — the checker still resolves the (compiler-injected) implicit JSX runtime import (<jsxImportSource>/jsx-runtime) that TypeScript adds to every.tsxfile with an automatic/implicit JSX factory. That resolution fails, and the checker reported:For example, with
// @jsx: react-jsx,// @jsxImportSource: solid-js,// @moduleDetection: legacyand even with the package's type declarations present, a file containing just:reports TS2875, which claims the package or its types are missing. The real reason is that the file is a script and script files cannot import modules, so the implicit
jsx-runtimeimport cannot be resolved at all. Addingexport {}(as reported in #64438) resolves it. Fixes #64438.Root cause
getJsxNamespaceContainerForImplicitImportintsc/internal/checker/jsx.goresolves the implicit JSX runtime import and, on failure, always reports TS2875 — without taking the importing file's module-ness into account. UndermoduleDetection: legacy(or when JSX does not force module-ness), a script file will never be able to resolve that import, so TS2875's "Make sure you have types for the appropriate package installed" advice was wrong/misleading.Fix
When the implicit JSX runtime import cannot be resolved and the importing file is not a module (
!ast.IsExternalOrCommonJSModule(file)), the checker now reports a new, targeted diagnostic TS2884:Behavior summary:
New message source is
tsc/internal/diagnosticMessages.json;diagnostics_generated.go,diagnosticMessages.generated.json, andloc_generated.goare regenerated withnpx hereby generate:diagnostics.Tests / baselines
tsc/testdata/tests/cases/compiler/jsxScriptFile.tsx— script file withjsxImportSourceand no resolvable runtime → expects TS2884 + TS7026.tsc/testdata/tests/cases/compiler/jsxImportSourceModuleMissing.tsx— module file with missing runtime → expects the unchanged TS2875 (guards the existing behavior).tsc/testdata/tests/cases/compiler/jsxScriptFileResolvableImportSource.tsx— script file whose runtime does resolve → expects no errors (guards against over-reporting).commentsOnJSXExpressionsArePreserved(jsx=react-jsx,module=commonjs,moduledetection=legacy).errors.txtand thereact-jsxdevsibling (script files under legacy module detection now report TS2884 instead of TS2875).go test ./internal/testrunnerpasses locally with these baselines.Verification
Built
tsc.exefrom source; before/after:Note: there are other open PRs for this issue; this one targets the current
main(native Go compiler) and follows its diagnostic-generation flow.🤖 Generated with Claude Code