mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 18:05:45 +00:00
fix(javascript): derive loader references from lexical binding ownership
Use shared lexical bindings and recovered bundler identities for require and chunk references. Preserve uncertain writes as unresolved evidence; remove name-only loader aliases and repeated definition scans. Reviewed-PR: #2989 Co-authored-by: Hussnain Ahmad <ha7886899@gmail.com>
This commit is contained in:
1 parent
f2f83934d2
commit
eef4a0bfb4
8 files changed
+509
-32
No files matched your search
@@ -6,6 +6,7 @@ import type { JavaScriptModuleOrigin } from "./javascriptSemanticIr.js";
|
||||
import {
|
||||
resolveSemanticBindingState,
|
||||
semanticResolutionBlocked,
|
||||
semanticStableInitializer,
|
||||
type JavaScriptSemanticAnalysisState,
|
||||
} from "./javascriptSemanticState.js";
|
||||
import {
|
||||
@@ -22,6 +23,169 @@ export interface JavaScriptRequireOrigin {
|
||||
readonly call: t.CallExpression;
|
||||
}
|
||||
|
||||
export type JavaScriptRequireLoaderFact =
|
||||
| "commonjs-loader"
|
||||
| "factory-loader"
|
||||
| "create-require"
|
||||
| "local"
|
||||
| "unknown";
|
||||
|
||||
/** Classify a bare `require(...)` using the shared lexical binding owner. */
|
||||
export const semanticRequireLoaderAt = (
|
||||
node: t.CallExpression | t.OptionalCallExpression | t.NewExpression,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
factoryBindingIds: ReadonlySet<string>,
|
||||
): JavaScriptRequireLoaderFact => {
|
||||
if (!t.isIdentifier(node.callee, { name: "require" })) return "local";
|
||||
const binding = resolveSemanticBindingState(state, node.callee, "require");
|
||||
if (binding === undefined)
|
||||
return semanticResolutionBlocked(state, node.callee, "require")
|
||||
? "unknown"
|
||||
: "commonjs-loader";
|
||||
if (factoryBindingIds.has(binding.bindingId))
|
||||
return binding.initializers.length === 0 ? "factory-loader" : "unknown";
|
||||
if (isWrapperRequireInitializerCall(binding, node, state))
|
||||
return "commonjs-loader";
|
||||
const scope = state.scopesById.get(binding.scopeId);
|
||||
if (
|
||||
scope?.kind === "program" &&
|
||||
binding.declarationKinds.size > 0 &&
|
||||
[...binding.declarationKinds].every((kind) => kind === "var") &&
|
||||
binding.initializers.length === 0
|
||||
)
|
||||
return "commonjs-loader";
|
||||
if (isSemanticCreateRequireBinding(binding, state)) return "create-require";
|
||||
const initializer = semanticStableInitializer(binding, state)?.node;
|
||||
if (
|
||||
(binding.kind === "function" || binding.kind === "class") &&
|
||||
initializer !== undefined
|
||||
)
|
||||
return "local";
|
||||
if (
|
||||
binding.declarationKinds.size === 1 &&
|
||||
binding.declarationKinds.has("const") &&
|
||||
(t.isFunctionExpression(initializer) ||
|
||||
t.isArrowFunctionExpression(initializer))
|
||||
)
|
||||
return "local";
|
||||
return "unknown";
|
||||
};
|
||||
|
||||
/** Classify a call through an exact recovered factory-loader binding. */
|
||||
export const semanticFactoryRequireLoaderAt = (
|
||||
node: t.CallExpression | t.OptionalCallExpression | t.NewExpression,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
bindingIds: ReadonlySet<string>,
|
||||
): JavaScriptRequireLoaderFact | undefined => {
|
||||
const callee = unwrapJavaScriptExpression(node.callee).node;
|
||||
if (t.isIdentifier(callee))
|
||||
return semanticFactoryLoaderMemberAt(node, state, bindingIds, null);
|
||||
if (
|
||||
(t.isMemberExpression(callee) || t.isOptionalMemberExpression(callee)) &&
|
||||
semanticStaticPropertyKey(callee.property, callee.computed) === "bind"
|
||||
)
|
||||
return semanticFactoryLoaderMemberAt(node, state, bindingIds, "bind");
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/** Classify an exact loader call by binding identity and expected member. */
|
||||
export const semanticFactoryLoaderMemberAt = (
|
||||
node: t.CallExpression | t.OptionalCallExpression | t.NewExpression,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
bindingIds: ReadonlySet<string>,
|
||||
member: string | null,
|
||||
): JavaScriptRequireLoaderFact | undefined => {
|
||||
const callee = unwrapJavaScriptExpression(node.callee).node;
|
||||
let loader: t.Identifier | undefined;
|
||||
if (member === null) {
|
||||
if (t.isIdentifier(callee)) loader = callee;
|
||||
} else if (
|
||||
(t.isMemberExpression(callee) || t.isOptionalMemberExpression(callee)) &&
|
||||
semanticStaticPropertyKey(callee.property, callee.computed) === member
|
||||
) {
|
||||
const root = unwrapJavaScriptExpression(callee.object).node;
|
||||
if (t.isIdentifier(root)) loader = root;
|
||||
}
|
||||
if (loader === undefined) return undefined;
|
||||
const binding = resolveSemanticBindingState(state, loader, loader.name);
|
||||
if (binding === undefined || !bindingIds.has(binding.bindingId))
|
||||
return undefined;
|
||||
return binding.initializers.length === 0 ? "factory-loader" : "unknown";
|
||||
};
|
||||
|
||||
const isSemanticCreateRequireBinding = (
|
||||
binding: NonNullable<ReturnType<typeof resolveSemanticBindingState>>,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
): boolean => {
|
||||
if (binding.initializers.length !== 1) return false;
|
||||
const initializer = semanticStableInitializer(binding, state)?.node;
|
||||
if (!t.isCallExpression(initializer)) return false;
|
||||
const callee = unwrapJavaScriptExpression(initializer.callee).node;
|
||||
if (t.isIdentifier(callee)) {
|
||||
const origins = resolveSemanticBindingState(
|
||||
state,
|
||||
callee,
|
||||
callee.name,
|
||||
)?.directOrigins;
|
||||
if (origins?.length !== 1) return false;
|
||||
const [origin] = origins;
|
||||
return (
|
||||
origin?.importedPath.at(-1) === "createRequire" &&
|
||||
["module", "node:module"].includes(origin.specifier)
|
||||
);
|
||||
}
|
||||
const chain = semanticMemberChain(callee);
|
||||
if (chain === undefined || chain.members.at(-1) !== "createRequire")
|
||||
return false;
|
||||
if (!t.isNode(chain.root)) return false;
|
||||
const root = unwrapJavaScriptExpression(chain.root).node;
|
||||
if (!t.isIdentifier(root)) return false;
|
||||
const origins = resolveSemanticBindingState(
|
||||
state,
|
||||
root,
|
||||
root.name,
|
||||
)?.directOrigins;
|
||||
if (origins?.length !== 1) return false;
|
||||
const [origin] = origins;
|
||||
return (
|
||||
origin?.importedPath.length === 0 &&
|
||||
["module", "node:module"].includes(origin.specifier)
|
||||
);
|
||||
};
|
||||
|
||||
/** The RHS call runs before its assignment replaces Node's wrapper loader. */
|
||||
const isWrapperRequireInitializerCall = (
|
||||
binding: NonNullable<ReturnType<typeof resolveSemanticBindingState>>,
|
||||
node: t.CallExpression | t.OptionalCallExpression | t.NewExpression,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
): boolean => {
|
||||
const scope = state.scopesById.get(binding.scopeId);
|
||||
if (
|
||||
scope?.kind !== "program" ||
|
||||
binding.kind !== "variable" ||
|
||||
binding.declarationKinds.size !== 1 ||
|
||||
!binding.declarationKinds.has("var") ||
|
||||
binding.initializers.length !== 1 ||
|
||||
binding.initializers[0]?.node !== node
|
||||
)
|
||||
return false;
|
||||
const assignment = state.parentsByNode.get(node);
|
||||
if (
|
||||
!t.isAssignmentExpression(assignment) ||
|
||||
assignment.right !== node ||
|
||||
state.conditionallySkippedExpressions.has(node)
|
||||
)
|
||||
return false;
|
||||
const statement = state.parentsByNode.get(assignment);
|
||||
if (!t.isExpressionStatement(statement)) return false;
|
||||
if (state.parentsByNode.get(statement) !== state.program) return false;
|
||||
const target = unwrapJavaScriptExpression(assignment.left).node;
|
||||
return (
|
||||
t.isIdentifier(target, { name: "require" }) &&
|
||||
resolveSemanticBindingState(state, target, target.name) === binding
|
||||
);
|
||||
};
|
||||
|
||||
// Ambient indexing finishes before these derived facts are consumed. Keep only
|
||||
// replacement sites, rather than rescanning unrelated global member writes.
|
||||
const globalProcessReplacements = new WeakMap<
|
||||
|
||||
Reference in new issue
Block a user