admin管理员组文章数量:1208155
I am trying to write a babel plugin to wrap every component with a Profiler. But it breaks react native.
This is my babel.config.js
module.exports = function (api) {
api.cache(false)
return {
presets: [
'babel-preset-expo', 'module:metro-react-native-babel-preset'
],
plugins: [
[ "./babel/babel-plugin-wrap-profiler/plugin.js"],
[
'babel-plugin-root-import',
{
paths: [
{
rootPathPrefix: ':ui/',
rootPathSuffix: './libs/ui/src/',
},
{
rootPathPrefix: ':data/',
rootPathSuffix: './libs/data/src/',
},
{
rootPathPrefix: ':utils/',
rootPathSuffix: './libs/utils/src/',
},
],
},
],
[
require.resolve('babel-plugin-module-resolver'),
{
alias: {
':common': './libs/common/src',
':ui': './libs/ui/src',
':data': './libs/data/src',
':utils': './libs/utils/src',
},
},
],
[
'@emotion',
{
// sourceMap is on by default but source maps are dead code eliminated in production
sourceMap: true,
autoLabel: 'dev-only',
labelFormat: '[local]',
cssPropOptimization: true,
},
],
['@babel/plugin-transform-object-assign'],
['@babel/plugin-transform-react-jsx-source'],
['macros'],
['react-native-reanimated/plugin'],
],
}
}
This is the plugin file
import * as fs from "fs"
import * as path from "path"
function checkIfIdentifierDefined(path, identifier) {
// Parse the code into an AST
let isDefined = false;
// Traverse the AST to check for the identifier
path.traverse({
VariableDeclarator(path) {
if (path.node.id.name === identifier) {
isDefined = true;
}
},
FunctionDeclaration(path) {
if (path.node.id && path.node.id.name === identifier) {
isDefined = true;
}
},
// Add other node types as needed (e.g., FunctionExpression, ArrowFunctionExpression, etc.)
});
return isDefined;
}
const handleProfilerImport = (t, path) => {
let profilerFound = false
let reactImportNode = null
let profilerImportName = 'Profiler'
if (checkIfIdentifierDefined(path, "Profiler")) {
// TODO: import with different name
return;
}
for (const n of path.node.body) {
if (n.type !== 'ImportDeclaration') continue;
if (n.source.value !== 'react') continue;
reactImportNode = n;
for (const specifier of n.specifiers) {
if (specifier.imported && specifier.local) {
if (specifier.imported.name === 'Profiler' || specifier.local.name === 'Profiler') {
profilerImportName = specifier.local.name;
profilerFound = true;
break;
}
if (!specifier.imported && specifier.local) {
if (specifier.local.name === 'Profiler') {
profilerFound = true;
break;
}
}
}
}
}
const importSpecifier = t.importSpecifier(
t.identifier('Profiler'),
t.identifier('Profiler')
)
if (!profilerFound) {
// react import found
if (reactImportNode !== null) {
reactImportNode.specifiers.push(importSpecifier)
} else {
// create es6 import
path.node.body.unshift(
t.importDeclaration(
[importSpecifier],
t.stringLiteral('react')
)
)
}
}
return profilerImportName
}
function enclosingFunctionName(path){
const functionPath = path.findParent((p) =>
p.isFunctionDeclaration() ||
p.isFunctionExpression() ||
p.isArrowFunctionExpression()
);
if (!functionPath) return
const node = functionPath.node;
if (node.type === 'FunctionDeclaration' && node.id) {
return node.id.name;
}
if (
(node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') &&
functionPath.parentPath.isVariableDeclarator()
) {
return functionPath.parentPath.node.id.name;
}
if (
node.type === 'FunctionExpression' &&
functionPath.parentPath.isObjectProperty()
) {
return functionPath.parentPath.node.key.name;
}
return "anonymous"
}
const _getComponentName = (scope, path) => {
if (scope.path.type === "ClassMethod") {
return scope.path.parentPath.parent.id.name
}
if (scope.path.type === "FunctionDeclaration") {
if (path.parent.type === "ReturnStatement") {
return enclosingFunctionName(path)
}
return scope.path.container.declaration.id.name
}
if (scope.path.type === "FunctionExpression") {
return scope.path.parentPath?.parentPath?.parentPath?.parent?.arguments?.find(arg => arg.type === "Identifier")?.name
}
return (scope.path?.container?.id?.name) ?? path.parent?.id?.name
}
const getComponentName = (scope, path) => {
return _getComponentName(scope, path) ?? enclosingFunctionName(path)
}
export default function ({ types: t, template }) {
const wrapWithProfiler = (jsx, componentName) => {
const Profiler = 'Profiler'
return template.ast`
React.createElement(
${Profiler},
{
id: '${componentName}',
onRender: onRenderCallBack$
},
${jsx.node}
)
`
}
return {
name: "wrap-profiler",
visitor: {
Program: {
enter(path, state) {
if (this?.file?.opts?.filename?.includes("node_modules")) {
return;
}
let hasJSX = true
path.traverse({
JSXElement: {
enter() {
hasJSX = true
}
}
})
if (!hasJSX) {
return
}
const profilerImport = handleProfilerImport(t, path)
path.unshiftContainer('body', t.importDeclaration(
[
t.importSpecifier(
t.identifier('onRenderCallBack$'),
t.identifier('onRenderCallBack$')
)
],
t.stringLiteral('babel-plugin-wrap-profiler/profiler-utils')
))
}
},
JSXElement: {
enter(path, state) {
if (this?.file?.opts?.filename?.includes("node_modules")) {
return;
}
const { parent, scope } = path
const topLevelReactComponent = parent.type === "ReturnStatement" || parent.type === "ArrowFunctionExpression"
if (topLevelReactComponent) {
// function component name
let componentName = getComponentName(scope, path)
// wrap top level react component with profiler
const newNodeAst = wrapWithProfiler(path, componentName)
path.replaceWith(newNodeAst)
path.skip();
}
}
}
}
}
}
I run below command to start the project.
rm -rf $TMPDIR/metro-cache && yarn start --clear
I get this error: Compiling JS Failed: import declaration must be at the top level of module.
The error is because of the wrapWithProfiler function but I assumed other plugins that run after me would transform that jsx into actual javascript. But I guess that's not happening, or maybe the wrapWithProfiler function is wrong.
本文标签: babeljsbabel plugin that outputs jsx breaks react nativeStack Overflow
版权声明:本文标题:babeljs - babel plugin that outputs jsx breaks react native - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738741419a2109872.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论