Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Resolve first identifier of the jsxFactory as part of type check
  • Loading branch information
sheetalkamat committed Nov 9, 2016
commit 06affa6672bc3b11e72dff76b986983fc7f2e72d
23 changes: 20 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ namespace ts {
});

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

return checker;

function getJsxNamespace(): string {
if (_jsxNamespace === undefined) {
_jsxNamespace = "React";
if (compilerOptions.jsxFactory) {
const jsxEntity = host.getJsxFactoryEntity();
if (jsxEntity) {
_jsxNamespace = getFirstIdentifier(jsxEntity).text;
}
}
else if (compilerOptions.reactNamespace) {
_jsxNamespace = compilerOptions.reactNamespace;
}
}
return _jsxNamespace;
}

function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) {
// Ensure we have all the type information in place for this file so that all the
// emitter questions of this resolver will return the right information.
Expand Down Expand Up @@ -11337,10 +11354,10 @@ namespace ts {
function checkJsxOpeningLikeElement(node: JsxOpeningLikeElement) {
checkGrammarJsxElement(node);
checkJsxPreconditions(node);
// The reactNamespace symbol should be marked as 'used' so we don't incorrectly elide its import. And if there
// is no reactNamespace symbol in scope when targeting React emit, we should issue an error.
// The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import.
// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
const reactRefErr = compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
const reactNamespace = compilerOptions.reactNamespace ? compilerOptions.reactNamespace : "React";
const reactNamespace = getJsxNamespace();
const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace);
if (reactSym) {
// Mark local symbol as referenced here because it might not have been marked
Expand Down
9 changes: 7 additions & 2 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,9 @@ namespace ts {
// Map storing if there is emit blocking diagnostics for given input
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);

// ReactNamespace and jsxFactory information
let jsxFactoryEntity: EntityName;

let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[];
if (host.resolveModuleNames) {
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => {
Expand Down Expand Up @@ -421,7 +424,8 @@ namespace ts {
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
isSourceFileFromExternalLibrary,
dropDiagnosticsProducingTypeChecker
dropDiagnosticsProducingTypeChecker,
getJsxFactoryEntity: () => jsxFactoryEntity
};

verifyCompilerOptions();
Expand Down Expand Up @@ -1674,7 +1678,8 @@ namespace ts {
if (options.reactNamespace) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory"));
}
if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
jsxFactoryEntity = parseIsolatedEntityName(options.jsxFactory, languageVersion);
if (!jsxFactoryEntity) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory));
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2177,6 +2177,7 @@ namespace ts {
getTypeChecker(): TypeChecker;

/* @internal */ getCommonSourceDirectory(): string;
/* @internal */ getJsxFactoryEntity(): EntityName;

// For testing purposes only. Should not be used by any other consumers (including the
// language service).
Expand Down Expand Up @@ -2250,6 +2251,7 @@ namespace ts {
/* @internal */
export interface TypeCheckerHost {
getCompilerOptions(): CompilerOptions;
getJsxFactoryEntity(): EntityName;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this feels odd. i would rather we parsed the jsxFactory string one more time in the checker instead.


getSourceFiles(): SourceFile[];
getSourceFile(fileName: string): SourceFile;
Expand Down
57 changes: 0 additions & 57 deletions tests/baselines/reference/jsxFactoryIdentifier.errors.txt

This file was deleted.

125 changes: 125 additions & 0 deletions tests/baselines/reference/jsxFactoryIdentifier.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
=== tests/cases/compiler/Element.ts ===

declare namespace JSX {
>JSX : Symbol(JSX, Decl(Element.ts, 0, 0))

interface Element {
>Element : Symbol(Element, Decl(Element.ts, 1, 23))

name: string;
>name : Symbol(Element.name, Decl(Element.ts, 2, 23))

isIntrinsic: boolean;
>isIntrinsic : Symbol(Element.isIntrinsic, Decl(Element.ts, 3, 21))

isCustomElement: boolean;
>isCustomElement : Symbol(Element.isCustomElement, Decl(Element.ts, 4, 29))

toString(renderId?: number): string;
>toString : Symbol(Element.toString, Decl(Element.ts, 5, 33))
>renderId : Symbol(renderId, Decl(Element.ts, 6, 17))

bindDOM(renderId?: number): number;
>bindDOM : Symbol(Element.bindDOM, Decl(Element.ts, 6, 44))
>renderId : Symbol(renderId, Decl(Element.ts, 7, 16))

resetComponent(): void;
>resetComponent : Symbol(Element.resetComponent, Decl(Element.ts, 7, 43))

instantiateComponents(renderId?: number): number;
>instantiateComponents : Symbol(Element.instantiateComponents, Decl(Element.ts, 8, 31))
>renderId : Symbol(renderId, Decl(Element.ts, 9, 30))

props: any;
>props : Symbol(Element.props, Decl(Element.ts, 9, 57))
}
}
export namespace Element {
>Element : Symbol(Element, Decl(Element.ts, 12, 1))

export function isElement(el: any): el is JSX.Element {
>isElement : Symbol(isElement, Decl(Element.ts, 13, 26))
>el : Symbol(el, Decl(Element.ts, 14, 30))
>el : Symbol(el, Decl(Element.ts, 14, 30))
>JSX : Symbol(JSX, Decl(Element.ts, 0, 0))
>Element : Symbol(JSX.Element, Decl(Element.ts, 1, 23))

return el.markAsChildOfRootElement !== undefined;
>el : Symbol(el, Decl(Element.ts, 14, 30))
>undefined : Symbol(undefined)
}

export function createElement(args: any[]) {
>createElement : Symbol(createElement, Decl(Element.ts, 16, 5))
>args : Symbol(args, Decl(Element.ts, 18, 34))

return {
}
}
}

export let createElement = Element.createElement;
>createElement : Symbol(createElement, Decl(Element.ts, 25, 10))
>Element.createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
>Element : Symbol(Element, Decl(Element.ts, 12, 1))
>createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))

function toCamelCase(text: string): string {
>toCamelCase : Symbol(toCamelCase, Decl(Element.ts, 25, 49))
>text : Symbol(text, Decl(Element.ts, 27, 21))

return text[0].toLowerCase() + text.substring(1);
>text[0].toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>text : Symbol(text, Decl(Element.ts, 27, 21))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>text.substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --))
>text : Symbol(text, Decl(Element.ts, 27, 21))
>substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --))
}

