Skip to content

Commit

Permalink
Add support for transpiling per-file jsx pragmas (#21218)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
weswigham authored Feb 27, 2018
1 parent 0b1e217 commit 32c63a2
Show file tree
Hide file tree
Showing 23 changed files with 975 additions and 159 deletions.
30 changes: 24 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ namespace ts {
resolveName(name, location, meaning, excludeGlobals) {
return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false, excludeGlobals);
},
getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
getJsxNamespace: n => unescapeLeadingUnderscores(getJsxNamespace(n)),
getAccessibleSymbolChain,
getTypePredicateOfSignature,
resolveExternalModuleSymbol,
Expand Down Expand Up @@ -765,7 +765,23 @@ namespace ts {
}
}

function getJsxNamespace(): __String {
function getJsxNamespace(location: Node | undefined): __String {
if (location) {
const file = getSourceFileOfNode(location);
if (file) {
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);
if (file.localJsxFactory) {
return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText;
}
}
}
}
if (!_jsxNamespace) {
_jsxNamespace = "React" as __String;
if (compilerOptions.jsxFactory) {
Expand Down Expand Up @@ -15082,8 +15098,10 @@ namespace ts {
function checkJsxFragment(node: JsxFragment, checkMode: CheckMode): Type {
checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment, checkMode);

if (compilerOptions.jsx === JsxEmit.React && compilerOptions.jsxFactory) {
error(node, Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory);
if (compilerOptions.jsx === JsxEmit.React && (compilerOptions.jsxFactory || getSourceFileOfNode(node).pragmas.has("jsx"))) {
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);
}

return getJsxGlobalElementType() || anyType;
Expand Down Expand Up @@ -15709,7 +15727,7 @@ namespace ts {
// 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();
const reactNamespace = getJsxNamespace(node);
const reactLocation = isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>node).tagName : node;
const reactSym = resolveName(reactLocation, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true);
if (reactSym) {
Expand Down Expand Up @@ -25556,7 +25574,7 @@ namespace ts {
return !!(symbol && getCheckFlags(symbol) & CheckFlags.Late);
},
writeLiteralConstValue,
getJsxFactoryEntity: () => _jsxFactoryEntity
getJsxFactoryEntity: location => location ? (getJsxNamespace(location), (getSourceFileOfNode(location).localJsxFactory || _jsxFactoryEntity)) : _jsxFactoryEntity
};

// defined here to avoid outer scope pollution
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3788,6 +3788,10 @@
"category": "Error",
"code": 17016
},
"JSX fragment is not supported when using an inline JSX factory pragma": {
"category": "Error",
"code": 17017
},

"Circularity detected while resolving configuration: {0}": {
"category": "Error",
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2375,6 +2375,9 @@ namespace ts {
if (node.resolvedTypeReferenceDirectiveNames !== undefined) updated.resolvedTypeReferenceDirectiveNames = node.resolvedTypeReferenceDirectiveNames;
if (node.imports !== undefined) updated.imports = node.imports;
if (node.moduleAugmentations !== undefined) updated.moduleAugmentations = node.moduleAugmentations;
if (node.pragmas !== undefined) updated.pragmas = node.pragmas;
if (node.localJsxFactory !== undefined) updated.localJsxFactory = node.localJsxFactory;
if (node.localJsxNamespace !== undefined) updated.localJsxNamespace = node.localJsxNamespace;
return updateNode(updated, node);
}

Expand Down
Loading

0 comments on commit 32c63a2

Please sign in to comment.