Skip to content

Commit 4791050

Browse files
authored
Merge pull request microsoft#41330 from weswigham/jsx-import-source-as-auto-import
Auto-include types for the jsx import source in the new jsx transforms
2 parents e044b56 + 1e1ae30 commit 4791050

23 files changed

+1517
-20
lines changed

src/compiler/checker.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25135,14 +25135,42 @@ namespace ts {
2513525135
return links.resolvedSymbol;
2513625136
}
2513725137

25138+
function getJsxNamespaceContainerForImplicitImport(location: Node | undefined): Symbol | undefined {
25139+
const file = location && getSourceFileOfNode(location);
25140+
const links = file && getNodeLinks(file);
25141+
if (links && links.jsxImplicitImportContainer === false) {
25142+
return undefined;
25143+
}
25144+
if (links && links.jsxImplicitImportContainer) {
25145+
return links.jsxImplicitImportContainer;
25146+
}
25147+
const runtimeImportSpecifier = getJSXRuntimeImport(getJSXImplicitImportBase(compilerOptions, file), compilerOptions);
25148+
if (!runtimeImportSpecifier) {
25149+
return undefined;
25150+
}
25151+
const isClassic = getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Classic;
25152+
const errorMessage = isClassic
25153+
? Diagnostics.Cannot_find_module_0_Did_you_mean_to_set_the_moduleResolution_option_to_node_or_to_add_aliases_to_the_paths_option
25154+
: Diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations;
25155+
const mod = resolveExternalModule(location!, runtimeImportSpecifier, errorMessage, location!);
25156+
const result = mod && mod !== unknownSymbol ? getMergedSymbol(resolveSymbol(mod)) : undefined;
25157+
if (links) {
25158+
links.jsxImplicitImportContainer = result || false;
25159+
}
25160+
return result;
25161+
}
25162+
2513825163
function getJsxNamespaceAt(location: Node | undefined): Symbol {
2513925164
const links = location && getNodeLinks(location);
2514025165
if (links && links.jsxNamespace) {
2514125166
return links.jsxNamespace;
2514225167
}
2514325168
if (!links || links.jsxNamespace !== false) {
2514425169
const namespaceName = getJsxNamespace(location);
25145-
const resolvedNamespace = resolveName(location, namespaceName, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined, namespaceName, /*isUse*/ false);
25170+
let resolvedNamespace = resolveName(location, namespaceName, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined, namespaceName, /*isUse*/ false);
25171+
if (!resolvedNamespace || resolvedNamespace === unknownSymbol) {
25172+
resolvedNamespace = getJsxNamespaceContainerForImplicitImport(location);
25173+
}
2514625174
if (resolvedNamespace) {
2514725175
const candidate = resolveSymbol(getSymbol(getExportsOfSymbol(resolveSymbol(resolvedNamespace)), JsxNames.JSX, SymbolFlags.Namespace));
2514825176
if (candidate && candidate !== unknownSymbol) {
@@ -25151,9 +25179,9 @@ namespace ts {
2515125179
}
2515225180
return candidate;
2515325181
}
25154-
if (links) {
25155-
links.jsxNamespace = false;
25156-
}
25182+
}
25183+
if (links) {
25184+
links.jsxNamespace = false;
2515725185
}
2515825186
}
2515925187
// JSX global fallback

src/compiler/commandLineParser.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ namespace ts {
382382
type: jsxOptionMap,
383383
affectsSourceFile: true,
384384
affectsEmit: true,
385+
affectsModuleResolution: true,
385386
paramType: Diagnostics.KIND,
386387
showInSimplifiedHelpView: true,
387388
category: Diagnostics.Basic_Options,
@@ -802,6 +803,9 @@ namespace ts {
802803
{
803804
name: "jsxImportSource",
804805
type: "string",
806+
affectsSemanticDiagnostics: true,
807+
affectsEmit: true,
808+
affectsModuleResolution: true,
805809
category: Diagnostics.Advanced_Options,
806810
description: Diagnostics.Specify_the_module_specifier_to_be_used_to_import_the_jsx_and_jsxs_factory_functions_from_eg_react
807811
},

src/compiler/program.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2201,6 +2201,15 @@ namespace ts {
22012201
: b.kind === SyntaxKind.StringLiteral && a.text === b.text;
22022202
}
22032203

2204+
function createSyntheticImport(text: string, file: SourceFile) {
2205+
const externalHelpersModuleReference = factory.createStringLiteral(text);
2206+
const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference);
2207+
addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper);
2208+
setParent(externalHelpersModuleReference, importDecl);
2209+
setParent(importDecl, file);
2210+
return externalHelpersModuleReference;
2211+
}
2212+
22042213
function collectExternalModuleReferences(file: SourceFile): void {
22052214
if (file.imports) {
22062215
return;
@@ -2216,16 +2225,17 @@ namespace ts {
22162225

22172226
// If we are importing helpers, we need to add a synthetic reference to resolve the
22182227
// helpers library.
2219-
if (options.importHelpers
2220-
&& (options.isolatedModules || isExternalModuleFile)
2228+
if ((options.isolatedModules || isExternalModuleFile)
22212229
&& !file.isDeclarationFile) {
2222-
// synthesize 'import "tslib"' declaration
2223-
const externalHelpersModuleReference = factory.createStringLiteral(externalHelpersModuleNameText);
2224-
const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference);
2225-
addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper);
2226-
setParent(externalHelpersModuleReference, importDecl);
2227-
setParent(importDecl, file);
2228-
imports = [externalHelpersModuleReference];
2230+
if (options.importHelpers) {
2231+
// synthesize 'import "tslib"' declaration
2232+
imports = [createSyntheticImport(externalHelpersModuleNameText, file)];
2233+
}
2234+
const jsxImport = getJSXRuntimeImport(getJSXImplicitImportBase(options, file), options);
2235+
if (jsxImport) {
2236+
// synthesize `import "base/jsx-runtime"` declaration
2237+
(imports ||= []).push(createSyntheticImport(jsxImport, file));
2238+
}
22292239
}
22302240

22312241
for (const node of file.statements) {

src/compiler/transformers/jsx.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ namespace ts {
4242
function getImplicitImportForName(name: string) {
4343
const importSource = name === "createElement"
4444
? currentFileState.importSpecifier!
45-
: `${currentFileState.importSpecifier}/${compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime" : "jsx-runtime"}`;
45+
: getJSXRuntimeImport(currentFileState.importSpecifier, compilerOptions)!;
4646
const existing = currentFileState.utilizedImplicitRuntimeImports?.get(importSource)?.get(name);
4747
if (existing) {
4848
return existing.name;

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4843,6 +4843,7 @@ namespace ts {
48434843
resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference
48444844
switchTypes?: Type[]; // Cached array of switch case expression types
48454845
jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node
4846+
jsxImplicitImportContainer?: Symbol | false; // Resolved module symbol the implicit jsx import of this file should refer to
48464847
contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive
48474848
deferredNodes?: ESMap<NodeId, Node>; // Set of nodes whose checking has been deferred
48484849
capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement

src/compiler/utilities.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6009,8 +6009,8 @@ namespace ts {
60096009
return jsx === JsxEmit.React || jsx === JsxEmit.ReactJSX || jsx === JsxEmit.ReactJSXDev;
60106010
}
60116011

6012-
export function getJSXImplicitImportBase(compilerOptions: CompilerOptions, file: SourceFile): string | undefined {
6013-
const jsxImportSourcePragmas = file.pragmas.get("jsximportsource");
6012+
export function getJSXImplicitImportBase(compilerOptions: CompilerOptions, file?: SourceFile): string | undefined {
6013+
const jsxImportSourcePragmas = file?.pragmas.get("jsximportsource");
60146014
const jsxImportSourcePragma = isArray(jsxImportSourcePragmas) ? jsxImportSourcePragmas[0] : jsxImportSourcePragmas;
60156015
return compilerOptions.jsx === JsxEmit.ReactJSX ||
60166016
compilerOptions.jsx === JsxEmit.ReactJSXDev ||
@@ -6020,6 +6020,10 @@ namespace ts {
60206020
undefined;
60216021
}
60226022

6023+
export function getJSXRuntimeImport(base: string | undefined, options: CompilerOptions) {
6024+
return base ? `${base}/${options.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime" : "jsx-runtime"}` : undefined;
6025+
}
6026+
60236027
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
60246028
let seenAsterisk = false;
60256029
for (let i = 0; i < str.length; i++) {

src/testRunner/unittests/tscWatch/incremental.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,5 +276,33 @@ export interface A {
276276
],
277277
modifyFs: host => host.deleteFile(`${project}/globals.d.ts`)
278278
});
279+
280+
const jsxImportSourceOptions = { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" };
281+
const jsxLibraryContent = `export namespace JSX {
282+
interface Element {}
283+
interface IntrinsicElements {
284+
div: {
285+
propA?: boolean;
286+
};
287+
}
288+
}
289+
export function jsx(...args: any[]): void;
290+
export function jsxs(...args: any[]): void;
291+
export const Fragment: unique symbol;
292+
`;
293+
294+
verifyIncrementalWatchEmit({
295+
subScenario: "jsxImportSource option changed",
296+
files: () => [
297+
{ path: libFile.path, content: libContent },
298+
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
299+
{ path: `${project}/node_modules/react/package.json`, content: JSON.stringify({ name: "react", version: "0.0.1" }) },
300+
{ path: `${project}/node_modules/preact/jsx-runtime/index.d.ts`, content: jsxLibraryContent.replace("propA", "propB") },
301+
{ path: `${project}/node_modules/preact/package.json`, content: JSON.stringify({ name: "preact", version: "0.0.1" }) },
302+
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
303+
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
304+
],
305+
modifyFs: host => host.writeFile(configFile.path, JSON.stringify({ compilerOptions: { ...jsxImportSourceOptions, jsxImportSource: "preact" } }))
306+
});
279307
});
280308
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformCustomImport.tsx(2,11): error TS2307: Cannot find module 'preact/jsx-runtime' or its corresponding type declarations.
2+
3+
4+
==== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformCustomImport.tsx (1 errors) ====
5+
/// <reference path="/.lib/react16.d.ts" />
6+
const a = <>
7+
~~
8+
!!! error TS2307: Cannot find module 'preact/jsx-runtime' or its corresponding type declarations.
9+
<p></p>
10+
text
11+
<div className="foo"></div>
12+
</>
13+
14+
export {};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformCustomImport.tsx(2,11): error TS2307: Cannot find module 'preact/jsx-dev-runtime' or its corresponding type declarations.
2+
3+
4+
==== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformCustomImport.tsx (1 errors) ====
5+
/// <reference path="/.lib/react16.d.ts" />
6+
const a = <>
7+
~~
8+
!!! error TS2307: Cannot find module 'preact/jsx-dev-runtime' or its corresponding type declarations.
9+
<p></p>
10+
text
11+
<div className="foo"></div>
12+
</>
13+
14+
export {};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
tests/cases/conformance/jsx/jsxs/preact.tsx(3,11): error TS2307: Cannot find module 'preact/jsx-runtime' or its corresponding type declarations.
2+
3+
4+
==== tests/cases/conformance/jsx/jsxs/react.tsx (0 errors) ====
5+
/// <reference path="/.lib/react16.d.ts" />
6+
/* @jsxImportSource react */
7+
import "./preact";
8+
const a = <>
9+
<p></p>
10+
text
11+
<div className="foo"></div>
12+
</>
13+
14+
export {};
15+
==== tests/cases/conformance/jsx/jsxs/preact.tsx (1 errors) ====
16+
/// <reference path="/.lib/react16.d.ts" />
17+
/* @jsxImportSource preact */
18+
const a = <>
19+
~~
20+
!!! error TS2307: Cannot find module 'preact/jsx-runtime' or its corresponding type declarations.
21+
<p></p>
22+
text
23+
<div className="foo"></div>
24+
</>
25+
26+
export {};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
tests/cases/conformance/jsx/jsxs/preact.tsx(3,11): error TS2307: Cannot find module 'preact/jsx-dev-runtime' or its corresponding type declarations.
2+
3+
4+
==== tests/cases/conformance/jsx/jsxs/react.tsx (0 errors) ====
5+
/// <reference path="/.lib/react16.d.ts" />
6+
/* @jsxImportSource react */
7+
import "./preact";
8+
const a = <>
9+
<p></p>
10+
text
11+
<div className="foo"></div>
12+
</>
13+
14+
export {};
15+
==== tests/cases/conformance/jsx/jsxs/preact.tsx (1 errors) ====
16+
/// <reference path="/.lib/react16.d.ts" />
17+
/* @jsxImportSource preact */
18+
const a = <>
19+
~~
20+
!!! error TS2307: Cannot find module 'preact/jsx-dev-runtime' or its corresponding type declarations.
21+
<p></p>
22+
text
23+
<div className="foo"></div>
24+
</>
25+
26+
export {};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx(3,11): error TS2307: Cannot find module 'preact/jsx-runtime' or its corresponding type declarations.
2+
3+
4+
==== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx (1 errors) ====
5+
/// <reference path="/.lib/react16.d.ts" />
6+
const props = { answer: 42 }
7+
const a = <div key="foo" {...props}>text</div>;
8+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9+
!!! error TS2307: Cannot find module 'preact/jsx-runtime' or its corresponding type declarations.
10+
const b = <div {...props} key="bar">text</div>;
11+
12+
export {};
13+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx(3,11): error TS2307: Cannot find module 'preact/jsx-dev-runtime' or its corresponding type declarations.
2+
3+
4+
==== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx (1 errors) ====
5+
/// <reference path="/.lib/react16.d.ts" />
6+
const props = { answer: 42 }
7+
const a = <div key="foo" {...props}>text</div>;
8+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9+
!!! error TS2307: Cannot find module 'preact/jsx-dev-runtime' or its corresponding type declarations.
10+
const b = <div {...props} key="bar">text</div>;
11+
12+
export {};
13+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
tests/cases/conformance/jsx/jsxs/preact.tsx(4,11): error TS2307: Cannot find module 'preact/jsx-runtime' or its corresponding type declarations.
2+
3+
4+
==== tests/cases/conformance/jsx/jsxs/react.tsx (0 errors) ====
5+
/// <reference path="/.lib/react16.d.ts" />
6+
/* @jsxImportSource react */
7+
import "./preact";
8+
const props2 = { answer: 42 }
9+
const a2 = <div key="foo" {...props2}>text</div>;
10+
const b2 = <div {...props2} key="bar">text</div>;
11+
12+
export {};
13+
14+
==== tests/cases/conformance/jsx/jsxs/preact.tsx (1 errors) ====
15+
/// <reference path="/.lib/react16.d.ts" />
16+
/* @jsxImportSource preact */
17+
const props = { answer: 42 }
18+
const a = <div key="foo" {...props}>text</div>;
19+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20+
!!! error TS2307: Cannot find module 'preact/jsx-runtime' or its corresponding type declarations.
21+
const b = <div {...props} key="bar">text</div>;
22+
23+
export {};
24+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
tests/cases/conformance/jsx/jsxs/preact.tsx(4,11): error TS2307: Cannot find module 'preact/jsx-dev-runtime' or its corresponding type declarations.
2+
3+
4+
==== tests/cases/conformance/jsx/jsxs/react.tsx (0 errors) ====
5+
/// <reference path="/.lib/react16.d.ts" />
6+
/* @jsxImportSource react */
7+
import "./preact";
8+
const props2 = { answer: 42 }
9+
const a2 = <div key="foo" {...props2}>text</div>;
10+
const b2 = <div {...props2} key="bar">text</div>;
11+
12+
export {};
13+
14+
==== tests/cases/conformance/jsx/jsxs/preact.tsx (1 errors) ====
15+
/// <reference path="/.lib/react16.d.ts" />
16+
/* @jsxImportSource preact */
17+
const props = { answer: 42 }
18+
const a = <div key="foo" {...props}>text</div>;
19+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20+
!!! error TS2307: Cannot find module 'preact/jsx-dev-runtime' or its corresponding type declarations.
21+
const b = <div {...props} key="bar">text</div>;
22+
23+
export {};
24+

0 commit comments

Comments
 (0)