Skip to content

Commit

Permalink
feat: add jsxPragmaFrag support to typescript transform
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed Aug 11, 2020
1 parent df9ee2c commit 7e29bc1
Show file tree
Hide file tree
Showing 12 changed files with 70 additions and 16 deletions.
43 changes: 31 additions & 12 deletions packages/babel-plugin-transform-typescript/src/index.js
Expand Up @@ -47,15 +47,16 @@ export default declare(
(
api,
{
jsxPragma = "React",
jsxPragma = "React.createElement",
jsxPragmaFrag = "React.Fragment",
allowNamespaces = false,
allowDeclareFields = false,
onlyRemoveTypeImports = false,
},
) => {
api.assertVersion(7);

const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
const JSX_PRAGMA_REGEX = /\*?\s*@jsx((?:Frag)?)\s+([^\s]+)/;

const classMemberVisitors = {
field(path) {
Expand Down Expand Up @@ -157,16 +158,22 @@ 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());
}

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];
}
}
}
}
Expand All @@ -176,6 +183,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)) {
Expand Down Expand Up @@ -210,7 +222,8 @@ export default declare(
isImportTypeOnly({
binding,
programPath: path,
jsxPragma: pragmaImportName,
pragmaImportName,
pragmaFragImportName,
})
) {
importsToRemove.push(binding.path);
Expand Down Expand Up @@ -445,25 +458,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;
Expand Down
@@ -0,0 +1,4 @@
/* @jsxFrag jsx.htm */
// Don't elide htm if a JSX fragment appears somewhere.
import * as jsx from "fake-jsx-package";
<></>;
@@ -0,0 +1,3 @@
{
"plugins": [["transform-typescript", { "isTSX": true }]]
}
@@ -0,0 +1,4 @@
/* @jsxFrag jsx.htm */
// Don't elide htm if a JSX fragment appears somewhere.
import * as jsx from "fake-jsx-package";
<></>;
@@ -0,0 +1,4 @@
/* @jsxFrag htm */
// Don't elide htm if a JSX fragment appears somewhere.
import { htm } from "fake-jsx-package";
<></>;
@@ -0,0 +1,3 @@
{
"plugins": [["transform-typescript", { "isTSX": true }]]
}
@@ -0,0 +1,4 @@
/* @jsxFrag htm */
// Don't elide htm if a JSX fragment appears somewhere.
import { htm } from "fake-jsx-package";
<></>;
@@ -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>;
<></>;
@@ -1,3 +1,8 @@
{
"plugins": [["transform-typescript", { "jsxPragma": "h", "isTSX": true }]]
"plugins": [
[
"transform-typescript",
{ "jsxPragma": "h", "jsxPragmaFrag": "Fragment", "isTSX": true }
]
]
}
@@ -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>;
<></>;
@@ -1,2 +1,2 @@
import { FooBar, h } from "preact";
import { FooBar, h, Fragment } from "preact";
const x: FooBar = 0;
6 changes: 6 additions & 0 deletions packages/babel-preset-typescript/src/index.js
Expand Up @@ -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");
}
Expand All @@ -32,6 +37,7 @@ export default declare(
allowNamespaces,
isTSX,
jsxPragma,
jsxPragmaFrag,
onlyRemoveTypeImports,
});

Expand Down

0 comments on commit 7e29bc1

Please sign in to comment.