Skip to content

Commit b950086

Browse files
author
Noj
committed
reset soft with master for single commit
1 parent c3c6be6 commit b950086

File tree

43 files changed

+815
-46
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+815
-46
lines changed

src/compiler/checker.ts

+72-23
Original file line numberDiff line numberDiff line change
@@ -954,16 +954,32 @@ namespace ts {
954954
if (location) {
955955
const file = getSourceFileOfNode(location);
956956
if (file) {
957-
if (file.localJsxNamespace) {
958-
return file.localJsxNamespace;
957+
if (isJsxOpeningFragment(location)) {
958+
if (file.localJsxFragmentNamespace) {
959+
return file.localJsxFragmentNamespace;
960+
}
961+
const jsxFragmentPragma = file.pragmas.get("jsxfrag");
962+
if (jsxFragmentPragma) {
963+
const chosenPragma = isArray(jsxFragmentPragma) ? jsxFragmentPragma[0] : jsxFragmentPragma;
964+
file.localJsxFragmentFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion);
965+
visitNode(file.localJsxFragmentFactory, markAsSynthetic);
966+
if (file.localJsxFragmentFactory) {
967+
return file.localJsxFragmentNamespace = getFirstIdentifier(file.localJsxFragmentFactory).escapedText;
968+
}
969+
}
959970
}
960-
const jsxPragma = file.pragmas.get("jsx");
961-
if (jsxPragma) {
962-
const chosenpragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma;
963-
file.localJsxFactory = parseIsolatedEntityName(chosenpragma.arguments.factory, languageVersion);
964-
visitNode(file.localJsxFactory, markAsSynthetic);
965-
if (file.localJsxFactory) {
966-
return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText;
971+
else {
972+
if (file.localJsxNamespace) {
973+
return file.localJsxNamespace;
974+
}
975+
const jsxPragma = file.pragmas.get("jsx");
976+
if (jsxPragma) {
977+
const chosenPragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma;
978+
file.localJsxFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion);
979+
visitNode(file.localJsxFactory, markAsSynthetic);
980+
if (file.localJsxFactory) {
981+
return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText;
982+
}
967983
}
968984
}
969985
}
@@ -23736,10 +23752,14 @@ namespace ts {
2373623752
function checkJsxFragment(node: JsxFragment): Type {
2373723753
checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment);
2373823754

23739-
if (compilerOptions.jsx === JsxEmit.React && (compilerOptions.jsxFactory || getSourceFileOfNode(node).pragmas.has("jsx"))) {
23755+
// by default, jsx:'react' will use jsxFactory = React.createElement and jsxFragmentFactory = React.Fragment
23756+
// if jsxFactory compiler option is provided, ensure jsxFragmentFactory compiler option or @jsxFrag pragma is provided too
23757+
const nodeSourceFile = getSourceFileOfNode(node);
23758+
if (compilerOptions.jsx === JsxEmit.React && (compilerOptions.jsxFactory || nodeSourceFile.pragmas.has("jsx"))
23759+
&& !compilerOptions.jsxFragmentFactory && !nodeSourceFile.pragmas.has("jsxfrag")) {
2374023760
error(node, compilerOptions.jsxFactory
23741-
? Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory
23742-
: Diagnostics.JSX_fragment_is_not_supported_when_using_an_inline_JSX_factory_pragma);
23761+
? Diagnostics.The_jsxFragmentFactory_compiler_option_must_be_provided_to_use_JSX_fragments_with_the_jsxFactory_compiler_option
23762+
: Diagnostics.An_jsxFrag_pragma_is_required_when_using_an_jsx_pragma_with_JSX_fragments);
2374323763
}
2374423764

2374523765
checkJsxChildren(node);
@@ -24197,21 +24217,28 @@ namespace ts {
2419724217
if (isNodeOpeningLikeElement) {
2419824218
checkGrammarJsxElement(<JsxOpeningLikeElement>node);
2419924219
}
24220+
2420024221
checkJsxPreconditions(node);
2420124222
// The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import.
2420224223
// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
24203-
const reactRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
24204-
const reactNamespace = getJsxNamespace(node);
24205-
const reactLocation = isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>node).tagName : node;
24206-
const reactSym = resolveName(reactLocation, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true);
24207-
if (reactSym) {
24224+
const jsxFactoryRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
24225+
const jsxFactoryNamespace = getJsxNamespace(node);
24226+
const jsxFactoryLocation = isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>node).tagName : node;
24227+
24228+
// allow null as jsxFragmentFactory
24229+
let jsxFactorySym: Symbol | undefined;
24230+
if (!(isJsxOpeningFragment(node) && jsxFactoryNamespace === "null")) {
24231+
jsxFactorySym = resolveName(jsxFactoryLocation, jsxFactoryNamespace, SymbolFlags.Value, jsxFactoryRefErr, jsxFactoryNamespace, /*isUse*/ true);
24232+
}
24233+
24234+
if (jsxFactorySym) {
2420824235
// Mark local symbol as referenced here because it might not have been marked
24209-
// if jsx emit was not react as there wont be error being emitted
24210-
reactSym.isReferenced = SymbolFlags.All;
24236+
// if jsx emit was not jsxFactory as there wont be error being emitted
24237+
jsxFactorySym.isReferenced = SymbolFlags.All;
2421124238

24212-
// If react symbol is alias, mark it as referenced
24213-
if (reactSym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(reactSym)) {
24214-
markAliasSymbolAsReferenced(reactSym);
24239+
// If react/jsxFactory symbol is alias, mark it as refereced
24240+
if (jsxFactorySym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(jsxFactorySym)) {
24241+
markAliasSymbolAsReferenced(jsxFactorySym);
2421524242
}
2421624243
}
2421724244

@@ -36728,10 +36755,31 @@ namespace ts {
3672836755
return literalTypeToNode(<FreshableType>type, node, tracker);
3672936756
}
3673036757

36731-
function getJsxFactoryEntity(location: Node) {
36758+
function getJsxFactoryEntity(location: Node): EntityName | undefined {
3673236759
return location ? (getJsxNamespace(location), (getSourceFileOfNode(location).localJsxFactory || _jsxFactoryEntity)) : _jsxFactoryEntity;
3673336760
}
3673436761

36762+
function getJsxFragmentFactoryEntity(location: Node): EntityName | undefined {
36763+
if (location) {
36764+
const file = getSourceFileOfNode(location);
36765+
if (file) {
36766+
if (file.localJsxFragmentFactory) {
36767+
return file.localJsxFragmentFactory;
36768+
}
36769+
const jsxFragPragmas = file.pragmas.get("jsxfrag");
36770+
const jsxFragPragma = isArray(jsxFragPragmas) ? jsxFragPragmas[0] : jsxFragPragmas;
36771+
if (jsxFragPragma) {
36772+
file.localJsxFragmentFactory = parseIsolatedEntityName(jsxFragPragma.arguments.factory, languageVersion);
36773+
return file.localJsxFragmentFactory;
36774+
}
36775+
}
36776+
}
36777+
36778+
if (compilerOptions.jsxFragmentFactory) {
36779+
return parseIsolatedEntityName(compilerOptions.jsxFragmentFactory, languageVersion);
36780+
}
36781+
}
36782+
3673536783
function createResolver(): EmitResolver {
3673636784
// this variable and functions that use it are deliberately moved here from the outer scope
3673736785
// to avoid scope pollution
@@ -36806,6 +36854,7 @@ namespace ts {
3680636854
return !!(symbol && getCheckFlags(symbol) & CheckFlags.Late);
3680736855
},
3680836856
getJsxFactoryEntity,
36857+
getJsxFragmentFactoryEntity,
3680936858
getAllAccessorDeclarations(accessor: AccessorDeclaration): AllAccessorDeclarations {
3681036859
accessor = getParseTreeNode(accessor, isGetOrSetAccessorDeclaration)!; // TODO: GH#18217
3681136860
const otherKind = accessor.kind === SyntaxKind.SetAccessor ? SyntaxKind.GetAccessor : SyntaxKind.SetAccessor;

src/compiler/commandLineParser.ts

+6
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,12 @@ namespace ts {
766766
category: Diagnostics.Advanced_Options,
767767
description: Diagnostics.Specify_the_JSX_factory_function_to_use_when_targeting_react_JSX_emit_e_g_React_createElement_or_h
768768
},
769+
{
770+
name: "jsxFragmentFactory",
771+
type: "string",
772+
category: Diagnostics.Advanced_Options,
773+
description: Diagnostics.Specify_the_JSX_fragment_factory_function_to_use_when_targeting_react_JSX_emit_with_jsxFactory_compiler_option_is_specified_e_g_Fragment
774+
},
769775
{
770776
name: "resolveJsonModule",
771777
type: "boolean",

src/compiler/diagnosticMessages.json

+10-2
Original file line numberDiff line numberDiff line change
@@ -5027,11 +5027,11 @@
50275027
"category": "Error",
50285028
"code": 17015
50295029
},
5030-
"JSX fragment is not supported when using --jsxFactory": {
5030+
"The 'jsxFragmentFactory' compiler option must be provided to use JSX fragments with the 'jsxFactory' compiler option.": {
50315031
"category": "Error",
50325032
"code": 17016
50335033
},
5034-
"JSX fragment is not supported when using an inline JSX factory pragma": {
5034+
"An @jsxFrag pragma is required when using an @jsx pragma with JSX fragments.": {
50355035
"category": "Error",
50365036
"code": 17017
50375037
},
@@ -5837,5 +5837,13 @@
58375837
"Only numeric enums can have computed members, but this expression has type '{0}'. If you do not need exhaustiveness checks, consider using an object literal instead.": {
58385838
"category": "Error",
58395839
"code": 18033
5840+
},
5841+
"Specify the JSX fragment factory function to use when targeting 'react' JSX emit with 'jsxFactory' compiler option is specified, e.g. 'Fragment'.": {
5842+
"category": "Message",
5843+
"code": 18034
5844+
},
5845+
"Invalid value for 'jsxFragmentFactory'. '{0}' is not a valid identifier or qualified-name.": {
5846+
"category": "Error",
5847+
"code": 18035
58405848
}
58415849
}

src/compiler/emitter.ts

+1
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ namespace ts {
660660
getTypeReferenceDirectivesForSymbol: notImplemented,
661661
isLiteralConstDeclaration: notImplemented,
662662
getJsxFactoryEntity: notImplemented,
663+
getJsxFragmentFactoryEntity: notImplemented,
663664
getAllAccessorDeclarations: notImplemented,
664665
getSymbolOfExternalModuleSpecifier: notImplemented,
665666
isBindingCapturedByNode: notImplemented,

src/compiler/factory/utilities.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ namespace ts {
5555
);
5656
}
5757

58+
function createJsxFragmentFactoryExpression(factory: NodeFactory, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
59+
return jsxFragmentFactoryEntity ?
60+
createJsxFactoryExpressionFromEntityName(factory, jsxFragmentFactoryEntity, parent) :
61+
factory.createPropertyAccessExpression(
62+
createReactNamespace(reactNamespace, parent),
63+
"Fragment"
64+
);
65+
}
66+
5867
export function createExpressionForJsxElement(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, parentElement: JsxOpeningLikeElement, location: TextRange): LeftHandSideExpression {
5968
const argumentsList = [tagName];
6069
if (props) {
@@ -87,14 +96,9 @@ namespace ts {
8796
);
8897
}
8998

90-
export function createExpressionForJsxFragment(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, children: readonly Expression[], parentElement: JsxOpeningFragment, location: TextRange): LeftHandSideExpression {
91-
const tagName = factory.createPropertyAccessExpression(
92-
createReactNamespace(reactNamespace, parentElement),
93-
"Fragment"
94-
);
95-
96-
const argumentsList = [<Expression>tagName];
97-
argumentsList.push(factory.createNull());
99+
export function createExpressionForJsxFragment(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, children: readonly Expression[], parentElement: JsxOpeningFragment, location: TextRange): LeftHandSideExpression {
100+
const tagName = createJsxFragmentFactoryExpression(factory, jsxFragmentFactoryEntity, reactNamespace, parentElement);
101+
const argumentsList = [tagName, factory.createNull()];
98102

99103
if (children && children.length > 0) {
100104
if (children.length > 1) {
@@ -820,4 +824,4 @@ namespace ts {
820824
export function isStaticModifier(node: Modifier): node is StaticKeyword {
821825
return node.kind === SyntaxKind.StaticKeyword;
822826
}
823-
}
827+
}

src/compiler/parser.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -8568,7 +8568,9 @@ namespace ts {
85688568
});
85698569
break;
85708570
}
8571-
case "jsx": return; // Accessed directly
8571+
case "jsx":
8572+
case "jsxfrag":
8573+
return; // Accessed directly
85728574
default: Debug.fail("Unhandled pragma kind"); // Can this be made into an assertNever in the future?
85738575
}
85748576
});

src/compiler/program.ts

+9
Original file line numberDiff line numberDiff line change
@@ -3196,6 +3196,15 @@ namespace ts {
31963196
createOptionValueDiagnostic("reactNamespace", Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace);
31973197
}
31983198

3199+
if (options.jsxFragmentFactory) {
3200+
if (!options.jsxFactory) {
3201+
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory");
3202+
}
3203+
if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) {
3204+
createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory);
3205+
}
3206+
}
3207+
31993208
// If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
32003209
if (!options.noEmit && !options.suppressOutputPathCheck) {
32013210
const emitHost = getEmitHost();

src/compiler/transformers/jsx.ts

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ namespace ts {
142142
const element = createExpressionForJsxFragment(
143143
factory,
144144
context.getEmitResolver().getJsxFactoryEntity(currentSourceFile),
145+
context.getEmitResolver().getJsxFragmentFactoryEntity(currentSourceFile),
145146
compilerOptions.reactNamespace!, // TODO: GH#18217
146147
mapDefined(children, transformJsxChildToExpression),
147148
node,

src/compiler/types.ts

+8
Original file line numberDiff line numberDiff line change
@@ -3454,7 +3454,9 @@ namespace ts {
34543454
/* @internal */ version: string;
34553455
/* @internal */ pragmas: ReadonlyPragmaMap;
34563456
/* @internal */ localJsxNamespace?: __String;
3457+
/* @internal */ localJsxFragmentNamespace?: __String;
34573458
/* @internal */ localJsxFactory?: EntityName;
3459+
/* @internal */ localJsxFragmentFactory?: EntityName;
34583460

34593461
/* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit;
34603462
}
@@ -4463,6 +4465,7 @@ namespace ts {
44634465
getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[] | undefined;
44644466
isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean;
44654467
getJsxFactoryEntity(location?: Node): EntityName | undefined;
4468+
getJsxFragmentFactoryEntity(location?: Node): EntityName | undefined;
44664469
getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations;
44674470
getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined;
44684471
isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement): boolean;
@@ -5681,6 +5684,7 @@ namespace ts {
56815684
/* @internal */ pretty?: boolean;
56825685
reactNamespace?: string;
56835686
jsxFactory?: string;
5687+
jsxFragmentFactory?: string;
56845688
composite?: boolean;
56855689
incremental?: boolean;
56865690
tsBuildInfoFile?: string;
@@ -7928,6 +7932,10 @@ namespace ts {
79287932
args: [{ name: "factory" }],
79297933
kind: PragmaKindFlags.MultiLine
79307934
},
7935+
"jsxfrag": {
7936+
args: [{ name: "factory" }],
7937+
kind: PragmaKindFlags.MultiLine
7938+
},
79317939
} as const;
79327940

79337941
/* @internal */

src/testRunner/unittests/services/transpile.ts

+4
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,10 @@ var x = 0;`, {
363363
options: { compilerOptions: { jsxFactory: "createElement" }, fileName: "input.js", reportDiagnostics: true }
364364
});
365365

366+
transpilesCorrectly("Supports setting 'jsxFragmentFactory'", "x;", {
367+
options: { compilerOptions: { jsxFactory: "x", jsxFragmentFactory: "frag" }, fileName: "input.js", reportDiagnostics: true }
368+
});
369+
366370
transpilesCorrectly("Supports setting 'removeComments'", "x;", {
367371
options: { compilerOptions: { removeComments: true }, fileName: "input.js", reportDiagnostics: true }
368372
});

tests/baselines/reference/api/tsserverlibrary.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2770,6 +2770,7 @@ declare namespace ts {
27702770
project?: string;
27712771
reactNamespace?: string;
27722772
jsxFactory?: string;
2773+
jsxFragmentFactory?: string;
27732774
composite?: boolean;
27742775
incremental?: boolean;
27752776
tsBuildInfoFile?: string;

tests/baselines/reference/api/typescript.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2770,6 +2770,7 @@ declare namespace ts {
27702770
project?: string;
27712771
reactNamespace?: string;
27722772
jsxFactory?: string;
2773+
jsxFragmentFactory?: string;
27732774
composite?: boolean;
27742775
incremental?: boolean;
27752776
tsBuildInfoFile?: string;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//// [tests/cases/conformance/jsx/inline/inlineJsxAndJsxFragPragma.tsx] ////
2+
3+
//// [renderer.d.ts]
4+
declare global {
5+
namespace JSX {
6+
interface IntrinsicElements {
7+
[e: string]: any;
8+
}
9+
}
10+
}
11+
export function h(): void;
12+
export function jsx(): void;
13+
export function Fragment(): void;
14+
15+
//// [preacty.tsx]
16+
/**
17+
* @jsx h
18+
* @jsxFrag Fragment
19+
*/
20+
import {h, Fragment} from "./renderer";
21+
<><div></div></>
22+
23+
//// [snabbdomy.tsx]
24+
/* @jsx jsx */
25+
/* @jsxfrag null */
26+
import {jsx} from "./renderer";
27+
<><span></span></>
28+
29+
//// [preacty.js]
30+
"use strict";
31+
exports.__esModule = true;
32+
/**
33+
* @jsx h
34+
* @jsxFrag Fragment
35+
*/
36+
var renderer_1 = require("./renderer");
37+
renderer_1.h(renderer_1.Fragment, null,
38+
renderer_1.h("div", null));
39+
//// [snabbdomy.js]
40+
"use strict";
41+
exports.__esModule = true;
42+
/* @jsx jsx */
43+
/* @jsxfrag null */
44+
var renderer_1 = require("./renderer");
45+
renderer_1.jsx(null, null,
46+
renderer_1.jsx("span", null));

0 commit comments

Comments
 (0)