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, api,
{ {
jsxPragma = "React", jsxPragma = "React.createElement",
jsxPragmaFrag = "React.Fragment",
allowNamespaces = false, allowNamespaces = false,
allowDeclareFields = false, allowDeclareFields = false,
onlyRemoveTypeImports = false, onlyRemoveTypeImports = false,
@ -57,7 +58,7 @@ export default declare(
) => { ) => {
api.assertVersion(7); api.assertVersion(7);
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/; const JSX_PRAGMA_REGEX = /\*?\s*@jsx((?:Frag)?)\s+([^\s]+)/;
const classMemberVisitors = { const classMemberVisitors = {
field(path) { field(path) {
@ -168,6 +169,7 @@ export default declare(
Program(path, state) { Program(path, state) {
const { file } = state; const { file } = state;
let fileJsxPragma = null; let fileJsxPragma = null;
let fileJsxPragmaFrag = null;
if (!GLOBAL_TYPES.has(path.node)) { if (!GLOBAL_TYPES.has(path.node)) {
GLOBAL_TYPES.set(path.node, new Set()); GLOBAL_TYPES.set(path.node, new Set());
@ -175,9 +177,14 @@ export default declare(
if (file.ast.comments) { if (file.ast.comments) {
for (const comment of (file.ast.comments: Array<Object>)) { 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) { 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("."); [pragmaImportName] = pragmaImportName.split(".");
} }
let pragmaFragImportName = fileJsxPragmaFrag || jsxPragmaFrag;
if (pragmaFragImportName) {
[pragmaFragImportName] = pragmaFragImportName.split(".");
}
// remove type imports // remove type imports
for (let stmt of path.get("body")) { for (let stmt of path.get("body")) {
if (t.isImportDeclaration(stmt)) { if (t.isImportDeclaration(stmt)) {
@ -221,7 +233,8 @@ export default declare(
isImportTypeOnly({ isImportTypeOnly({
binding, binding,
programPath: path, programPath: path,
jsxPragma: pragmaImportName, pragmaImportName,
pragmaFragImportName,
}) })
) { ) {
importsToRemove.push(binding.path); 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. // '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) { for (const path of binding.referencePaths) {
if (!isInType(path)) { if (!isInType(path)) {
return false; return false;
} }
} }
if (binding.identifier.name !== jsxPragma) { if (
binding.identifier.name !== pragmaImportName &&
binding.identifier.name !== pragmaFragImportName
) {
return true; 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; let sourceFileHasJsx = false;
programPath.traverse({ programPath.traverse({
JSXElement() { "JSXElement|JSXFragment"(path) {
sourceFileHasJsx = true;
},
JSXFragment() {
sourceFileHasJsx = true; sourceFileHasJsx = true;
path.stop();
}, },
}); });
return !sourceFileHasJsx; 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. // Don't elide Preact if a JSX element appears somewhere.
import { h, render } from "preact"; import { h, Fragment, render } from "preact";
<div></div>; <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. // Don't elide Preact if a JSX element appears somewhere.
import { h } from "preact"; import { h, Fragment } from "preact";
<div></div>; <div></div>;
<></>;

View File

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

View File

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