-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Add jsxFactory compiler option. #11267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
63fed8f
3076add
db9d0ff
71b8f00
ceda48b
49ee64d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really understand this comment. What does "resolving the namespace" by the emit resolver mean, and why does it need to? My guess would have been to ensure that the |
||
| // 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -109,8 +109,12 @@ namespace ts { | |
| || createAssignHelper(currentSourceFile.externalHelpersModuleName, segments); | ||
| } | ||
|
|
||
| const element = createReactCreateElement( | ||
| compilerOptions.reactNamespace, | ||
| const jsxFactory = compilerOptions.jsxFactory | ||
| ? createJsxFactory(compilerOptions.jsxFactory) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is a fully-synthesized entity name, consider caching it on |
||
| : createReactCreateElement(compilerOptions.reactNamespace, node); | ||
|
|
||
| const element = createJsxFactoryCall( | ||
| jsxFactory, | ||
| tagName, | ||
| objectProperties, | ||
| filter(map(children, transformJsxChildToExpression), isDefined), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2701,6 +2701,7 @@ namespace ts { | |
| inlineSources?: boolean; | ||
| isolatedModules?: boolean; | ||
| jsx?: JsxEmit; | ||
| jsxFactory?: string; | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of parsing the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'd probably be cleanest to do this once in checker.ts and then only again if needed in emitter.ts.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is somewhat similar to what we do for |
||
| lib?: string[]; | ||
| /*@internal*/listEmittedFiles?: boolean; | ||
| /*@internal*/listFiles?: boolean; | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
| <div />; | ||
| ~~~ | ||
| !!! error TS2304: Cannot find name 'h'. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| //// [file.tsx] | ||
| declare module JSX { | ||
| interface IntrinsicElements { | ||
| [s: string]: any; | ||
| } | ||
| } | ||
|
|
||
| // This should raise an error as 'h' is not declared. | ||
| <div />; | ||
|
|
||
|
|
||
| //// [file.js] | ||
| // This should raise an error as 'h' is not declared. | ||
| h("div", null); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| //// [file.tsx] | ||
| declare module JSX { | ||
| interface IntrinsicElements { | ||
| [s: string]: any; | ||
| } | ||
| } | ||
|
|
||
| declare var h: any; | ||
|
|
||
| <div />; | ||
|
|
||
|
|
||
| //// [file.js] | ||
| h("div", null); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 />; | ||
| >div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 0, 20)) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
|
||
| <div />; | ||
| ><div /> : any | ||
| >div : any | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| //// [file.tsx] | ||
| declare module JSX { | ||
| interface IntrinsicElements { | ||
| [s: string]: any; | ||
| } | ||
| } | ||
|
|
||
| declare var React: any; | ||
|
|
||
| <div />; | ||
|
|
||
|
|
||
| //// [file.js] | ||
| React.createElement("div", null); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 />; | ||
| >div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 0, 20)) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
|
||
| <div />; | ||
| ><div /> : any | ||
| >div : any | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| //// [file.tsx] | ||
| declare module JSX { | ||
| interface IntrinsicElements { | ||
| [s: string]: any; | ||
| } | ||
| } | ||
|
|
||
| declare var a: any; | ||
|
|
||
| <div />; | ||
|
|
||
|
|
||
| //// [file.js] | ||
| a.b.c("div", null); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 />; | ||
| >div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 0, 20)) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
|
||
| <div />; | ||
| ><div /> : any | ||
| >div : any | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| <div />; | ||
|
|
||
|
|
||
| //// [file.js] | ||
| //// [react-consumer.js] | ||
| "use strict"; | ||
| var h_1 = require("./h"); | ||
| // Should not elide h import | ||
| h("div", null); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 />; | ||
| >div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 0, 20)) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| <div />; | ||
| ><div /> : any | ||
| >div : any | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could also be an expression like
foo["bar"], should be able to split on./[There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a qualified name is sufficient here. we could relax that in the future.