Skip to content

Commit 06affa6

Browse files
committed
Resolve first identifier of the jsxFactory as part of type check
1 parent f7bac98 commit 06affa6

10 files changed

+548
-119
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ namespace ts {
335335
});
336336

337337
let jsxElementType: Type;
338+
let _jsxNamespace: string;
338339
/** Things we lazy load from the JSX namespace */
339340
const jsxTypes = createMap<Type>();
340341
const JsxNames = {
@@ -372,6 +373,22 @@ namespace ts {
372373

373374
return checker;
374375

376+
function getJsxNamespace(): string {
377+
if (_jsxNamespace === undefined) {
378+
_jsxNamespace = "React";
379+
if (compilerOptions.jsxFactory) {
380+
const jsxEntity = host.getJsxFactoryEntity();
381+
if (jsxEntity) {
382+
_jsxNamespace = getFirstIdentifier(jsxEntity).text;
383+
}
384+
}
385+
else if (compilerOptions.reactNamespace) {
386+
_jsxNamespace = compilerOptions.reactNamespace;
387+
}
388+
}
389+
return _jsxNamespace;
390+
}
391+
375392
function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) {
376393
// Ensure we have all the type information in place for this file so that all the
377394
// emitter questions of this resolver will return the right information.
@@ -11337,10 +11354,10 @@ namespace ts {
1133711354
function checkJsxOpeningLikeElement(node: JsxOpeningLikeElement) {
1133811355
checkGrammarJsxElement(node);
1133911356
checkJsxPreconditions(node);
11340-
// The reactNamespace symbol should be marked as 'used' so we don't incorrectly elide its import. And if there
11341-
// is no reactNamespace symbol in scope when targeting React emit, we should issue an error.
11357+
// The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import.
11358+
// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
1134211359
const reactRefErr = compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
11343-
const reactNamespace = compilerOptions.reactNamespace ? compilerOptions.reactNamespace : "React";
11360+
const reactNamespace = getJsxNamespace();
1134411361
const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace);
1134511362
if (reactSym) {
1134611363
// Mark local symbol as referenced here because it might not have been marked

src/compiler/program.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ namespace ts {
329329
// Map storing if there is emit blocking diagnostics for given input
330330
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);
331331

332+
// ReactNamespace and jsxFactory information
333+
let jsxFactoryEntity: EntityName;
334+
332335
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[];
333336
if (host.resolveModuleNames) {
334337
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => {
@@ -421,7 +424,8 @@ namespace ts {
421424
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
422425
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
423426
isSourceFileFromExternalLibrary,
424-
dropDiagnosticsProducingTypeChecker
427+
dropDiagnosticsProducingTypeChecker,
428+
getJsxFactoryEntity: () => jsxFactoryEntity
425429
};
426430

427431
verifyCompilerOptions();
@@ -1674,7 +1678,8 @@ namespace ts {
16741678
if (options.reactNamespace) {
16751679
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory"));
16761680
}
1677-
if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
1681+
jsxFactoryEntity = parseIsolatedEntityName(options.jsxFactory, languageVersion);
1682+
if (!jsxFactoryEntity) {
16781683
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory));
16791684
}
16801685
}

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,6 +2177,7 @@ namespace ts {
21772177
getTypeChecker(): TypeChecker;
21782178

21792179
/* @internal */ getCommonSourceDirectory(): string;
2180+
/* @internal */ getJsxFactoryEntity(): EntityName;
21802181

21812182
// For testing purposes only. Should not be used by any other consumers (including the
21822183
// language service).
@@ -2250,6 +2251,7 @@ namespace ts {
22502251
/* @internal */
22512252
export interface TypeCheckerHost {
22522253
getCompilerOptions(): CompilerOptions;
2254+
getJsxFactoryEntity(): EntityName;
22532255

22542256
getSourceFiles(): SourceFile[];
22552257
getSourceFile(fileName: string): SourceFile;

tests/baselines/reference/jsxFactoryIdentifier.errors.txt

Lines changed: 0 additions & 57 deletions
This file was deleted.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
=== tests/cases/compiler/Element.ts ===
2+
3+
declare namespace JSX {
4+
>JSX : Symbol(JSX, Decl(Element.ts, 0, 0))
5+
6+
interface Element {
7+
>Element : Symbol(Element, Decl(Element.ts, 1, 23))
8+
9+
name: string;
10+
>name : Symbol(Element.name, Decl(Element.ts, 2, 23))
11+
12+
isIntrinsic: boolean;
13+
>isIntrinsic : Symbol(Element.isIntrinsic, Decl(Element.ts, 3, 21))
14+
15+
isCustomElement: boolean;
16+
>isCustomElement : Symbol(Element.isCustomElement, Decl(Element.ts, 4, 29))
17+
18+
toString(renderId?: number): string;
19+
>toString : Symbol(Element.toString, Decl(Element.ts, 5, 33))
20+
>renderId : Symbol(renderId, Decl(Element.ts, 6, 17))
21+
22+
bindDOM(renderId?: number): number;
23+
>bindDOM : Symbol(Element.bindDOM, Decl(Element.ts, 6, 44))
24+
>renderId : Symbol(renderId, Decl(Element.ts, 7, 16))
25+
26+
resetComponent(): void;
27+
>resetComponent : Symbol(Element.resetComponent, Decl(Element.ts, 7, 43))
28+
29+
instantiateComponents(renderId?: number): number;
30+
>instantiateComponents : Symbol(Element.instantiateComponents, Decl(Element.ts, 8, 31))
31+
>renderId : Symbol(renderId, Decl(Element.ts, 9, 30))
32+
33+
props: any;
34+
>props : Symbol(Element.props, Decl(Element.ts, 9, 57))
35+
}
36+
}
37+
export namespace Element {
38+
>Element : Symbol(Element, Decl(Element.ts, 12, 1))
39+
40+
export function isElement(el: any): el is JSX.Element {
41+
>isElement : Symbol(isElement, Decl(Element.ts, 13, 26))
42+
>el : Symbol(el, Decl(Element.ts, 14, 30))
43+
>el : Symbol(el, Decl(Element.ts, 14, 30))
44+
>JSX : Symbol(JSX, Decl(Element.ts, 0, 0))
45+
>Element : Symbol(JSX.Element, Decl(Element.ts, 1, 23))
46+
47+
return el.markAsChildOfRootElement !== undefined;
48+
>el : Symbol(el, Decl(Element.ts, 14, 30))
49+
>undefined : Symbol(undefined)
50+
}
51+
52+
export function createElement(args: any[]) {
53+
>createElement : Symbol(createElement, Decl(Element.ts, 16, 5))
54+
>args : Symbol(args, Decl(Element.ts, 18, 34))
55+
56+
return {
57+
}
58+
}
59+
}
60+
61+
export let createElement = Element.createElement;
62+
>createElement : Symbol(createElement, Decl(Element.ts, 25, 10))
63+
>Element.createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
64+
>Element : Symbol(Element, Decl(Element.ts, 12, 1))
65+
>createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
66+
67+
function toCamelCase(text: string): string {
68+
>toCamelCase : Symbol(toCamelCase, Decl(Element.ts, 25, 49))
69+
>text : Symbol(text, Decl(Element.ts, 27, 21))
70+
71+
return text[0].toLowerCase() + text.substring(1);
72+
>text[0].toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
73+
>text : Symbol(text, Decl(Element.ts, 27, 21))
74+
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
75+
>text.substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --))
76+
>text : Symbol(text, Decl(Element.ts, 27, 21))
77+
>substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --))
78+
}
79+
80+
=== tests/cases/compiler/test.tsx ===
81+
import { Element} from './Element';
82+
>Element : Symbol(Element, Decl(test.tsx, 0, 8))
83+
84+
let createElement = Element.createElement;
85+
>createElement : Symbol(createElement, Decl(test.tsx, 1, 3))
86+
>Element.createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
87+
>Element : Symbol(Element, Decl(test.tsx, 0, 8))
88+
>createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
89+
90+
let c: {
91+
>c : Symbol(c, Decl(test.tsx, 2, 3))
92+
93+
a?: {
94+
>a : Symbol(a, Decl(test.tsx, 2, 8))
95+
96+
b: string
97+
>b : Symbol(b, Decl(test.tsx, 3, 6))
98+
}
99+
};
100+
101+
class A {
102+
>A : Symbol(A, Decl(test.tsx, 6, 2))
103+
104+
view() {
105+
>view : Symbol(A.view, Decl(test.tsx, 8, 9))
106+
107+
return [
108+
<meta content="helloworld"></meta>,
109+
>meta : Symbol(unknown)
110+
>content : Symbol(unknown)
111+
>meta : Symbol(unknown)
112+
113+
<meta content={c.a!.b}></meta>
114+
>meta : Symbol(unknown)
115+
>content : Symbol(unknown)
116+
>c.a!.b : Symbol(b, Decl(test.tsx, 3, 6))
117+
>c.a : Symbol(a, Decl(test.tsx, 2, 8))
118+
>c : Symbol(c, Decl(test.tsx, 2, 3))
119+
>a : Symbol(a, Decl(test.tsx, 2, 8))
120+
>b : Symbol(b, Decl(test.tsx, 3, 6))
121+
>meta : Symbol(unknown)
122+
123+
];
124+
}
125+
}

0 commit comments

Comments
 (0)