UNPKG

15.3 kBSource Map (JSON)View Raw
1{"version":3,"names":["_helperPluginUtils","require","_core","_default","exports","default","declare","api","options","assertVersion","allowMutablePropsOnTags","Array","isArray","Error","HOISTED","WeakMap","declares","node","scope","t","isJSXIdentifier","name","path","isFunctionParent","isArrowFunctionExpression","hasOwnBinding","isHoistingScope","isLoop","isProgram","getHoistingScope","parent","targetScopeVisitor","ReferencedIdentifier","state","jsxScope","targetScope","immutabilityVisitor","enter","_expressionResult$deo","stop","isImmutable","skip","isJSXClosingElement","parentPath","isJSXAttribute","isJSXMemberExpression","isJSXNamespacedName","isIdentifier","binding","getBinding","constant","mutablePropsAllowed","isFunction","traverse","isPure","expressionResult","evaluate","confident","value","deopt","hoistingVisitor","Object","assign","visitor","JSXElement","_jsxScope","has","openingElement","lastSegment","property","elementName","includes","current","isJSX","get","set","visitorState","getProgramParent","currentScope","id","generateUidBasedOnNode","push","identifier","replacement","template","expression","ast","isJSXElement","jsxExpressionContainer","replaceWith"],"sources":["../src/index.ts"],"sourcesContent":["import { declare } from \"@babel/helper-plugin-utils\";\nimport { types as t, template } from \"@babel/core\";\nimport type { Visitor, Scope, NodePath } from \"@babel/traverse\";\n\nexport interface Options {\n allowMutablePropsOnTags?: null | string[];\n}\n\ninterface VisitorState {\n isImmutable: boolean;\n mutablePropsAllowed: boolean;\n jsxScope: Scope;\n targetScope: Scope;\n}\nexport default declare((api, options: Options) => {\n api.assertVersion(REQUIRED_VERSION(7));\n\n const { allowMutablePropsOnTags } = options;\n\n if (\n allowMutablePropsOnTags != null &&\n !Array.isArray(allowMutablePropsOnTags)\n ) {\n throw new Error(\n \".allowMutablePropsOnTags must be an array, null, or undefined.\",\n );\n }\n\n // Element -> Target scope\n const HOISTED = new WeakMap();\n\n function declares(node: t.Identifier | t.JSXIdentifier, scope: Scope) {\n if (\n t.isJSXIdentifier(node, { name: \"this\" }) ||\n t.isJSXIdentifier(node, { name: \"arguments\" }) ||\n t.isJSXIdentifier(node, { name: \"super\" }) ||\n t.isJSXIdentifier(node, { name: \"new\" })\n ) {\n const { path } = scope;\n return path.isFunctionParent() && !path.isArrowFunctionExpression();\n }\n\n return scope.hasOwnBinding(node.name);\n }\n\n function isHoistingScope({ path }: Scope) {\n return path.isFunctionParent() || path.isLoop() || path.isProgram();\n }\n\n function getHoistingScope(scope: Scope) {\n while (!isHoistingScope(scope)) scope = scope.parent;\n return scope;\n }\n\n const targetScopeVisitor: Visitor<VisitorState> = {\n ReferencedIdentifier(path, state) {\n const { node } = path;\n let { scope } = path;\n\n while (scope !== state.jsxScope) {\n // If a binding is declared in an inner function, it doesn't affect hoisting.\n if (declares(node, scope)) return;\n\n scope = scope.parent;\n }\n\n while (scope) {\n // We cannot hoist outside of the previous hoisting target\n // scope, so we return early and we don't update it.\n if (scope === state.targetScope) return;\n\n // If the scope declares this identifier (or we're at the function\n // providing the lexical env binding), we can't hoist the var any\n // higher.\n if (declares(node, scope)) break;\n\n scope = scope.parent;\n }\n\n state.targetScope = getHoistingScope(scope);\n },\n };\n\n const immutabilityVisitor: Visitor<VisitorState> = {\n enter(path, state) {\n const stop = () => {\n state.isImmutable = false;\n path.stop();\n };\n\n const skip = () => {\n path.skip();\n };\n\n if (path.isJSXClosingElement()) {\n skip();\n return;\n }\n\n // Elements with refs are not safe to hoist.\n if (\n path.isJSXIdentifier({ name: \"ref\" }) &&\n path.parentPath.isJSXAttribute({ name: path.node })\n ) {\n stop();\n return;\n }\n\n // Ignore JSX expressions and immutable values.\n if (\n path.isJSXIdentifier() ||\n path.isJSXMemberExpression() ||\n path.isJSXNamespacedName() ||\n path.isImmutable()\n ) {\n return;\n }\n\n // Ignore constant bindings.\n if (path.isIdentifier()) {\n const binding = path.scope.getBinding(path.node.name);\n if (binding && binding.constant) return;\n }\n\n // If we allow mutable props, tags with function expressions can be\n // safely hoisted.\n const { mutablePropsAllowed } = state;\n if (mutablePropsAllowed && path.isFunction()) {\n path.traverse(targetScopeVisitor, state);\n skip();\n return;\n }\n\n if (!path.isPure()) {\n stop();\n return;\n }\n\n // If it's not immutable, it may still be a pure expression, such as string concatenation.\n // It is still safe to hoist that, so long as its result is immutable.\n // If not, it is not safe to replace as mutable values (like objects) could be mutated after render.\n // https://github.com/facebook/react/issues/3226\n const expressionResult = path.evaluate();\n if (expressionResult.confident) {\n // We know the result; check its mutability.\n const { value } = expressionResult;\n if (\n mutablePropsAllowed ||\n value === null ||\n (typeof value !== \"object\" && typeof value !== \"function\")\n ) {\n // It evaluated to an immutable value, so we can hoist it.\n skip();\n return;\n }\n } else if (expressionResult.deopt?.isIdentifier()) {\n // It's safe to hoist here if the deopt reason is an identifier (e.g. func param).\n // The hoister will take care of how high up it can be hoisted.\n return;\n }\n\n stop();\n },\n };\n\n // We cannot use traverse.visitors.merge because it doesn't support\n // immutabilityVisitor's bare `enter` visitor.\n // It's safe to just use ... because the two visitors don't share any key.\n const hoistingVisitor = { ...immutabilityVisitor, ...targetScopeVisitor };\n\n return {\n name: \"transform-react-constant-elements\",\n\n visitor: {\n JSXElement(path) {\n if (HOISTED.has(path.node)) return;\n const name = path.node.openingElement.name;\n\n // This transform takes the option `allowMutablePropsOnTags`, which is an array\n // of JSX tags to allow mutable props (such as objects, functions) on. Use sparingly\n // and only on tags you know will never modify their own props.\n let mutablePropsAllowed = false;\n if (allowMutablePropsOnTags != null) {\n // Get the element's name. If it's a member expression, we use the last part of the path.\n // So the option [\"FormattedMessage\"] would match \"Intl.FormattedMessage\".\n let lastSegment = name;\n while (t.isJSXMemberExpression(lastSegment)) {\n lastSegment = lastSegment.property;\n }\n\n const elementName = lastSegment.name;\n // @ts-expect-error Fixme: allowMutablePropsOnTags should handle JSXNamespacedName\n mutablePropsAllowed = allowMutablePropsOnTags.includes(elementName);\n }\n\n // In order to avoid hoisting unnecessarily, we need to know which is\n // the scope containing the current JSX element. If a parent of the\n // current element has already been hoisted, we can consider its target\n // scope as the base scope for the current element.\n let jsxScope;\n let current: NodePath<t.JSX> = path;\n while (!jsxScope && current.parentPath.isJSX()) {\n current = current.parentPath;\n jsxScope = HOISTED.get(current.node);\n }\n jsxScope ??= path.scope;\n // The initial HOISTED is set to jsxScope, s.t.\n // if the element's JSX ancestor has been hoisted, it will be skipped\n HOISTED.set(path.node, jsxScope);\n\n const visitorState: VisitorState = {\n isImmutable: true,\n mutablePropsAllowed,\n jsxScope,\n targetScope: path.scope.getProgramParent(),\n };\n path.traverse(hoistingVisitor, visitorState);\n if (!visitorState.isImmutable) return;\n\n const { targetScope } = visitorState;\n // Only hoist if it would give us an advantage.\n for (let currentScope = jsxScope; ; ) {\n if (targetScope === currentScope) return;\n if (isHoistingScope(currentScope)) break;\n\n currentScope = currentScope.parent;\n if (!currentScope) {\n throw new Error(\n \"Internal @babel/plugin-transform-react-constant-elements error: \" +\n \"targetScope must be an ancestor of jsxScope. \" +\n \"This is a Babel bug, please report it.\",\n );\n }\n }\n\n const id = path.scope.generateUidBasedOnNode(name);\n targetScope.push({ id: t.identifier(id) });\n // If the element is to be hoisted, update HOISTED to be the target scope\n HOISTED.set(path.node, targetScope);\n\n let replacement: t.Expression | t.JSXExpressionContainer = template\n .expression.ast`\n ${t.identifier(id)} || (${t.identifier(id)} = ${path.node})\n `;\n if (\n path.parentPath.isJSXElement() ||\n path.parentPath.isJSXAttribute()\n ) {\n replacement = t.jsxExpressionContainer(replacement);\n }\n\n path.replaceWith(replacement);\n },\n },\n };\n});\n"],"mappings":"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","ignoreList":[]}
\No newline at end of file