Files
FrenoCorp/node_modules/metro-transform-plugins/src/inline-requires-plugin.js.flow
Michael Freno 7c684a42cc FRE-600: Fix code review blockers
- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 00:08:01 -04:00

498 lines
14 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 strict
* @format
*/
import type {PluginObj} from '@babel/core';
import typeof * as Babel from '@babel/core';
import type {NodePath, Scope} from '@babel/traverse';
import type {Program} from '@babel/types';
type Types = Babel['types'];
export type PluginOptions = Readonly<{
ignoredRequires?: ReadonlyArray<string>,
inlineableCalls?: ReadonlyArray<string>,
nonMemoizedModules?: ReadonlyArray<string>,
memoizeCalls?: boolean,
}>;
export type State = {
opts?: PluginOptions,
ignoredRequires: Set<string>,
inlineableCalls: Set<string>,
membersAssigned: Map<string, Set<string>>,
...
};
/**
* This transform inlines top-level require(...) aliases with to enable lazy
* loading of dependencies. It is able to inline both single references and
* child property references.
*
* For instance:
* var Foo = require('foo');
* f(Foo);
*
* Will be transformed into:
* f(require('foo'));
*
* When the assigment expression has a property access, it will be inlined too,
* keeping the property. For instance:
* var Bar = require('foo').bar;
* g(Bar);
*
* Will be transformed into:
* g(require('foo').bar);
*
* Destructuring also works the same way. For instance:
* const {Baz} = require('foo');
* h(Baz);
*
* Is also successfully inlined into:
* g(require('foo').Baz);
*/
export default ({types: t, traverse}: Babel): PluginObj<State> => ({
name: 'inline-requires',
visitor: {
Program: {
enter() {},
exit(path: NodePath<Program>, state: State): void {
const ignoredRequires = new Set<string>();
const inlineableCalls = new Set(['require']);
const nonMemoizedModules = new Set<string>();
let memoizeCalls = false;
const opts = state.opts;
if (opts != null) {
opts.ignoredRequires?.forEach(name => ignoredRequires.add(name));
opts.inlineableCalls?.forEach(name => inlineableCalls.add(name));
opts.nonMemoizedModules?.forEach(name =>
nonMemoizedModules.add(name),
);
memoizeCalls = opts.memoizeCalls ?? false;
}
const programNode = path.scope.block;
if (programNode.type !== 'Program') {
return;
}
path.scope.crawl();
path.traverse<State>(
{
CallExpression(path, state) {
const parseResult =
parseInlineableAlias(path, state) ||
parseInlineableMemberAlias(path, state);
if (parseResult == null) {
return;
}
const {declarationPath, moduleName, requireFnName} = parseResult;
const maybeInit = declarationPath.node.init;
const name = declarationPath.node.id
? declarationPath.node.id.name
: null;
const binding =
name == null ? null : declarationPath.scope.getBinding(name);
if (
maybeInit == null ||
!t.isExpression(maybeInit) ||
binding == null ||
binding.constantViolations.length > 0
) {
return;
}
const init: BabelNodeExpression = maybeInit;
const initPath = declarationPath.get('init');
if (Array.isArray(initPath)) {
return;
}
const initLoc = getNearestLocFromPath(initPath);
deleteLocation(init);
traverse(init, {
noScope: true,
enter: path => deleteLocation(path.node),
});
let thrown = false;
const memoVarName = parseResult.identifierName;
// Whether the module has a "var foo" at program scope, used to
// store the result of a require call if memoizeCalls is true.
let hasMemoVar = false;
if (
memoizeCalls &&
// Don't add a var init statement if there are no references to
// the lvalue of the require assignment.
binding.referencePaths.length > 0 &&
// Some modules should never be memoized even though they
// may be inlined.
!nonMemoizedModules.has(moduleName)
) {
// create var init statement
const varInitStmt = t.variableDeclaration('var', [
t.variableDeclarator(t.identifier(memoVarName)),
]);
// Must remove the declaration path
declarationPath.remove();
hasMemoVar = addStmtToBlock(programNode, varInitStmt, 0);
}
function getMemoOrCallExpr() {
const refExpr = t.cloneDeep(init);
// $FlowFixMe[prop-missing]
refExpr.METRO_INLINE_REQUIRES_INIT_LOC = initLoc;
return t.logicalExpression(
'||',
t.identifier(memoVarName),
t.assignmentExpression(
'=',
t.identifier(memoVarName),
refExpr,
),
);
}
const scopesWithInlinedRequire = new Set<Scope>();
for (const referencePath of binding.referencePaths) {
excludeMemberAssignment(moduleName, referencePath, state);
try {
referencePath.scope.rename(requireFnName);
if (hasMemoVar) {
referencePath.scope.rename(memoVarName);
// Swap the local reference with (v || v = require(m)),
// unless it is directly enclosed.
if (!isDirectlyEnclosedByBlock(t, referencePath)) {
referencePath.replaceWith(getMemoOrCallExpr());
continue;
}
// if the current scope already has a (v || v = require(m))
// expression for module m, use identifier reference v
// instead. Else use the full (v || v = require(m)) and
// register the current scope for subsequent references.
if (scopesWithInlinedRequire.has(referencePath.scope)) {
referencePath.replaceWith(t.identifier(memoVarName));
} else {
referencePath.replaceWith(getMemoOrCallExpr());
scopesWithInlinedRequire.add(referencePath.scope);
}
} else {
const refExpr = t.cloneDeep(init);
// $FlowFixMe[prop-missing]
refExpr.METRO_INLINE_REQUIRES_INIT_LOC = initLoc;
referencePath.replaceWith(refExpr);
}
} catch (error) {
thrown = true;
}
}
// If a replacement failed (e.g. replacing a type annotation),
// avoid removing the initial require just to be safe.
if (!thrown && declarationPath.node != null) {
declarationPath.remove();
}
},
},
{
ignoredRequires,
inlineableCalls,
membersAssigned: new Map(),
},
);
},
},
},
});
function excludeMemberAssignment(
moduleName: string,
referencePath: NodePath<>,
state: State,
) {
const assignment: ?BabelNode = referencePath.parentPath?.parent;
if (assignment?.type !== 'AssignmentExpression') {
return;
}
const left = assignment.left;
if (left.type !== 'MemberExpression' || left.object !== referencePath.node) {
return;
}
const memberPropertyName = getMemberPropertyName(left);
if (memberPropertyName == null) {
return;
}
let membersAssigned = state.membersAssigned.get(moduleName);
if (membersAssigned == null) {
membersAssigned = new Set();
state.membersAssigned.set(moduleName, membersAssigned);
}
membersAssigned.add(memberPropertyName);
}
function isExcludedMemberAssignment(
moduleName: string,
memberPropertyName: string,
state: State,
) {
const excludedAliases = state.membersAssigned.get(moduleName);
return excludedAliases != null && excludedAliases.has(memberPropertyName);
}
function getMemberPropertyName(node: BabelNodeMemberExpression): ?string {
if (node.property.type === 'Identifier') {
return node.property.name;
}
if (node.property.type === 'StringLiteral') {
return node.property.value;
}
return null;
}
function deleteLocation(node: BabelNode) {
delete node.start;
delete node.end;
delete node.loc;
}
function parseInlineableAlias(
path: NodePath<BabelNodeCallExpression>,
state: State,
): ?{
declarationPath: NodePath<BabelNode>,
moduleName: string,
requireFnName: string,
identifierName: string,
} {
const module = getInlineableModule(path, state);
if (module == null) {
return null;
}
const {moduleName, requireFnName} = module;
const parentPath = path.parentPath;
if (parentPath == null) {
return null;
}
const grandParentPath = parentPath.parentPath;
if (grandParentPath == null) {
return null;
}
if (path.parent.type !== 'VariableDeclarator') {
return null;
}
const variableDeclarator = path.parent;
if (variableDeclarator.id.type !== 'Identifier') {
return null;
}
const identifier = variableDeclarator.id;
const isValid =
parentPath.parent.type === 'VariableDeclaration' &&
grandParentPath.parent.type === 'Program';
return !isValid || parentPath.node == null
? null
: {
declarationPath: parentPath,
moduleName,
requireFnName,
identifierName: identifier.name,
};
}
function parseInlineableMemberAlias(
path: NodePath<BabelNodeCallExpression>,
state: State,
): ?{
declarationPath: NodePath<BabelNode>,
moduleName: string,
requireFnName: string,
identifierName: string,
} {
const module = getInlineableModule(path, state);
if (module == null) {
return null;
}
const {moduleName, requireFnName} = module;
const parent = path.parent;
const parentPath = path.parentPath;
if (parentPath == null) {
return null;
}
const grandParentPath = parentPath.parentPath;
if (grandParentPath == null) {
return null;
}
if (parent.type !== 'MemberExpression') {
return null;
}
const memberExpression: BabelNodeMemberExpression = parent;
if (parentPath.parent.type !== 'VariableDeclarator') {
return null;
}
const variableDeclarator = parentPath.parent;
if (variableDeclarator.id.type !== 'Identifier') {
return null;
}
const identifier = variableDeclarator.id;
if (
grandParentPath.parent.type !== 'VariableDeclaration' ||
grandParentPath.parentPath?.parent.type !== 'Program' ||
grandParentPath.node == null
) {
return null;
}
const memberPropertyName = getMemberPropertyName(memberExpression);
return memberPropertyName == null ||
isExcludedMemberAssignment(moduleName, memberPropertyName, state)
? null
: {
declarationPath: grandParentPath,
moduleName,
requireFnName,
identifierName: identifier.name,
};
}
function getInlineableModule(
path: NodePath<BabelNodeCallExpression>,
state: State,
): ?{moduleName: string, requireFnName: string} {
const node = path.node;
const isInlineable =
node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
state.inlineableCalls.has(node.callee.name) &&
node['arguments'].length >= 1;
if (!isInlineable) {
return null;
}
// require('foo');
let moduleName =
node['arguments'][0].type === 'StringLiteral'
? node['arguments'][0].value
: null;
// require(require.resolve('foo'));
if (moduleName == null) {
const callNode = node['arguments'][0];
if (
callNode.type === 'CallExpression' &&
callNode.callee.type === 'MemberExpression' &&
callNode.callee.object.type === 'Identifier'
) {
const callee = callNode.callee;
moduleName =
callee.object.type === 'Identifier' &&
state.inlineableCalls.has(callee.object.name) &&
callee.property.type === 'Identifier' &&
callee.property.name === 'resolve' &&
callNode['arguments'].length >= 1 &&
callNode['arguments'][0].type === 'StringLiteral'
? callNode['arguments'][0].value
: null;
}
}
// Check if require is in any parent scope
const fnName = node.callee.name;
if (fnName == null) {
return null;
}
const isRequireInScope = path.scope.getBinding(fnName) != null;
return moduleName == null ||
state.ignoredRequires.has(moduleName) ||
moduleName.startsWith('@babel/runtime/') ||
isRequireInScope
? null
: {moduleName, requireFnName: fnName};
}
function getNearestLocFromPath(path: NodePath<>): ?BabelSourceLocation {
let current: ?(NodePath<> | NodePath<BabelNode>) = path;
while (current && !current.node.loc) {
current = current.parentPath;
}
return current?.node.loc;
}
// check if a node is a branch
function isBranch(t: Types, node: BabelNode) {
return (
t.isIfStatement(node) ||
t.isLogicalExpression(node) ||
t.isConditionalExpression(node) ||
t.isSwitchStatement(node) ||
t.isSwitchCase(node) ||
t.isForStatement(node) ||
t.isForInStatement(node) ||
t.isForOfStatement(node) ||
t.isWhileStatement(node)
);
}
function isDirectlyEnclosedByBlock(t: Types, path: NodePath<BabelNode>) {
let curPath: ?NodePath<BabelNode> = path;
while (curPath) {
if (isBranch(t, curPath.node)) {
return false;
}
if (t.isBlockStatement(curPath.node)) {
return true;
}
curPath = curPath.parentPath;
}
return true;
}
// insert statement to the beginning of the scope block
function addStmtToBlock(
block: BabelNodeProgram,
stmt: BabelNodeStatement,
idx: number,
): boolean {
const scopeBody = block.body;
if (Array.isArray(scopeBody)) {
// if the code is inside global scope
scopeBody.splice(idx, 0, stmt);
return true;
} else if (scopeBody && Array.isArray(scopeBody.body)) {
// if the code is inside function scope
scopeBody.body.splice(idx, 0, stmt);
return true;
} else {
return false;
}
}