Add jsxPragmaFrag support to typescript transform (#11950)

This commit is contained in:
Huáng Jùnliàng 2020-10-14 14:12:57 -04:00 committed by GitHub
parent 94b5f92e39
commit d0d1fdb921
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 84 additions and 16 deletions

View File

@ -49,7 +49,8 @@ export default declare(
(
api,
{
jsxPragma = "React",
jsxPragma = "React.createElement",
jsxPragmaFrag = "React.Fragment",
allowNamespaces = false,
allowDeclareFields = false,
onlyRemoveTypeImports = false,
@ -57,7 +58,7 @@ export default declare(
) => {
api.assertVersion(7);
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
const JSX_PRAGMA_REGEX = /\*?\s*@jsx((?:Frag)?)\s+([^\s]+)/;
const classMemberVisitors = {
field(path) {
@ -168,6 +169,7 @@ export default declare(
Program(path, state) {
const { file } = state;
let fileJsxPragma = null;
let fileJsxPragmaFrag = null;
if (!GLOBAL_TYPES.has(path.node)) {
GLOBAL_TYPES.set(path.node, new Set());
@ -175,9 +177,14 @@ export default declare(
if (file.ast.comments) {
for (const comment of (file.ast.comments: Array<Object>)) {
const jsxMatches = JSX_ANNOTATION_REGEX.exec(comment.value);
const jsxMatches = JSX_PRAGMA_REGEX.exec(comment.value);
if (jsxMatches) {
fileJsxPragma = jsxMatches[1];
if (jsxMatches[1]) {
// isFragment
fileJsxPragmaFrag = jsxMatches[2];
} else {
fileJsxPragma = jsxMatches[2];
}
}
}
}
@ -187,6 +194,11 @@ export default declare(
[pragmaImportName] = pragmaImportName.split(".");
}
let pragmaFragImportName = fileJsxPragmaFrag || jsxPragmaFrag;
if (pragmaFragImportName) {
[pragmaFragImportName] = pragmaFragImportName.split(".");
}
// remove type imports
for (let stmt of path.get("body")) {
if (t.isImportDeclaration(stmt)) {
@ -221,7 +233,8 @@ export default declare(
isImportTypeOnly({
binding,
programPath: path,
jsxPragma: pragmaImportName,
pragmaImportName,
pragmaFragImportName,
})
) {
importsToRemove.push(binding.path);
@ -456,25 +469,31 @@ export default declare(
// 'access' and 'readonly' are only for parameter properties, so constructor visitor will handle them.
}
function isImportTypeOnly({ binding, programPath, jsxPragma }) {
function isImportTypeOnly({
binding,
programPath,
pragmaImportName,
pragmaFragImportName,
}) {
for (const path of binding.referencePaths) {
if (!isInType(path)) {
return false;
}
}
if (binding.identifier.name !== jsxPragma) {
if (
binding.identifier.name !== pragmaImportName &&
binding.identifier.name !== pragmaFragImportName
) {
return true;
}
// "React" or the JSX pragma is referenced as a value if there are any JSX elements in the code.
// "React" or the JSX pragma is referenced as a value if there are any JSX elements/fragments in the code.
let sourceFileHasJsx = false;
programPath.traverse({
JSXElement() {
sourceFileHasJsx = true;
},
JSXFragment() {
"JSXElement|JSXFragment"(path) {
sourceFileHasJsx = true;
path.stop();
},
});
return !sourceFileHasJsx;

View File

@ -0,0 +1,4 @@
/* @jsxFrag jsx.htm */
// Don't elide htm if a JSX fragment appears somewhere.
import * as jsx from "fake-jsx-package";
<></>;

View File

@ -0,0 +1,3 @@
{
"plugins": [["transform-typescript", { "isTSX": true }]]
}

View File

@ -0,0 +1,4 @@
/* @jsxFrag jsx.htm */
// Don't elide htm if a JSX fragment appears somewhere.
import * as jsx from "fake-jsx-package";
<></>;

View File

@ -0,0 +1,4 @@
/* @jsxFrag htm */
// Don't elide htm if a JSX fragment appears somewhere.
import { htm } from "fake-jsx-package";
<></>;

View File

@ -0,0 +1,3 @@
{
"plugins": [["transform-typescript", { "isTSX": true }]]
}

View File

@ -0,0 +1,4 @@
/* @jsxFrag htm */
// Don't elide htm if a JSX fragment appears somewhere.
import { htm } from "fake-jsx-package";
<></>;

View File

@ -0,0 +1,3 @@
// Don't elide Preact if a JSX fragment appears somewhere.
import { Fragment, render } from "preact";
<></>;

View File

@ -0,0 +1,8 @@
{
"plugins": [
[
"transform-typescript",
{ "jsxPragma": "h", "jsxPragmaFrag": "Fragment", "isTSX": true }
]
]
}

View File

@ -0,0 +1,3 @@
// Don't elide Preact if a JSX fragment appears somewhere.
import { Fragment } from "preact";
<></>;

View File

@ -1,3 +1,4 @@
// Don't elide Preact if a JSX element appears somewhere.
import { h, render } from "preact";
import { h, Fragment, render } from "preact";
<div></div>;
<></>;

View File

@ -1,3 +1,8 @@
{
"plugins": [["transform-typescript", { "jsxPragma": "h", "isTSX": true }]]
"plugins": [
[
"transform-typescript",
{ "jsxPragma": "h", "jsxPragmaFrag": "Fragment", "isTSX": true }
]
]
}

View File

@ -1,3 +1,4 @@
// Don't elide Preact if a JSX element appears somewhere.
import { h } from "preact";
import { h, Fragment } from "preact";
<div></div>;
<></>;

View File

@ -1,2 +1,2 @@
import { FooBar, h } from "preact";
import { FooBar, h, Fragment } from "preact";
const x: FooBar = 0;

View File

@ -9,12 +9,17 @@ export default declare(
allowDeclareFields,
allowNamespaces,
jsxPragma,
jsxPragmaFrag = "React.Fragment",
isTSX = false,
onlyRemoveTypeImports,
},
) => {
api.assertVersion(7);
if (typeof jsxPragmaFrag !== "string") {
throw new Error(".jsxPragmaFrag must be a string, or undefined");
}
if (typeof allExtensions !== "boolean") {
throw new Error(".allExtensions must be a boolean, or undefined");
}
@ -32,6 +37,7 @@ export default declare(
allowNamespaces,
isTSX,
jsxPragma,
jsxPragmaFrag,
onlyRemoveTypeImports,
});