From 63fed8f934148e5ab21f5b4556f6ab52e6332899 Mon Sep 17 00:00:00 2001 From: Bradley Ayers Date: Fri, 30 Sep 2016 09:17:12 +1000 Subject: [PATCH 1/5] Add jsxFactory compiler option. --- src/compiler/checker.ts | 19 ++++++++------ src/compiler/commandLineParser.ts | 5 ++++ src/compiler/factory.ts | 19 ++++++++------ src/compiler/program.ts | 4 +++ src/compiler/transformers/jsx.ts | 8 ++++-- src/compiler/types.ts | 1 + src/harness/unittests/transpile.ts | 4 +++ .../transpile/Supports setting jsxFactory.js | 3 +++ .../tsxReactEmitJsxFactory1.errors.txt | 15 +++++++++++ .../reference/tsxReactEmitJsxFactory1.js | 14 +++++++++++ .../reference/tsxReactEmitJsxFactory2.js | 14 +++++++++++ .../reference/tsxReactEmitJsxFactory2.symbols | 18 +++++++++++++ .../reference/tsxReactEmitJsxFactory2.types | 19 ++++++++++++++ .../reference/tsxReactEmitJsxFactory3.js | 14 +++++++++++ .../reference/tsxReactEmitJsxFactory3.symbols | 18 +++++++++++++ .../reference/tsxReactEmitJsxFactory3.types | 19 ++++++++++++++ .../reference/tsxReactEmitJsxFactory4.js | 14 +++++++++++ .../reference/tsxReactEmitJsxFactory4.symbols | 18 +++++++++++++ .../reference/tsxReactEmitJsxFactory4.types | 19 ++++++++++++++ .../reference/tsxReactEmitJsxFactory5.js | 24 ++++++++++++++++++ .../reference/tsxReactEmitJsxFactory5.symbols | 24 ++++++++++++++++++ .../reference/tsxReactEmitJsxFactory5.types | 25 +++++++++++++++++++ .../tsxReactEmitJsxFactory6.errors.txt | 16 ++++++++++++ .../reference/tsxReactEmitJsxFactory6.js | 15 +++++++++++ .../jsx/tsxReactEmitJsxFactory1.tsx | 11 ++++++++ .../jsx/tsxReactEmitJsxFactory2.tsx | 12 +++++++++ .../jsx/tsxReactEmitJsxFactory3.tsx | 12 +++++++++ .../jsx/tsxReactEmitJsxFactory4.tsx | 12 +++++++++ .../jsx/tsxReactEmitJsxFactory5.tsx | 16 ++++++++++++ .../jsx/tsxReactEmitJsxFactory6.tsx | 14 +++++++++++ 30 files changed, 409 insertions(+), 17 deletions(-) create mode 100644 tests/baselines/reference/transpile/Supports setting jsxFactory.js create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory1.errors.txt create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory1.js create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory2.js create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory2.symbols create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory2.types create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory3.js create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory3.symbols create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory3.types create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory4.js create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory4.symbols create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory4.types create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory5.js create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory5.symbols create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory5.types create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory6.errors.txt create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory6.js create mode 100644 tests/cases/conformance/jsx/tsxReactEmitJsxFactory1.tsx create mode 100644 tests/cases/conformance/jsx/tsxReactEmitJsxFactory2.tsx create mode 100644 tests/cases/conformance/jsx/tsxReactEmitJsxFactory3.tsx create mode 100644 tests/cases/conformance/jsx/tsxReactEmitJsxFactory4.tsx create mode 100644 tests/cases/conformance/jsx/tsxReactEmitJsxFactory5.tsx create mode 100644 tests/cases/conformance/jsx/tsxReactEmitJsxFactory6.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 51550ef873fc5..73a07cf058dd7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10713,13 +10713,18 @@ namespace ts { 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. - const reactRefErr = compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined; - const reactNamespace = compilerOptions.reactNamespace ? compilerOptions.reactNamespace : "React"; - const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace); - if (reactSym) { - getSymbolLinks(reactSym).referenced = true; + // The JSX factory namespace symbol should be marked as 'used' so we don't incorrectly elide its import. And if + // it isn't in scope when targeting React emit, we should issue an error. + const jsxFactoryRefErr = compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined; + let jsxFactoryNamespace = "React"; + if (compilerOptions.jsxFactory) { + jsxFactoryNamespace = compilerOptions.jsxFactory.split(".")[0]; + } else if (compilerOptions.reactNamespace) { + jsxFactoryNamespace = compilerOptions.reactNamespace; + } + const jsxFactorySym = resolveName(node.tagName, jsxFactoryNamespace, SymbolFlags.Value, jsxFactoryRefErr, jsxFactoryNamespace); + if (jsxFactorySym) { + getSymbolLinks(jsxFactorySym).referenced = true; } const targetAttributesType = getJsxElementAttributesType(node); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 4e8b81da81b86..232f0bdb7a4ca 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -69,6 +69,11 @@ namespace ts { paramType: Diagnostics.KIND, description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_or_react, }, + { + name: "jsxFactory", + type: "string", + description: Diagnostics.Specify_the_JSX_factory_function_to_use_when_targeting_react_JSX_emit_eg_React_createElement_or_h, + }, { name: "reactNamespace", type: "string", diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 16eeb86f59a73..f6a573dc12cc2 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1618,17 +1618,23 @@ namespace ts { ); } - function createReactNamespace(reactNamespace: string, parent: JsxOpeningLikeElement) { + export function createJsxFactory(jsxFactory: string) { + // No explicit validation of this parameter is required. Users are + // assumed to have provided a correct string. + return createIdentifier(jsxFactory); + } + + export function createReactCreateElement(reactNamespace: string, parentElement: JsxOpeningLikeElement) { // To ensure the emit resolver can properly resolve the namespace, we need to // treat this identifier as if it were a source tree node by clearing the `Synthesized` // flag and setting a parent node. const react = createIdentifier(reactNamespace || "React"); react.flags &= ~NodeFlags.Synthesized; - react.parent = parent; - return react; + react.parent = parentElement; + return createPropertyAccess(react, "createElement"); } - export function createReactCreateElement(reactNamespace: string, tagName: Expression, props: Expression, children: Expression[], parentElement: JsxOpeningLikeElement, location: TextRange): LeftHandSideExpression { + export function createJsxFactoryCall(jsxFactory: Identifier | PropertyAccessExpression, tagName: Expression, props: Expression, children: Expression[], parentElement: JsxOpeningLikeElement, location: TextRange): LeftHandSideExpression { const argumentsList = [tagName]; if (props) { argumentsList.push(props); @@ -1651,10 +1657,7 @@ namespace ts { } return createCall( - createPropertyAccess( - createReactNamespace(reactNamespace, parentElement), - "createElement" - ), + jsxFactory, /*typeArguments*/ undefined, argumentsList, location diff --git a/src/compiler/program.ts b/src/compiler/program.ts index ab306e90f3a29..8bec9724b9574 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2346,6 +2346,10 @@ namespace ts { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace)); } + if (options.jsxFactory && options.reactNamespace) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "jsxFactory", "reactNamespace")); + } + // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files if (!options.noEmit && !options.suppressOutputPathCheck) { const emitHost = getEmitHost(); diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 9e6aa507cce5a..66a1348b7d9a2 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -109,8 +109,12 @@ namespace ts { || createAssignHelper(currentSourceFile.externalHelpersModuleName, segments); } - const element = createReactCreateElement( - compilerOptions.reactNamespace, + const jsxFactory = compilerOptions.jsxFactory + ? createJsxFactory(compilerOptions.jsxFactory) + : createReactCreateElement(compilerOptions.reactNamespace, node); + + const element = createJsxFactoryCall( + jsxFactory, tagName, objectProperties, filter(map(children, transformJsxChildToExpression), isDefined), diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e4bc9a29d5b03..0f97ed48dc9b4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2701,6 +2701,7 @@ namespace ts { inlineSources?: boolean; isolatedModules?: boolean; jsx?: JsxEmit; + jsxFactory?: string; lib?: string[]; /*@internal*/listEmittedFiles?: boolean; /*@internal*/listFiles?: boolean; diff --git a/src/harness/unittests/transpile.ts b/src/harness/unittests/transpile.ts index 808a5df37c15d..8fd34617e2e27 100644 --- a/src/harness/unittests/transpile.ts +++ b/src/harness/unittests/transpile.ts @@ -293,6 +293,10 @@ var x = 0;`, { options: { compilerOptions: { jsx: 1 }, fileName: "input.js", reportDiagnostics: true } }); + transpilesCorrectly("Supports setting 'jsxFactory'", "x;", { + options: { compilerOptions: { jsxFactory: "React.createElement" }, fileName: "input.js", reportDiagnostics: true } + }); + transpilesCorrectly("Supports setting 'lib'", "x;", { options: { compilerOptions: { lib: ["es2015", "dom"] }, fileName: "input.js", reportDiagnostics: true } }); diff --git a/tests/baselines/reference/transpile/Supports setting jsxFactory.js b/tests/baselines/reference/transpile/Supports setting jsxFactory.js new file mode 100644 index 0000000000000..8d91090453b8a --- /dev/null +++ b/tests/baselines/reference/transpile/Supports setting jsxFactory.js @@ -0,0 +1,3 @@ +"use strict"; +x; +//# sourceMappingURL=input.js.map \ No newline at end of file diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory1.errors.txt b/tests/baselines/reference/tsxReactEmitJsxFactory1.errors.txt new file mode 100644 index 0000000000000..2be884bd3774c --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory1.errors.txt @@ -0,0 +1,15 @@ +tests/cases/conformance/jsx/file.tsx(8,2): error TS2304: Cannot find name 'h'. + + +==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== + declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } + } + + // This should raise an error as 'h' is not declared. +
; + ~~~ +!!! error TS2304: Cannot find name 'h'. + \ No newline at end of file diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory1.js b/tests/baselines/reference/tsxReactEmitJsxFactory1.js new file mode 100644 index 0000000000000..8e19c0eda6cba --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory1.js @@ -0,0 +1,14 @@ +//// [file.tsx] +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +// This should raise an error as 'h' is not declared. +
; + + +//// [file.js] +// This should raise an error as 'h' is not declared. +h("div", null); diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory2.js b/tests/baselines/reference/tsxReactEmitJsxFactory2.js new file mode 100644 index 0000000000000..054882fd054b4 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory2.js @@ -0,0 +1,14 @@ +//// [file.tsx] +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +declare var h: any; + +
; + + +//// [file.js] +h("div", null); diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory2.symbols b/tests/baselines/reference/tsxReactEmitJsxFactory2.symbols new file mode 100644 index 0000000000000..c3ad64d877abf --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory2.symbols @@ -0,0 +1,18 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { +>JSX : Symbol(JSX, Decl(file.tsx, 0, 0)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 0, 20)) + + [s: string]: any; +>s : Symbol(s, Decl(file.tsx, 2, 3)) + } +} + +declare var h: any; +>h : Symbol(h, Decl(file.tsx, 6, 11)) + +
; +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 0, 20)) + diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory2.types b/tests/baselines/reference/tsxReactEmitJsxFactory2.types new file mode 100644 index 0000000000000..a95f3e323b26f --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory2.types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { +>JSX : any + + interface IntrinsicElements { +>IntrinsicElements : IntrinsicElements + + [s: string]: any; +>s : string + } +} + +declare var h: any; +>h : any + +
; +>
: any +>div : any + diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory3.js b/tests/baselines/reference/tsxReactEmitJsxFactory3.js new file mode 100644 index 0000000000000..ef261a9b6152c --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory3.js @@ -0,0 +1,14 @@ +//// [file.tsx] +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +declare var React: any; + +
; + + +//// [file.js] +React.createElement("div", null); diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory3.symbols b/tests/baselines/reference/tsxReactEmitJsxFactory3.symbols new file mode 100644 index 0000000000000..6c499aea616f3 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory3.symbols @@ -0,0 +1,18 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { +>JSX : Symbol(JSX, Decl(file.tsx, 0, 0)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 0, 20)) + + [s: string]: any; +>s : Symbol(s, Decl(file.tsx, 2, 3)) + } +} + +declare var React: any; +>React : Symbol(React, Decl(file.tsx, 6, 11)) + +
; +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 0, 20)) + diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory3.types b/tests/baselines/reference/tsxReactEmitJsxFactory3.types new file mode 100644 index 0000000000000..e7a87602da263 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory3.types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { +>JSX : any + + interface IntrinsicElements { +>IntrinsicElements : IntrinsicElements + + [s: string]: any; +>s : string + } +} + +declare var React: any; +>React : any + +
; +>
: any +>div : any + diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory4.js b/tests/baselines/reference/tsxReactEmitJsxFactory4.js new file mode 100644 index 0000000000000..278c2ca345976 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory4.js @@ -0,0 +1,14 @@ +//// [file.tsx] +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +declare var a: any; + +
; + + +//// [file.js] +a.b.c("div", null); diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory4.symbols b/tests/baselines/reference/tsxReactEmitJsxFactory4.symbols new file mode 100644 index 0000000000000..cae9d4eeb1c86 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory4.symbols @@ -0,0 +1,18 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { +>JSX : Symbol(JSX, Decl(file.tsx, 0, 0)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 0, 20)) + + [s: string]: any; +>s : Symbol(s, Decl(file.tsx, 2, 3)) + } +} + +declare var a: any; +>a : Symbol(a, Decl(file.tsx, 6, 11)) + +
; +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 0, 20)) + diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory4.types b/tests/baselines/reference/tsxReactEmitJsxFactory4.types new file mode 100644 index 0000000000000..176d907a6f7d1 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory4.types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { +>JSX : any + + interface IntrinsicElements { +>IntrinsicElements : IntrinsicElements + + [s: string]: any; +>s : string + } +} + +declare var a: any; +>a : any + +
; +>
: any +>div : any + diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory5.js b/tests/baselines/reference/tsxReactEmitJsxFactory5.js new file mode 100644 index 0000000000000..f12a25e4e4fce --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory5.js @@ -0,0 +1,24 @@ +//// [tests/cases/conformance/jsx/tsxReactEmitJsxFactory5.tsx] //// + +//// [file.tsx] +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +//// [h.d.ts] +export var h; + +//// [react-consumer.tsx] +import {h} from "./h"; +// Should not elide h import +
; + + +//// [file.js] +//// [react-consumer.js] +"use strict"; +var h_1 = require("./h"); +// Should not elide h import +h("div", null); diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory5.symbols b/tests/baselines/reference/tsxReactEmitJsxFactory5.symbols new file mode 100644 index 0000000000000..b8bdf5772bd8c --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory5.symbols @@ -0,0 +1,24 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { +>JSX : Symbol(JSX, Decl(file.tsx, 0, 0)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 0, 20)) + + [s: string]: any; +>s : Symbol(s, Decl(file.tsx, 2, 3)) + } +} + +=== tests/cases/conformance/jsx/h.d.ts === +export var h; +>h : Symbol(h, Decl(h.d.ts, 0, 10)) + +=== tests/cases/conformance/jsx/react-consumer.tsx === +import {h} from "./h"; +>h : Symbol(h, Decl(react-consumer.tsx, 0, 8)) + +// Should not elide h import +
; +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 0, 20)) + diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory5.types b/tests/baselines/reference/tsxReactEmitJsxFactory5.types new file mode 100644 index 0000000000000..2c8cddf4ac3bc --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory5.types @@ -0,0 +1,25 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { +>JSX : any + + interface IntrinsicElements { +>IntrinsicElements : IntrinsicElements + + [s: string]: any; +>s : string + } +} + +=== tests/cases/conformance/jsx/h.d.ts === +export var h; +>h : any + +=== tests/cases/conformance/jsx/react-consumer.tsx === +import {h} from "./h"; +>h : any + +// Should not elide h import +
; +>
: any +>div : any + diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory6.errors.txt b/tests/baselines/reference/tsxReactEmitJsxFactory6.errors.txt new file mode 100644 index 0000000000000..d0ce4240bdb6d --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory6.errors.txt @@ -0,0 +1,16 @@ +error TS5053: Option 'jsxFactory' cannot be specified with option 'reactNamespace'. + + +!!! error TS5053: Option 'jsxFactory' cannot be specified with option 'reactNamespace'. +==== tests/cases/conformance/jsx/file.tsx (0 errors) ==== + // An error should be thrown when jsxFactory and reactNamespace are both specified. + declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } + } + + declare var React: any; + +
; + \ No newline at end of file diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory6.js b/tests/baselines/reference/tsxReactEmitJsxFactory6.js new file mode 100644 index 0000000000000..f4e826b3f19a6 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory6.js @@ -0,0 +1,15 @@ +//// [file.tsx] +// An error should be thrown when jsxFactory and reactNamespace are both specified. +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +declare var React: any; + +
; + + +//// [file.js] +React.createElement("div", null); diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory1.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory1.tsx new file mode 100644 index 0000000000000..df5cb807881b6 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory1.tsx @@ -0,0 +1,11 @@ +//@filename: file.tsx +//@jsx: react +//@jsxFactory: h +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +// This should raise an error as 'h' is not declared. +
; diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory2.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory2.tsx new file mode 100644 index 0000000000000..31ba0bef2c557 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory2.tsx @@ -0,0 +1,12 @@ +//@filename: file.tsx +//@jsx: react +//@jsxFactory: h +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +declare var h: any; + +
; diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory3.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory3.tsx new file mode 100644 index 0000000000000..f34ab398bedc4 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory3.tsx @@ -0,0 +1,12 @@ +//@filename: file.tsx +//@jsx: react +//@jsxFactory: React.createElement +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +declare var React: any; + +
; diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory4.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory4.tsx new file mode 100644 index 0000000000000..912bf18984133 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory4.tsx @@ -0,0 +1,12 @@ +//@filename: file.tsx +//@jsx: react +//@jsxFactory: a.b.c +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +declare var a: any; + +
; diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory5.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory5.tsx new file mode 100644 index 0000000000000..bb0b73a8f956d --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory5.tsx @@ -0,0 +1,16 @@ +//@filename: file.tsx +//@jsx: react +//@jsxFactory: h +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +//@filename: h.d.ts +export var h; + +//@filename: react-consumer.tsx +import {h} from "./h"; +// Should not elide h import +
; diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory6.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory6.tsx new file mode 100644 index 0000000000000..9d7ea13def93d --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory6.tsx @@ -0,0 +1,14 @@ +//@filename: file.tsx +//@jsx: react +// An error should be thrown when jsxFactory and reactNamespace are both specified. +//@jsxFactory: React.createElement +//@reactNamespace: React +declare module JSX { + interface IntrinsicElements { + [s: string]: any; + } +} + +declare var React: any; + +
; From 3076add94d2201706d81e56ba57ef1d09d754f31 Mon Sep 17 00:00:00 2001 From: Bradley Ayers Date: Fri, 30 Sep 2016 18:21:25 +1000 Subject: [PATCH 2/5] Require 'jsx' if 'jsxFactory' is used and other tidy ups. --- src/compiler/checker.ts | 3 ++- src/compiler/factory.ts | 4 ++-- src/compiler/program.ts | 4 ++++ src/harness/unittests/transpile.ts | 2 +- .../reference/tsxReactEmitJsxFactory1.errors.txt | 2 +- .../baselines/reference/tsxReactEmitJsxFactory1.js | 4 ++-- .../reference/tsxReactEmitJsxFactory6.errors.txt | 11 +---------- .../baselines/reference/tsxReactEmitJsxFactory6.js | 13 ++----------- .../reference/tsxReactEmitJsxFactory7.errors.txt | 7 +++++++ .../baselines/reference/tsxReactEmitJsxFactory7.js | 6 ++++++ .../conformance/jsx/tsxReactEmitJsxFactory1.tsx | 2 +- .../conformance/jsx/tsxReactEmitJsxFactory6.tsx | 11 +---------- .../conformance/jsx/tsxReactEmitJsxFactory7.tsx | 4 ++++ 13 files changed, 34 insertions(+), 39 deletions(-) create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory7.errors.txt create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory7.js create mode 100644 tests/cases/conformance/jsx/tsxReactEmitJsxFactory7.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 73a07cf058dd7..163f3564f6f4d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10719,7 +10719,8 @@ namespace ts { let jsxFactoryNamespace = "React"; if (compilerOptions.jsxFactory) { jsxFactoryNamespace = compilerOptions.jsxFactory.split(".")[0]; - } else if (compilerOptions.reactNamespace) { + } + else if (compilerOptions.reactNamespace) { jsxFactoryNamespace = compilerOptions.reactNamespace; } const jsxFactorySym = resolveName(node.tagName, jsxFactoryNamespace, SymbolFlags.Value, jsxFactoryRefErr, jsxFactoryNamespace); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index f6a573dc12cc2..f049e351d4bb1 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1624,7 +1624,7 @@ namespace ts { return createIdentifier(jsxFactory); } - export function createReactCreateElement(reactNamespace: string, parentElement: JsxOpeningLikeElement) { + export function createReactCreateElement(reactNamespace: string | undefined, parentElement: JsxOpeningLikeElement) { // To ensure the emit resolver can properly resolve the namespace, we need to // treat this identifier as if it were a source tree node by clearing the `Synthesized` // flag and setting a parent node. @@ -2748,4 +2748,4 @@ namespace ts { function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) { return tryGetModuleNameFromFile(resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions); } -} \ No newline at end of file +} diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 8bec9724b9574..6b543b9adf887 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2346,6 +2346,10 @@ namespace ts { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace)); } + if (options.jsxFactory && options.jsx !== JsxEmit.React) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_1_is_2, "jsxFactory", "jsx", "react")); + } + if (options.jsxFactory && options.reactNamespace) { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "jsxFactory", "reactNamespace")); } diff --git a/src/harness/unittests/transpile.ts b/src/harness/unittests/transpile.ts index 8fd34617e2e27..8ebaadcd63952 100644 --- a/src/harness/unittests/transpile.ts +++ b/src/harness/unittests/transpile.ts @@ -294,7 +294,7 @@ var x = 0;`, { }); transpilesCorrectly("Supports setting 'jsxFactory'", "x;", { - options: { compilerOptions: { jsxFactory: "React.createElement" }, fileName: "input.js", reportDiagnostics: true } + options: { compilerOptions: { jsxFactory: "React.createElement", jsx: JsxEmit.React }, fileName: "input.js", reportDiagnostics: true } }); transpilesCorrectly("Supports setting 'lib'", "x;", { diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory1.errors.txt b/tests/baselines/reference/tsxReactEmitJsxFactory1.errors.txt index 2be884bd3774c..b5f213b35077b 100644 --- a/tests/baselines/reference/tsxReactEmitJsxFactory1.errors.txt +++ b/tests/baselines/reference/tsxReactEmitJsxFactory1.errors.txt @@ -8,7 +8,7 @@ tests/cases/conformance/jsx/file.tsx(8,2): error TS2304: Cannot find name 'h'. } } - // This should raise an error as 'h' is not declared. + // This should issue an error as 'h' is not declared.
; ~~~ !!! error TS2304: Cannot find name 'h'. diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory1.js b/tests/baselines/reference/tsxReactEmitJsxFactory1.js index 8e19c0eda6cba..dad37460ecda0 100644 --- a/tests/baselines/reference/tsxReactEmitJsxFactory1.js +++ b/tests/baselines/reference/tsxReactEmitJsxFactory1.js @@ -5,10 +5,10 @@ declare module JSX { } } -// This should raise an error as 'h' is not declared. +// This should issue an error as 'h' is not declared.
; //// [file.js] -// This should raise an error as 'h' is not declared. +// This should issue an error as 'h' is not declared. h("div", null); diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory6.errors.txt b/tests/baselines/reference/tsxReactEmitJsxFactory6.errors.txt index d0ce4240bdb6d..6f19300d19680 100644 --- a/tests/baselines/reference/tsxReactEmitJsxFactory6.errors.txt +++ b/tests/baselines/reference/tsxReactEmitJsxFactory6.errors.txt @@ -3,14 +3,5 @@ error TS5053: Option 'jsxFactory' cannot be specified with option 'reactNamespac !!! error TS5053: Option 'jsxFactory' cannot be specified with option 'reactNamespace'. ==== tests/cases/conformance/jsx/file.tsx (0 errors) ==== - // An error should be thrown when jsxFactory and reactNamespace are both specified. - declare module JSX { - interface IntrinsicElements { - [s: string]: any; - } - } - - declare var React: any; - -
; + // An error should be issued when jsxFactory and reactNamespace are both specified. \ No newline at end of file diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory6.js b/tests/baselines/reference/tsxReactEmitJsxFactory6.js index f4e826b3f19a6..652ee8d0d3a11 100644 --- a/tests/baselines/reference/tsxReactEmitJsxFactory6.js +++ b/tests/baselines/reference/tsxReactEmitJsxFactory6.js @@ -1,15 +1,6 @@ //// [file.tsx] -// An error should be thrown when jsxFactory and reactNamespace are both specified. -declare module JSX { - interface IntrinsicElements { - [s: string]: any; - } -} - -declare var React: any; - -
; +// An error should be issued when jsxFactory and reactNamespace are both specified. //// [file.js] -React.createElement("div", null); +// An error should be issued when jsxFactory and reactNamespace are both specified. diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory7.errors.txt b/tests/baselines/reference/tsxReactEmitJsxFactory7.errors.txt new file mode 100644 index 0000000000000..fad204d890fc9 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory7.errors.txt @@ -0,0 +1,7 @@ +error TS5067: Option 'jsxFactory' can only be used when 'jsx' is 'react'. + + +!!! error TS5067: Option 'jsxFactory' can only be used when 'jsx' is 'react'. +==== tests/cases/conformance/jsx/file.tsx (0 errors) ==== + // An error should be issued when 'jsxFactory' is used when 'jsx' is not 'react'. + \ No newline at end of file diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory7.js b/tests/baselines/reference/tsxReactEmitJsxFactory7.js new file mode 100644 index 0000000000000..8bc875ecb0481 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory7.js @@ -0,0 +1,6 @@ +//// [file.tsx] +// An error should be issued when 'jsxFactory' is used when 'jsx' is not 'react'. + + +//// [file.jsx] +// An error should be issued when 'jsxFactory' is used when 'jsx' is not 'react'. diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory1.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory1.tsx index df5cb807881b6..6b89481604d1e 100644 --- a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory1.tsx +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory1.tsx @@ -7,5 +7,5 @@ declare module JSX { } } -// This should raise an error as 'h' is not declared. +// This should issue an error as 'h' is not declared.
; diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory6.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory6.tsx index 9d7ea13def93d..fa7d4c28a4a74 100644 --- a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory6.tsx +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory6.tsx @@ -1,14 +1,5 @@ //@filename: file.tsx //@jsx: react -// An error should be thrown when jsxFactory and reactNamespace are both specified. +// An error should be issued when jsxFactory and reactNamespace are both specified. //@jsxFactory: React.createElement //@reactNamespace: React -declare module JSX { - interface IntrinsicElements { - [s: string]: any; - } -} - -declare var React: any; - -
; diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory7.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory7.tsx new file mode 100644 index 0000000000000..54092e36857a3 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory7.tsx @@ -0,0 +1,4 @@ +//@filename: file.tsx +// An error should be issued when 'jsxFactory' is used when 'jsx' is not 'react'. +//@jsx: preserve +//@jsxFactory: h From db9d0fff4a2bb1a10fa5d510e324831f19a050c2 Mon Sep 17 00:00:00 2001 From: Bradley Ayers Date: Fri, 30 Sep 2016 19:05:51 +1000 Subject: [PATCH 3/5] Put diagnostic messages in the correct file. --- src/compiler/commandLineParser.ts | 2 +- src/compiler/diagnosticMessages.json | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 232f0bdb7a4ca..9f83ba8da1dbf 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -72,7 +72,7 @@ namespace ts { { name: "jsxFactory", type: "string", - description: Diagnostics.Specify_the_JSX_factory_function_to_use_when_targeting_react_JSX_emit_eg_React_createElement_or_h, + description: Diagnostics.Specify_the_JSX_factory_function_to_use_when_targeting_react_JSX_emit_e_g_React_createElement_or_h, }, { name: "reactNamespace", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index be4ee2fa07d98..a50f69d344516 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2361,6 +2361,10 @@ "category": "Error", "code": 5066 }, + "Option '{0}' can only be used when '{1}' is '{2}'.": { + "category": "Error", + "code": 5067 + }, "Concatenate and emit output to single file.": { "category": "Message", "code": 6001 @@ -2853,6 +2857,10 @@ "category": "Message", "code": 6139 }, + "Specify the JSX factory function to use when targeting 'react' JSX emit, e.g. 'React.createElement' or 'h'.": { + "category": "Message", + "code": 6140 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 From ceda48b68b18e1c994709698ecdba50bc8d4c110 Mon Sep 17 00:00:00 2001 From: Bradley Ayers Date: Sun, 2 Oct 2016 19:34:47 +1100 Subject: [PATCH 4/5] Validate jsxFactory as an EntityName. --- src/compiler/checker.ts | 7 ++++++- src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/parser.ts | 21 +++++++++++++++++++ src/compiler/program.ts | 17 ++++++++++----- .../tsxReactEmitJsxFactory8.errors.txt | 7 +++++++ .../reference/tsxReactEmitJsxFactory8.js | 6 ++++++ .../tsxReactEmitJsxFactory9.errors.txt | 7 +++++++ .../reference/tsxReactEmitJsxFactory9.js | 6 ++++++ .../jsx/tsxReactEmitJsxFactory8.tsx | 4 ++++ .../jsx/tsxReactEmitJsxFactory9.tsx | 4 ++++ 10 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory8.errors.txt create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory8.js create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory9.errors.txt create mode 100644 tests/baselines/reference/tsxReactEmitJsxFactory9.js create mode 100644 tests/cases/conformance/jsx/tsxReactEmitJsxFactory8.tsx create mode 100644 tests/cases/conformance/jsx/tsxReactEmitJsxFactory9.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3af80ab658307..d2b2e050ea551 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10800,7 +10800,12 @@ namespace ts { const jsxFactoryRefErr = compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined; let jsxFactoryNamespace = "React"; if (compilerOptions.jsxFactory) { - jsxFactoryNamespace = compilerOptions.jsxFactory.split(".")[0]; + let { entityName } = parseIsolatedEntityName(compilerOptions.jsxFactory); + while (entityName.kind === SyntaxKind.QualifiedName) { + entityName = (entityName).left; + } + Debug.assertNode(entityName, node => node.kind === SyntaxKind.Identifier); + jsxFactoryNamespace = (entityName).text; } else if (compilerOptions.reactNamespace) { jsxFactoryNamespace = compilerOptions.reactNamespace; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 94b7ce58a0276..eafb191535c82 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2369,6 +2369,10 @@ "category": "Error", "code": 5067 }, + "Invalid value for '{0}'. '{1}' is not a valid identifier/qualified-name.": { + "category": "Error", + "code": 5068 + }, "Concatenate and emit output to single file.": { "category": "Message", "code": 6001 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9bd9311b74921..3d04b13a44b1b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -462,6 +462,13 @@ namespace ts { return result; } + /* @internal */ + export function parseIsolatedEntityName(content: string) { + const result = Parser.parseIsolatedEntityName(content); + Parser.fixupParentReferences(result.entityName); + return result; + } + /* @internal */ // Exposed only for testing. export function parseJSDocTypeExpressionForTests(content: string, start?: number, length?: number) { @@ -584,6 +591,20 @@ namespace ts { return result; } + export function parseIsolatedEntityName(content: string, allowReservedWords = false) { + initializeState("file.js", content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); + sourceFile = { languageVariant: LanguageVariant.Standard, text: content }; + + nextToken(); + const entityName = parseEntityName(allowReservedWords); + parseExpected(SyntaxKind.EndOfFileToken); + + const diagnostics = parseDiagnostics; + clearState(); + + return { entityName, diagnostics }; + } + function getLanguageVariant(scriptKind: ScriptKind) { // .tsx and .jsx files are treated as jsx language variant. return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS ? LanguageVariant.JSX : LanguageVariant.Standard; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 3f705c613fd37..ca341e9d1d080 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1533,12 +1533,19 @@ namespace ts { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace)); } - if (options.jsxFactory && options.jsx !== JsxEmit.React) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_1_is_2, "jsxFactory", "jsx", "react")); - } + if (options.jsxFactory) { + if (options.jsx !== JsxEmit.React) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_1_is_2, "jsxFactory", "jsx", "react")); + } - if (options.jsxFactory && options.reactNamespace) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "jsxFactory", "reactNamespace")); + if (options.reactNamespace) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "jsxFactory", "reactNamespace")); + } + + const { diagnostics } = parseIsolatedEntityName(options.jsxFactory); + if (diagnostics.length > 0) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_0_1_is_not_a_valid_identifier_Slashqualified_name, "jsxFactory", options.jsxFactory)); + } } // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory8.errors.txt b/tests/baselines/reference/tsxReactEmitJsxFactory8.errors.txt new file mode 100644 index 0000000000000..e0b29e4aead43 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory8.errors.txt @@ -0,0 +1,7 @@ +error TS5068: Invalid value for 'jsxFactory'. '0' is not a valid identifier/qualified-name. + + +!!! error TS5068: Invalid value for 'jsxFactory'. '0' is not a valid identifier/qualified-name. +==== tests/cases/conformance/jsx/file.tsx (0 errors) ==== + // An error should be issued when 'jsxFactory' is an invalid identifier. + \ No newline at end of file diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory8.js b/tests/baselines/reference/tsxReactEmitJsxFactory8.js new file mode 100644 index 0000000000000..75fd287adc337 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory8.js @@ -0,0 +1,6 @@ +//// [file.tsx] +// An error should be issued when 'jsxFactory' is an invalid identifier. + + +//// [file.js] +// An error should be issued when 'jsxFactory' is an invalid identifier. diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory9.errors.txt b/tests/baselines/reference/tsxReactEmitJsxFactory9.errors.txt new file mode 100644 index 0000000000000..ae58ed3c8c9e1 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory9.errors.txt @@ -0,0 +1,7 @@ +error TS5068: Invalid value for 'jsxFactory'. 'h.0' is not a valid identifier/qualified-name. + + +!!! error TS5068: Invalid value for 'jsxFactory'. 'h.0' is not a valid identifier/qualified-name. +==== tests/cases/conformance/jsx/file.tsx (0 errors) ==== + // An error should be issued when 'jsxFactory' is an invalid qualified name. + \ No newline at end of file diff --git a/tests/baselines/reference/tsxReactEmitJsxFactory9.js b/tests/baselines/reference/tsxReactEmitJsxFactory9.js new file mode 100644 index 0000000000000..cdc5eeee444cc --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitJsxFactory9.js @@ -0,0 +1,6 @@ +//// [file.tsx] +// An error should be issued when 'jsxFactory' is an invalid qualified name. + + +//// [file.js] +// An error should be issued when 'jsxFactory' is an invalid qualified name. diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory8.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory8.tsx new file mode 100644 index 0000000000000..2227a6e29859e --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory8.tsx @@ -0,0 +1,4 @@ +//@filename: file.tsx +//@jsx: react +// An error should be issued when 'jsxFactory' is an invalid identifier. +//@jsxFactory: 0 diff --git a/tests/cases/conformance/jsx/tsxReactEmitJsxFactory9.tsx b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory9.tsx new file mode 100644 index 0000000000000..c928a1d42d891 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitJsxFactory9.tsx @@ -0,0 +1,4 @@ +//@filename: file.tsx +//@jsx: react +// An error should be issued when 'jsxFactory' is an invalid qualified name. +//@jsxFactory: h.0 From 49ee64d9c6230968b0a7b16fd9f2633511b5078b Mon Sep 17 00:00:00 2001 From: Bradley Ayers Date: Sun, 2 Oct 2016 21:24:28 +1100 Subject: [PATCH 5/5] Refactor factory:jsxFactory to create granular nodes for entity name. --- src/compiler/factory.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index a10a49fb14ec1..7572df79ce940 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1618,10 +1618,25 @@ namespace ts { ); } + export function createEntityName(entityName: string) { + const { entityName: node } = parseIsolatedEntityName(entityName); + + // Parsing occurred in an isolated context, so nodes need to be synthesized + // and positions erased for correct positioning in emit. + const synthesize = (node: Node) => { + node.pos = -1; + node.end = -1; + node.flags |= NodeFlags.Synthesized; + forEachChild(node, synthesize); + }; + synthesize(node); + + return node; + } + export function createJsxFactory(jsxFactory: string) { - // No explicit validation of this parameter is required. Users are - // assumed to have provided a correct string. - return createIdentifier(jsxFactory); + const entityName = createEntityName(jsxFactory); + return createExpressionFromEntityName(entityName); } export function createReactCreateElement(reactNamespace: string | undefined, parentElement: JsxOpeningLikeElement) { @@ -1634,7 +1649,7 @@ namespace ts { return createPropertyAccess(react, "createElement"); } - export function createJsxFactoryCall(jsxFactory: Identifier | PropertyAccessExpression, tagName: Expression, props: Expression, children: Expression[], parentElement: JsxOpeningLikeElement, location: TextRange): LeftHandSideExpression { + export function createJsxFactoryCall(jsxFactory: Expression, tagName: Expression, props: Expression, children: Expression[], parentElement: JsxOpeningLikeElement, location: TextRange): LeftHandSideExpression { const argumentsList = [tagName]; if (props) { argumentsList.push(props);