Files
rea/src/domain/webBundleAnalyzerAst.ts
T
morluto d23be5c089 fix(web): preserve static WebMCP schema property coverage
Reuse the shared object-member owner for literal spreads, retain known property names under unresolved membership, and distinguish complete, partial, unknown, and absent projections. Keep historical coverage omission unchanged and preserve own keys across prototype setters.

Refs #5075
2026-10-21 03:56:35 +08:00

63 lines
1.9 KiB
TypeScript

import * as t from "@babel/types";
import { sanitizeBrowserUrl } from "./browserObservation.js";
import {
isNullishJavaScriptSyntax,
readExactJavaScriptLiteral,
staticObjectProperty,
} from "./javascript/javascriptAstValues.js";
/** Read a statically known own property value from an object expression. */
export const objectValue = (
object: t.ObjectExpression,
name: string,
): t.ObjectProperty["value"] | undefined => {
const result = staticObjectProperty(object, name, isNullishJavaScriptSyntax);
return result.status === "explicit" ? result.property.value : undefined;
};
export const objectString = (
object: t.ObjectExpression,
name: string,
): string | undefined => {
const value = objectValue(object, name);
if (value === undefined) return undefined;
const literal = readExactJavaScriptLiteral(value);
return literal.found && typeof literal.value === "string"
? literal.value
: undefined;
};
/** Whether module syntax supplies a URL location without a package/import-map resolver. */
export const isUrlLikeModuleSpecifier = (specifier: string): boolean =>
specifier.startsWith("/") ||
specifier.startsWith("./") ||
specifier.startsWith("../") ||
/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(specifier);
export const resolveSpecifier = (
specifier: string,
base: string,
): string | null => {
try {
const resolved = new URL(specifier, base);
if (resolved.protocol !== "http:" && resolved.protocol !== "https:")
return null;
return sanitizeBrowserUrl(resolved.href).url;
} catch (cause: unknown) {
// Unresolvable specifiers are represented by the null return.
void cause;
return null;
}
};
export const location = (scriptKey: string, node: t.Node) => ({
script_key: scriptKey,
...locationFields(node),
});
export const locationFields = (node: t.Node) => ({
line: node.loc?.start.line ?? null,
column: node.loc?.start.column ?? null,
});