babel-plugin-transform-react-create-element
Advanced tools
Comparing version 0.1.0 to 0.2.0
{ | ||
"name": "babel-plugin-transform-react-create-element", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Shorten JSX React.createElement calls using a local variable.", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -49,5 +49,5 @@ # babel-plugin-transform-react-create-element | ||
It works by inserting a JSX pragma that points to the local variable. Hence, it needs to run before any JSX compilers. If you're using `@babel/preset-react` everything will work out of the box. If you're using `@babel/plugin-transform-react-jsx` directly, make sure to list this plugin before it. | ||
It works by translating any found calls to `React.createElement` to use the newly inserted local variable instead. | ||
This plugin _does_ respect any existing JSX pragmas in the file and does not modify them. | ||
As such, it automatically respects any existing JSX pragmas in the file and does not interfer with their use. | ||
@@ -63,1 +63,3 @@ ## Why? | ||
MIT | ||
Loosely based on https://github.com/facebook/create-react-app/pull/6219. |
@@ -1,7 +0,11 @@ | ||
import { Visitor } from "@babel/core"; | ||
import * as t from "@babel/types"; | ||
declare const transform: () => { | ||
name: string; | ||
visitor: Visitor<{}>; | ||
visitor: { | ||
CallExpression(path: any, state: { | ||
localVariableIdent: t.Identifier; | ||
}): void; | ||
}; | ||
}; | ||
export default transform; | ||
//# sourceMappingURL=index.d.ts.map |
134
src/index.js
@@ -5,74 +5,76 @@ "use strict"; | ||
const template_1 = tslib_1.__importDefault(require("@babel/template")); | ||
const PRAGMA_ELEM_NAME = "createElement"; | ||
const LOCAL_VARIABLE_TEMPLATE = template_1.default(` | ||
const %%pragma%% = React.createElement; | ||
`); | ||
const PRAGMA_ELEM_NAME = "createElement"; | ||
const transform = () => { | ||
let createElementIdent; | ||
let hasInsertedJsxLocalVariable = false; | ||
let usesJsx = false; | ||
const checkUsesJsxVisitor = { | ||
JSX() { | ||
usesJsx = true; | ||
}, | ||
}; | ||
const insertingVisitor = { | ||
ImportDeclaration(path) { | ||
if (path.node.source.value !== "react" || hasInsertedJsxLocalVariable) { | ||
const %%pragma%% = /*#__PURE__*/ React.createElement; | ||
`, { | ||
preserveComments: true, | ||
}); | ||
const getReactImport = (path) => { | ||
const binding = path.scope.getBinding(path.node.name); | ||
if (!binding) { | ||
return null; | ||
} | ||
const bindingPath = binding.path; | ||
if (bindingPath.isImportDefaultSpecifier()) { | ||
const parentPath = bindingPath.parentPath; | ||
if (parentPath.isImportDeclaration() && | ||
parentPath.node.source.value === "react") { | ||
return bindingPath; | ||
} | ||
} | ||
else if (bindingPath.isVariableDeclarator()) { | ||
const init = bindingPath.get("init"); | ||
if (init.isCallExpression() && | ||
init.node.callee.isIdentifier() && | ||
init.node.callee.name === "require" && | ||
init.node.arguments.length === 1 && | ||
init.node.arguments[0].isStringLiteral() && | ||
init.node.arguments[0].value === "react") { | ||
return bindingPath; | ||
} | ||
} | ||
return null; | ||
}; | ||
const getCreateElementAndImport = (path) => { | ||
const callee = path.get("callee"); | ||
if (!callee.isMemberExpression()) { | ||
return null; | ||
} | ||
const property = callee.get("property"); | ||
if (!property.isIdentifier() || property.node.name !== "createElement") { | ||
return null; | ||
} | ||
const object = callee.get("object"); | ||
const reactImport = getReactImport(object); | ||
if (!reactImport) { | ||
return null; | ||
} | ||
return [callee, reactImport]; | ||
}; | ||
const insertLocalVariable = (importSite) => { | ||
const uniqueIdent = importSite.scope.generateUidIdentifier(PRAGMA_ELEM_NAME); | ||
const variableDeclaration = LOCAL_VARIABLE_TEMPLATE({ | ||
pragma: uniqueIdent, | ||
}); | ||
const [node] = importSite.parentPath.insertAfter(variableDeclaration); | ||
importSite.parentPath.scope.registerDeclaration(node); | ||
return uniqueIdent; | ||
}; | ||
const transform = () => ({ | ||
name: "transform-create-element", | ||
visitor: { | ||
CallExpression(path, state) { | ||
const data = getCreateElementAndImport(path); | ||
if (!data) { | ||
return; | ||
} | ||
const defaultImportSpec = path.node.specifiers.find((spec) => spec.type === "ImportDefaultSpecifier"); | ||
if (!defaultImportSpec) { | ||
return; | ||
const [callee, importSite] = data; | ||
if (!state.localVariableIdent) { | ||
state.localVariableIdent = insertLocalVariable(importSite); | ||
} | ||
const spec = defaultImportSpec; | ||
if (spec.local.name !== "React") { | ||
return; | ||
} | ||
path.insertAfter(LOCAL_VARIABLE_TEMPLATE({ pragma: createElementIdent })); | ||
hasInsertedJsxLocalVariable = true; | ||
callee.replaceWith(state.localVariableIdent); | ||
}, | ||
}; | ||
const programVisitor = { | ||
Program(path, { file }) { | ||
var _a; | ||
/* | ||
* Check if we already have an existing pragma in the source code and if so, | ||
* leave the file be. | ||
*/ | ||
const alreadyContainsJsxPragma = file.ast.comments.some((c) => c.value.indexOf("@jsx") !== -1); | ||
if (alreadyContainsJsxPragma) { | ||
return; | ||
} | ||
/* | ||
* Leave the file be if it doesn't use any JSX. | ||
*/ | ||
path.traverse(checkUsesJsxVisitor); | ||
if (!usesJsx) { | ||
return; | ||
} | ||
createElementIdent = path.scope.generateUidIdentifier(PRAGMA_ELEM_NAME); | ||
path.traverse(insertingVisitor); | ||
if (!hasInsertedJsxLocalVariable) { | ||
return; | ||
} | ||
const commentText = `* @jsx ${createElementIdent.name} `; | ||
path.addComment("leading", commentText); | ||
/* | ||
* Because babel doesn't reparse the source code, our new comment does not | ||
* show up in `file.ast.comments`, where @babel/plugin-transform-react-jsx | ||
* expects it. | ||
* | ||
* Hence add it manually. | ||
*/ | ||
const insertedCommentNode = (_a = path.node.leadingComments) === null || _a === void 0 ? void 0 : _a.find((c) => c.value === commentText); | ||
file.ast.comments.push(insertedCommentNode); | ||
}, | ||
}; | ||
return { | ||
name: "transform-react-imports", | ||
visitor: programVisitor, | ||
}; | ||
}; | ||
}, | ||
}); | ||
exports.default = transform; | ||
//# sourceMappingURL=index.js.map |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
8817
88
64