mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
* refactor(javascript): break the semantic analysis import cycle Split shared semantic facts out of the analysis hub so projectors of effects, evaluation, and identity no longer import one another in a cycle. * refactor(javascript): share the import specifier reader Read static imports in one module used by the boundary checker and both cycle guards, so those rules cannot drift apart. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
57 lines
2.3 KiB
TypeScript
57 lines
2.3 KiB
TypeScript
import * as t from "@babel/types";
|
|
import { electronMemberPath } from "./javascriptElectronMemberWrites.js";
|
|
import { isUnshadowedGlobal } from "./javascriptSemanticMutationInitializers.js";
|
|
import type { JavaScriptSemanticAnalysisState } from "./javascriptSemanticState.js";
|
|
import { isImportMetaUrl } from "./javascriptStaticAnalysisHelpers.js";
|
|
|
|
export const GLOBAL_OBJECT_NAMES = ["globalThis", "global", "window", "self"];
|
|
|
|
export const globalObjectName = (
|
|
node: t.Node,
|
|
state: JavaScriptSemanticAnalysisState,
|
|
): string | undefined =>
|
|
GLOBAL_OBJECT_NAMES.find((name) => isUnshadowedGlobal(node, state, name));
|
|
|
|
/** Match lexical global identity; null denotes a direct global root. */
|
|
export const globalReferenceNamespace = (
|
|
callee: t.Node,
|
|
state: JavaScriptSemanticAnalysisState,
|
|
path: readonly [string, ...string[]],
|
|
): string | null | undefined => {
|
|
const { root, members } = electronMemberPath(callee);
|
|
const matches = (expected: readonly string[]) =>
|
|
members.length === expected.length &&
|
|
members.every((key, index) => key === expected[index]);
|
|
if (isUnshadowedGlobal(root, state, path[0]) && matches(path.slice(1)))
|
|
return null;
|
|
const namespace = globalObjectName(root, state);
|
|
return namespace !== undefined && matches(path) ? namespace : undefined;
|
|
};
|
|
|
|
/**
|
|
* Recognize an ambient property path, optionally through a global object, without a visible
|
|
* source-local overwrite. This is a syntax-local identity fact, not proof that
|
|
* the runtime's globals have never been patched by other code.
|
|
*/
|
|
export const isUnmodifiedGlobalReference = (
|
|
callee: t.Node,
|
|
state: JavaScriptSemanticAnalysisState,
|
|
name: string | readonly [string, ...string[]],
|
|
): boolean => {
|
|
const path: readonly [string, ...string[]] =
|
|
typeof name === "string" ? [name] : name;
|
|
if (state.ambientWrites.covers(["global", ...path])) return false;
|
|
const namespace = globalReferenceNamespace(callee, state, path);
|
|
return (
|
|
namespace !== undefined &&
|
|
(namespace === null || !state.ambientWrites.covers(["global", namespace]))
|
|
);
|
|
};
|
|
|
|
/** A module URL base is known only while its property has no visible write. */
|
|
export const isUnmodifiedImportMetaUrl = (
|
|
node: t.Node | undefined,
|
|
state: JavaScriptSemanticAnalysisState,
|
|
): boolean =>
|
|
isImportMetaUrl(node) && !state.ambientWrites.covers(["import-meta", "url"]);
|