mirror of
https://github.com/morluto/rea
synced 2026-10-27 14:42:18 -04:00
fix(javascript): follow bundler namespace interop for Electron APIs (#1827)
* fix(javascript): follow bundler namespace interop for Electron APIs
Rolldown/tsdown emit `let electron = require("electron"); electron =
__toESM(electron);` and esbuild/tsc emit `__toESM(require("electron"))`
or `__importStar(require("electron"))` for namespace imports. The
reassignment or the helper call hid the Electron origin, so bundled
main processes reported zero BrowserWindows and preloads.
Locally declared, never reassigned interop helpers recognized by name
now pass the module through, and a binding's rewrap of its own value is
not treated as a reassignment. Other writes stay unresolved.
Fixes #1802
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(javascript): require emitted interop helper definitions
Only trust an interop helper name when it has a single program-scope
function definition, and state that helper bodies are not evaluated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,10 @@ import {
|
||||
completeSemanticSteps,
|
||||
traverseJavaScriptAstSteps,
|
||||
} from "./javascriptSemanticTraversal.js";
|
||||
import { semanticRequireOrigin } from "./javascriptSemanticRequireOrigin.js";
|
||||
import {
|
||||
semanticNamespaceInteropOperand,
|
||||
semanticRequireOrigin,
|
||||
} from "./javascriptSemanticRequireOrigin.js";
|
||||
import { collectSemanticMemberMutationsSteps } from "./javascriptSemanticMemberMutations.js";
|
||||
import {
|
||||
collectSemanticReturns,
|
||||
@@ -384,7 +387,10 @@ export function* classifyParsedJavaScriptElectronBindingsSteps(
|
||||
}
|
||||
return exported ?? undefined;
|
||||
}
|
||||
const origin = semanticRequireOrigin(root, state)?.origin;
|
||||
const origin = semanticRequireOrigin(
|
||||
semanticNamespaceInteropOperand(root, state) ?? root,
|
||||
state,
|
||||
)?.origin;
|
||||
return origin !== undefined && ELECTRON_MODULE.test(origin.specifier)
|
||||
? electronExportPath(origin.importedPath)
|
||||
: undefined;
|
||||
@@ -416,14 +422,15 @@ const electronExport = (
|
||||
binding: JavaScriptSemanticBindingState,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
): readonly string[] | null => {
|
||||
if (binding.definitions.some(({ kind }) => kind === "assignment"))
|
||||
return null;
|
||||
const directOrigin = bindingOrigin(binding, state);
|
||||
const [initializer] = binding.initializers;
|
||||
const initializers = namespacePreservingInitializers(binding, state);
|
||||
if (initializers === undefined) return null;
|
||||
const directOrigin = bindingOrigin(binding, initializers, state);
|
||||
const [initializer] = initializers;
|
||||
if (
|
||||
directOrigin === undefined &&
|
||||
(initializer === undefined ||
|
||||
binding.initializers.length !== 1 ||
|
||||
initializers.length !== 1 ||
|
||||
initializers.length !== binding.initializers.length ||
|
||||
state.conditionalInitializers.has(initializer.node))
|
||||
)
|
||||
return null;
|
||||
@@ -441,25 +448,66 @@ const electronExport = (
|
||||
: null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the initializers that can change which module a binding names.
|
||||
* Bundlers emit `name = __toESM(name)` after a CommonJS require; rewrapping
|
||||
* the binding's own value keeps every member, so it is not a reassignment.
|
||||
*/
|
||||
const namespacePreservingInitializers = (
|
||||
binding: JavaScriptSemanticBindingState,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
): JavaScriptSemanticBindingState["initializers"] | undefined => {
|
||||
const initializers = binding.initializers.filter(
|
||||
({ node }) => !rewrapsOwnNamespace(binding, node, state),
|
||||
);
|
||||
const rewraps = binding.initializers.length - initializers.length;
|
||||
const assignments = binding.definitions.filter(
|
||||
({ kind }) => kind === "assignment",
|
||||
).length;
|
||||
return assignments === rewraps ? initializers : undefined;
|
||||
};
|
||||
|
||||
const rewrapsOwnNamespace = (
|
||||
binding: JavaScriptSemanticBindingState,
|
||||
node: t.Node,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
): boolean => {
|
||||
const assignment = state.parentsByNode.get(node);
|
||||
const operand = semanticNamespaceInteropOperand(node, state);
|
||||
return (
|
||||
t.isAssignmentExpression(assignment) &&
|
||||
assignment.operator === "=" &&
|
||||
assignment.right === node &&
|
||||
t.isIdentifier(assignment.left, { name: binding.name }) &&
|
||||
t.isIdentifier(operand, { name: binding.name }) &&
|
||||
!semanticResolutionBlocked(state, operand, operand.name) &&
|
||||
resolveSemanticBindingState(state, operand, operand.name) === binding
|
||||
);
|
||||
};
|
||||
|
||||
const bindingOrigin = (
|
||||
binding: JavaScriptSemanticBindingState,
|
||||
initializers: JavaScriptSemanticBindingState["initializers"],
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
): JavaScriptModuleOrigin | undefined => {
|
||||
if (binding.directOrigins.length > 0)
|
||||
return binding.directOrigins.length === 1
|
||||
? binding.directOrigins[0]
|
||||
: undefined;
|
||||
const [initializer] = binding.initializers;
|
||||
const [initializer] = initializers;
|
||||
// A var/let declaration permits writes without proving one occurred.
|
||||
// Require one unconditional initializer and no actual binding writes.
|
||||
if (
|
||||
initializer === undefined ||
|
||||
binding.initializers.length !== 1 ||
|
||||
binding.definitions.some(({ kind }) => kind === "assignment") ||
|
||||
initializers.length !== 1 ||
|
||||
state.conditionalInitializers.has(initializer.node)
|
||||
)
|
||||
return undefined;
|
||||
const origin = semanticRequireOrigin(initializer.node, state)?.origin;
|
||||
const origin = semanticRequireOrigin(
|
||||
semanticNamespaceInteropOperand(initializer.node, state) ??
|
||||
initializer.node,
|
||||
state,
|
||||
)?.origin;
|
||||
const projection = initializer.projection;
|
||||
return origin === undefined ||
|
||||
!projection.every((key): key is string => typeof key === "string")
|
||||
|
||||
Reference in New Issue
Block a user