michangarrito/apps/backend/node_modules/metro-transform-plugins/src/constant-folding-plugin.js.flow
rckrdmrd 97f407c661 [MIGRATION-V2] feat: Migrar michangarrito a estructura v2
- Prefijo v2: MCH
- TRACEABILITY-MASTER.yml creado
- Listo para integracion como submodulo

Workspace: v2.0.0 | SIMCO: v4.0.0
2026-01-10 11:28:54 -06:00

223 lines
6.2 KiB
Plaintext

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
import type {PluginObj} from '@babel/core';
import type {NodePath, VisitNode, Visitor} from '@babel/traverse';
import typeof Traverse from '@babel/traverse';
// This is only a typeof import, no runtime dependency exists
// eslint-disable-next-line import/no-extraneous-dependencies
import typeof * as Types from '@babel/types';
type State = {stripped: boolean};
export default function constantFoldingPlugin(context: {
types: Types,
traverse: Traverse,
...
}): PluginObj<State> {
const t = context.types;
const {isLiteral, isVariableDeclarator, isUnaryExpression} = t;
const traverse = context.traverse;
const evaluate = function (path: NodePath<>): {
confident: boolean,
value: mixed,
} {
const state = {safe: true};
const unsafe = (
path:
| NodePath<BabelNodeAssignmentExpression>
| NodePath<BabelNodeCallExpression>
| NodePath<BabelNodeOptionalCallExpression>,
state: {safe: boolean},
) => {
state.safe = false;
};
if (isUnaryExpression(path.node) && path.node.operator === 'void') {
// Void expressions always evaluate to undefined but would rarely be used
// to express a constant (with the exception of `void 0`). More often,
// they are used to discard the value of a side-effectful expression, so
// are unsafe to fold. Conservatively, evaluate to undefined only if the
// argument is a literal.
if (isLiteral(path.node.argument)) {
return {confident: true, value: undefined};
}
return {confident: false, value: null};
}
path.traverse(
{
AssignmentExpression: unsafe,
CallExpression: unsafe,
/**
* This will mark `foo?.()` as unsafe, so it is not replaced with `undefined` down the line.
*
* We saw this case in the wild, where the unary expression `void foo?.()` was replaced with `undefined`
* resulting in the expression call being skipped.
*/
OptionalCallExpression: unsafe,
},
state,
);
try {
if (!state.safe) {
return {confident: false, value: null};
}
const evaluated = path.evaluate();
return {confident: evaluated.confident, value: evaluated.value};
} catch {
return {confident: false, value: null};
}
};
const FunctionDeclaration = {
exit(path: NodePath<BabelNodeFunctionDeclaration>, state: State): void {
const binding =
path.node.id != null && path.scope.parent.getBinding(path.node.id.name);
if (binding && !binding.referenced) {
state.stripped = true;
path.remove();
}
},
};
const FunctionExpression: VisitNode<
BabelNodeFunctionExpression | BabelNodeArrowFunctionExpression,
State,
> =
// $FlowFixMe[incompatible-type]
{
exit(path, state) {
const parentPath = path.parentPath;
const parentNode = parentPath?.node;
if (isVariableDeclarator(parentNode) && parentNode.id.name != null) {
const binding = parentPath?.scope.getBinding(parentNode.id.name);
if (binding && !binding.referenced) {
state.stripped = true;
parentPath?.remove();
}
}
},
};
const Conditional: VisitNode<
BabelNodeIfStatement | BabelNodeConditionalExpression,
State,
> =
// $FlowFixMe[incompatible-type]
{
exit(path, state): void {
const node = path.node;
const result = evaluate(path.get('test'));
if (result.confident) {
state.stripped = true;
if (result.value || node.alternate) {
// $FlowFixMe[incompatible-type]
// $FlowFixMe[sketchy-null-mixed] Flow error uncovered by typing Babel more strictly
path.replaceWith(result.value ? node.consequent : node.alternate);
} else if (!result.value) {
path.remove();
}
}
},
};
const Expression: VisitNode<
BabelNodeUnaryExpression | BabelNodeBinaryExpression,
State,
> =
// $FlowFixMe[incompatible-type]
{
exit(path) {
const result = evaluate(path);
if (result.confident) {
path.replaceWith(t.valueToNode(result.value));
path.skip();
}
},
};
const LogicalExpression = {
exit(path: NodePath<BabelNodeLogicalExpression>) {
const node = path.node;
const result = evaluate(path.get('left'));
if (result.confident) {
const value = result.value;
switch (node.operator) {
case '||':
path.replaceWith(value ? node.left : node.right);
break;
case '&&':
path.replaceWith(value ? node.right : node.left);
break;
case '??':
path.replaceWith(value == null ? node.right : node.left);
break;
}
}
},
};
const Program = {
enter(path: NodePath<BabelNodeProgram>, state: State): void {
state.stripped = false;
},
exit(path: NodePath<BabelNodeProgram>, state: State): void {
path.traverse(
{
ArrowFunctionExpression: FunctionExpression,
ConditionalExpression: Conditional,
// $FlowFixMe[incompatible-type]
FunctionDeclaration,
FunctionExpression,
IfStatement: Conditional,
},
state,
);
if (state.stripped) {
traverse.cache.clearScope();
path.scope.crawl();
// Re-traverse all program, if we removed any blocks. Manually re-call
// enter and exit, because traversing a Program node won't call them.
Program.enter(path, state);
path.traverse(visitor, {stripped: false});
Program.exit(path, state);
}
},
};
const visitor: Visitor<State> = {
BinaryExpression: Expression,
// $FlowFixMe[incompatible-type]
LogicalExpression,
Program: {...Program}, // Babel mutates objects passed.
UnaryExpression: Expression,
};
return {visitor};
}