Skip to content

Commit

Permalink
reset soft with master for single commit (#38720)
Browse files Browse the repository at this point in the history
  • Loading branch information
Noj Vek authored Jun 18, 2020
1 parent c3c6be6 commit f697d26
Show file tree
Hide file tree
Showing 43 changed files with 815 additions and 46 deletions.
95 changes: 72 additions & 23 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -954,16 +954,32 @@ namespace ts {
if (location) {
const file = getSourceFileOfNode(location);
if (file) {
if (file.localJsxNamespace) {
return file.localJsxNamespace;
if (isJsxOpeningFragment(location)) {
if (file.localJsxFragmentNamespace) {
return file.localJsxFragmentNamespace;
}
const jsxFragmentPragma = file.pragmas.get("jsxfrag");
if (jsxFragmentPragma) {
const chosenPragma = isArray(jsxFragmentPragma) ? jsxFragmentPragma[0] : jsxFragmentPragma;
file.localJsxFragmentFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion);
visitNode(file.localJsxFragmentFactory, markAsSynthetic);
if (file.localJsxFragmentFactory) {
return file.localJsxFragmentNamespace = getFirstIdentifier(file.localJsxFragmentFactory).escapedText;
}
}
}
const jsxPragma = file.pragmas.get("jsx");
if (jsxPragma) {
const chosenpragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma;
file.localJsxFactory = parseIsolatedEntityName(chosenpragma.arguments.factory, languageVersion);
visitNode(file.localJsxFactory, markAsSynthetic);
if (file.localJsxFactory) {
return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText;
else {
if (file.localJsxNamespace) {
return file.localJsxNamespace;
}
const jsxPragma = file.pragmas.get("jsx");
if (jsxPragma) {
const chosenPragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma;
file.localJsxFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion);
visitNode(file.localJsxFactory, markAsSynthetic);
if (file.localJsxFactory) {
return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText;
}
}
}
}
Expand Down Expand Up @@ -23736,10 +23752,14 @@ namespace ts {
function checkJsxFragment(node: JsxFragment): Type {
checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment);

if (compilerOptions.jsx === JsxEmit.React && (compilerOptions.jsxFactory || getSourceFileOfNode(node).pragmas.has("jsx"))) {
// by default, jsx:'react' will use jsxFactory = React.createElement and jsxFragmentFactory = React.Fragment
// if jsxFactory compiler option is provided, ensure jsxFragmentFactory compiler option or @jsxFrag pragma is provided too
const nodeSourceFile = getSourceFileOfNode(node);
if (compilerOptions.jsx === JsxEmit.React && (compilerOptions.jsxFactory || nodeSourceFile.pragmas.has("jsx"))
&& !compilerOptions.jsxFragmentFactory && !nodeSourceFile.pragmas.has("jsxfrag")) {
error(node, compilerOptions.jsxFactory
? Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory
: Diagnostics.JSX_fragment_is_not_supported_when_using_an_inline_JSX_factory_pragma);
? Diagnostics.The_jsxFragmentFactory_compiler_option_must_be_provided_to_use_JSX_fragments_with_the_jsxFactory_compiler_option
: Diagnostics.An_jsxFrag_pragma_is_required_when_using_an_jsx_pragma_with_JSX_fragments);
}

checkJsxChildren(node);
Expand Down Expand Up @@ -24197,21 +24217,28 @@ namespace ts {
if (isNodeOpeningLikeElement) {
checkGrammarJsxElement(<JsxOpeningLikeElement>node);
}

checkJsxPreconditions(node);
// 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 = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
const reactNamespace = getJsxNamespace(node);
const reactLocation = isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>node).tagName : node;
const reactSym = resolveName(reactLocation, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true);
if (reactSym) {
const jsxFactoryRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
const jsxFactoryNamespace = getJsxNamespace(node);
const jsxFactoryLocation = isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>node).tagName : node;

// allow null as jsxFragmentFactory
let jsxFactorySym: Symbol | undefined;
if (!(isJsxOpeningFragment(node) && jsxFactoryNamespace === "null")) {
jsxFactorySym = resolveName(jsxFactoryLocation, jsxFactoryNamespace, SymbolFlags.Value, jsxFactoryRefErr, jsxFactoryNamespace, /*isUse*/ true);
}

if (jsxFactorySym) {
// Mark local symbol as referenced here because it might not have been marked
// if jsx emit was not react as there wont be error being emitted
reactSym.isReferenced = SymbolFlags.All;
// if jsx emit was not jsxFactory as there wont be error being emitted
jsxFactorySym.isReferenced = SymbolFlags.All;

// If react symbol is alias, mark it as referenced
if (reactSym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(reactSym)) {
markAliasSymbolAsReferenced(reactSym);
// If react/jsxFactory symbol is alias, mark it as refereced
if (jsxFactorySym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(jsxFactorySym)) {
markAliasSymbolAsReferenced(jsxFactorySym);
}
}

Expand Down Expand Up @@ -36728,10 +36755,31 @@ namespace ts {
return literalTypeToNode(<FreshableType>type, node, tracker);
}

function getJsxFactoryEntity(location: Node) {
function getJsxFactoryEntity(location: Node): EntityName | undefined {
return location ? (getJsxNamespace(location), (getSourceFileOfNode(location).localJsxFactory || _jsxFactoryEntity)) : _jsxFactoryEntity;
}

function getJsxFragmentFactoryEntity(location: Node): EntityName | undefined {
if (location) {
const file = getSourceFileOfNode(location);
if (file) {
if (file.localJsxFragmentFactory) {
return file.localJsxFragmentFactory;
}
const jsxFragPragmas = file.pragmas.get("jsxfrag");
const jsxFragPragma = isArray(jsxFragPragmas) ? jsxFragPragmas[0] : jsxFragPragmas;
if (jsxFragPragma) {
file.localJsxFragmentFactory = parseIsolatedEntityName(jsxFragPragma.arguments.factory, languageVersion);
return file.localJsxFragmentFactory;
}
}
}

if (compilerOptions.jsxFragmentFactory) {
return parseIsolatedEntityName(compilerOptions.jsxFragmentFactory, languageVersion);
}
}

function createResolver(): EmitResolver {
// this variable and functions that use it are deliberately moved here from the outer scope
// to avoid scope pollution
Expand Down Expand Up @@ -36806,6 +36854,7 @@ namespace ts {
return !!(symbol && getCheckFlags(symbol) & CheckFlags.Late);
},
getJsxFactoryEntity,
getJsxFragmentFactoryEntity,
getAllAccessorDeclarations(accessor: AccessorDeclaration): AllAccessorDeclarations {
accessor = getParseTreeNode(accessor, isGetOrSetAccessorDeclaration)!; // TODO: GH#18217
const otherKind = accessor.kind === SyntaxKind.SetAccessor ? SyntaxKind.GetAccessor : SyntaxKind.SetAccessor;
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,12 @@ namespace ts {
category: Diagnostics.Advanced_Options,
description: Diagnostics.Specify_the_JSX_factory_function_to_use_when_targeting_react_JSX_emit_e_g_React_createElement_or_h
},
{
name: "jsxFragmentFactory",
type: "string",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Specify_the_JSX_fragment_factory_function_to_use_when_targeting_react_JSX_emit_with_jsxFactory_compiler_option_is_specified_e_g_Fragment
},
{
name: "resolveJsonModule",
type: "boolean",
Expand Down
12 changes: 10 additions & 2 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -5027,11 +5027,11 @@
"category": "Error",
"code": 17015
},
"JSX fragment is not supported when using --jsxFactory": {
"The 'jsxFragmentFactory' compiler option must be provided to use JSX fragments with the 'jsxFactory' compiler option.": {
"category": "Error",
"code": 17016
},
"JSX fragment is not supported when using an inline JSX factory pragma": {
"An @jsxFrag pragma is required when using an @jsx pragma with JSX fragments.": {
"category": "Error",
"code": 17017
},
Expand Down Expand Up @@ -5837,5 +5837,13 @@
"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.": {
"category": "Error",
"code": 18033
},
"Specify the JSX fragment factory function to use when targeting 'react' JSX emit with 'jsxFactory' compiler option is specified, e.g. 'Fragment'.": {
"category": "Message",
"code": 18034
},
"Invalid value for 'jsxFragmentFactory'. '{0}' is not a valid identifier or qualified-name.": {
"category": "Error",
"code": 18035
}
}
1 change: 1 addition & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ namespace ts {
getTypeReferenceDirectivesForSymbol: notImplemented,
isLiteralConstDeclaration: notImplemented,
getJsxFactoryEntity: notImplemented,
getJsxFragmentFactoryEntity: notImplemented,
getAllAccessorDeclarations: notImplemented,
getSymbolOfExternalModuleSpecifier: notImplemented,
isBindingCapturedByNode: notImplemented,
Expand Down
22 changes: 13 additions & 9 deletions src/compiler/factory/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ namespace ts {
);
}

function createJsxFragmentFactoryExpression(factory: NodeFactory, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
return jsxFragmentFactoryEntity ?
createJsxFactoryExpressionFromEntityName(factory, jsxFragmentFactoryEntity, parent) :
factory.createPropertyAccessExpression(
createReactNamespace(reactNamespace, parent),
"Fragment"
);
}

export function createExpressionForJsxElement(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, parentElement: JsxOpeningLikeElement, location: TextRange): LeftHandSideExpression {
const argumentsList = [tagName];
if (props) {
Expand Down Expand Up @@ -87,14 +96,9 @@ namespace ts {
);
}

export function createExpressionForJsxFragment(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, children: readonly Expression[], parentElement: JsxOpeningFragment, location: TextRange): LeftHandSideExpression {
const tagName = factory.createPropertyAccessExpression(
createReactNamespace(reactNamespace, parentElement),
"Fragment"
);

const argumentsList = [<Expression>tagName];
argumentsList.push(factory.createNull());
export function createExpressionForJsxFragment(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, children: readonly Expression[], parentElement: JsxOpeningFragment, location: TextRange): LeftHandSideExpression {
const tagName = createJsxFragmentFactoryExpression(factory, jsxFragmentFactoryEntity, reactNamespace, parentElement);
const argumentsList = [tagName, factory.createNull()];

if (children && children.length > 0) {
if (children.length > 1) {
Expand Down Expand Up @@ -820,4 +824,4 @@ namespace ts {
export function isStaticModifier(node: Modifier): node is StaticKeyword {
return node.kind === SyntaxKind.StaticKeyword;
}
}
}
4 changes: 3 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8568,7 +8568,9 @@ namespace ts {
});
break;
}
case "jsx": return; // Accessed directly
case "jsx":
case "jsxfrag":
return; // Accessed directly
default: Debug.fail("Unhandled pragma kind"); // Can this be made into an assertNever in the future?
}
});
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3196,6 +3196,15 @@ namespace ts {
createOptionValueDiagnostic("reactNamespace", Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace);
}

if (options.jsxFragmentFactory) {
if (!options.jsxFactory) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory");
}
if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) {
createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory);
}
}

// 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();
Expand Down
1 change: 1 addition & 0 deletions src/compiler/transformers/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ namespace ts {
const element = createExpressionForJsxFragment(
factory,
context.getEmitResolver().getJsxFactoryEntity(currentSourceFile),
context.getEmitResolver().getJsxFragmentFactoryEntity(currentSourceFile),
compilerOptions.reactNamespace!, // TODO: GH#18217
mapDefined(children, transformJsxChildToExpression),
node,
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3454,7 +3454,9 @@ namespace ts {
/* @internal */ version: string;
/* @internal */ pragmas: ReadonlyPragmaMap;
/* @internal */ localJsxNamespace?: __String;
/* @internal */ localJsxFragmentNamespace?: __String;
/* @internal */ localJsxFactory?: EntityName;
/* @internal */ localJsxFragmentFactory?: EntityName;

/* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit;
}
Expand Down Expand Up @@ -4463,6 +4465,7 @@ namespace ts {
getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[] | undefined;
isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean;
getJsxFactoryEntity(location?: Node): EntityName | undefined;
getJsxFragmentFactoryEntity(location?: Node): EntityName | undefined;
getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations;
getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined;
isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement): boolean;
Expand Down Expand Up @@ -5681,6 +5684,7 @@ namespace ts {
/* @internal */ pretty?: boolean;
reactNamespace?: string;
jsxFactory?: string;
jsxFragmentFactory?: string;
composite?: boolean;
incremental?: boolean;
tsBuildInfoFile?: string;
Expand Down Expand Up @@ -7928,6 +7932,10 @@ namespace ts {
args: [{ name: "factory" }],
kind: PragmaKindFlags.MultiLine
},
"jsxfrag": {
args: [{ name: "factory" }],
kind: PragmaKindFlags.MultiLine
},
} as const;

/* @internal */
Expand Down
4 changes: 4 additions & 0 deletions src/testRunner/unittests/services/transpile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,10 @@ var x = 0;`, {
options: { compilerOptions: { jsxFactory: "createElement" }, fileName: "input.js", reportDiagnostics: true }
});

transpilesCorrectly("Supports setting 'jsxFragmentFactory'", "x;", {
options: { compilerOptions: { jsxFactory: "x", jsxFragmentFactory: "frag" }, fileName: "input.js", reportDiagnostics: true }
});

transpilesCorrectly("Supports setting 'removeComments'", "x;", {
options: { compilerOptions: { removeComments: true }, fileName: "input.js", reportDiagnostics: true }
});
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2770,6 +2770,7 @@ declare namespace ts {
project?: string;
reactNamespace?: string;
jsxFactory?: string;
jsxFragmentFactory?: string;
composite?: boolean;
incremental?: boolean;
tsBuildInfoFile?: string;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2770,6 +2770,7 @@ declare namespace ts {
project?: string;
reactNamespace?: string;
jsxFactory?: string;
jsxFragmentFactory?: string;
composite?: boolean;
incremental?: boolean;
tsBuildInfoFile?: string;
Expand Down
46 changes: 46 additions & 0 deletions tests/baselines/reference/inlineJsxAndJsxFragPragma.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//// [tests/cases/conformance/jsx/inline/inlineJsxAndJsxFragPragma.tsx] ////

//// [renderer.d.ts]
declare global {
namespace JSX {
interface IntrinsicElements {
[e: string]: any;
}
}
}
export function h(): void;
export function jsx(): void;
export function Fragment(): void;

//// [preacty.tsx]
/**
* @jsx h
* @jsxFrag Fragment
*/
import {h, Fragment} from "./renderer";
<><div></div></>

//// [snabbdomy.tsx]
/* @jsx jsx */
/* @jsxfrag null */
import {jsx} from "./renderer";
<><span></span></>

//// [preacty.js]
"use strict";
exports.__esModule = true;
/**
* @jsx h
* @jsxFrag Fragment
*/
var renderer_1 = require("./renderer");
renderer_1.h(renderer_1.Fragment, null,
renderer_1.h("div", null));
//// [snabbdomy.js]
"use strict";
exports.__esModule = true;
/* @jsx jsx */
/* @jsxfrag null */
var renderer_1 = require("./renderer");
renderer_1.jsx(null, null,
renderer_1.jsx("span", null));
Loading

0 comments on commit f697d26

Please sign in to comment.