=== tests/cases/compiler/test.tsx ===
import { Element} from './Element';
>Element : Symbol(Element, Decl(test.tsx, 0, 8))

let createElement = Element.createElement;
>createElement : Symbol(createElement, Decl(test.tsx, 1, 3))
>Element.createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
>Element : Symbol(Element, Decl(test.tsx, 0, 8))
>createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))

let c: {
>c : Symbol(c, Decl(test.tsx, 2, 3))

a?: {
>a : Symbol(a, Decl(test.tsx, 2, 8))

b: string
>b : Symbol(b, Decl(test.tsx, 3, 6))
}
};

class A {
>A : Symbol(A, Decl(test.tsx, 6, 2))

view() {
>view : Symbol(A.view, Decl(test.tsx, 8, 9))

return [
<meta content="helloworld"></meta>,
>meta : Symbol(unknown)
>content : Symbol(unknown)
>meta : Symbol(unknown)

<meta content={c.a!.b}></meta>
>meta : Symbol(unknown)
>content : Symbol(unknown)
>c.a!.b : Symbol(b, Decl(test.tsx, 3, 6))
>c.a : Symbol(a, Decl(test.tsx, 2, 8))
>c : Symbol(c, Decl(test.tsx, 2, 3))
>a : Symbol(a, Decl(test.tsx, 2, 8))
>b : Symbol(b, Decl(test.tsx, 3, 6))
>meta : Symbol(unknown)

];
}
}
Loading