Skip to content

Commit 32c63a2

Browse files
authored
Add support for transpiling per-file jsx pragmas (#21218)
* Add support for per-file jsx pragmas * Add error for using jsx factory pragma with fragments * More tests, use different regex class for pragma capture * Unify all pragma parsing machinery
1 parent 0b1e217 commit 32c63a2

23 files changed

+975
-159
lines changed

src/compiler/checker.ts

+24-6
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ namespace ts {
299299
resolveName(name, location, meaning, excludeGlobals) {
300300
return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false, excludeGlobals);
301301
},
302-
getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
302+
getJsxNamespace: n => unescapeLeadingUnderscores(getJsxNamespace(n)),
303303
getAccessibleSymbolChain,
304304
getTypePredicateOfSignature,
305305
resolveExternalModuleSymbol,
@@ -765,7 +765,23 @@ namespace ts {
765765
}
766766
}
767767

768-
function getJsxNamespace(): __String {
768+
function getJsxNamespace(location: Node | undefined): __String {
769+
if (location) {
770+
const file = getSourceFileOfNode(location);
771+
if (file) {
772+
if (file.localJsxNamespace) {
773+
return file.localJsxNamespace;
774+
}
775+
const jsxPragma = file.pragmas.get("jsx");
776+
if (jsxPragma) {
777+
const chosenpragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma;
778+
file.localJsxFactory = parseIsolatedEntityName(chosenpragma.arguments.factory, languageVersion);
779+
if (file.localJsxFactory) {
780+
return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText;
781+
}
782+
}
783+
}
784+
}
769785
if (!_jsxNamespace) {
770786
_jsxNamespace = "React" as __String;
771787
if (compilerOptions.jsxFactory) {
@@ -15082,8 +15098,10 @@ namespace ts {
1508215098
function checkJsxFragment(node: JsxFragment, checkMode: CheckMode): Type {
1508315099
checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment, checkMode);
1508415100

15085-
if (compilerOptions.jsx === JsxEmit.React && compilerOptions.jsxFactory) {
15086-
error(node, Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory);
15101+
if (compilerOptions.jsx === JsxEmit.React && (compilerOptions.jsxFactory || getSourceFileOfNode(node).pragmas.has("jsx"))) {
15102+
error(node, compilerOptions.jsxFactory
15103+
? Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory
15104+
: Diagnostics.JSX_fragment_is_not_supported_when_using_an_inline_JSX_factory_pragma);
1508715105
}
1508815106

1508915107
return getJsxGlobalElementType() || anyType;
@@ -15709,7 +15727,7 @@ namespace ts {
1570915727
// The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import.
1571015728
// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
1571115729
const reactRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
15712-
const reactNamespace = getJsxNamespace();
15730+
const reactNamespace = getJsxNamespace(node);
1571315731
const reactLocation = isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>node).tagName : node;
1571415732
const reactSym = resolveName(reactLocation, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true);
1571515733
if (reactSym) {
@@ -25556,7 +25574,7 @@ namespace ts {
2555625574
return !!(symbol && getCheckFlags(symbol) & CheckFlags.Late);
2555725575
},
2555825576
writeLiteralConstValue,
25559-
getJsxFactoryEntity: () => _jsxFactoryEntity
25577+
getJsxFactoryEntity: location => location ? (getJsxNamespace(location), (getSourceFileOfNode(location).localJsxFactory || _jsxFactoryEntity)) : _jsxFactoryEntity
2556025578
};
2556125579

2556225580
// defined here to avoid outer scope pollution

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3788,6 +3788,10 @@
37883788
"category": "Error",
37893789
"code": 17016
37903790
},
3791+
"JSX fragment is not supported when using an inline JSX factory pragma": {
3792+
"category": "Error",
3793+
"code": 17017
3794+
},
37913795

37923796
"Circularity detected while resolving configuration: {0}": {
37933797
"category": "Error",

src/compiler/factory.ts

+3
Original file line numberDiff line numberDiff line change
@@ -2375,6 +2375,9 @@ namespace ts {
23752375
if (node.resolvedTypeReferenceDirectiveNames !== undefined) updated.resolvedTypeReferenceDirectiveNames = node.resolvedTypeReferenceDirectiveNames;
23762376
if (node.imports !== undefined) updated.imports = node.imports;
23772377
if (node.moduleAugmentations !== undefined) updated.moduleAugmentations = node.moduleAugmentations;
2378+
if (node.pragmas !== undefined) updated.pragmas = node.pragmas;
2379+
if (node.localJsxFactory !== undefined) updated.localJsxFactory = node.localJsxFactory;
2380+
if (node.localJsxNamespace !== undefined) updated.localJsxNamespace = node.localJsxNamespace;
23782381
return updateNode(updated, node);
23792382
}
23802383

0 commit comments

Comments
 (0)