aran
Version:
JavaScript instrumenter
117 lines (111 loc) • 4.53 kB
TypeScript
import type {
Node as EstreeNode,
Kind as EstreeNodeKind,
Path as EstreeNodePath,
} from "estree-sentry";
import type { Situ } from "./source.d.ts";
import type { GetDefault } from "../util/util.d.ts";
export type GlobalDeclarativeRecord = "builtin" | "emulate";
/**
* A parsed JavaScript program which should adhere to `estree.Program`. Can be
* generated by:
* - `acorn.parse(code, {ecmaVersion: 2024})`
* - `@babel/parser` with `estree` plugin
*/
export type LooseEstreeProgram = {
type: "Program";
sourceType: "module" | "script";
body: unknown[];
};
/**
* A parsed JavaScript program. Represents both static code and dynamically
* generated code.
*/
export type File<param extends { FilePath?: unknown } = {}> = {
/**
* The `estree.Program` node to instrument.
*/
root: LooseEstreeProgram;
/**
* Indicates how the source will be executed.
* @defaultValue Either `"script"` or `"module"` based on
* `file.root.sourceType`.
*/
kind: "module" | "script" | "eval";
/**
* Further precises the context in which the source will be executed. Only
* relevant when `source.kind` is `"eval"`.
* - `GlobalSitu`: The source will be executed in the global context. It is
* the only valid option when `source.kind` is `"module"` or `"script"`.
* - `RootLocalSitu`: The source will be executed in a local context that is
* not controlled by Aran -- ie: a direct eval call within non-instrumented
* code.
* - `DeepLocalSitu`: The source will be executed in a local context that is
* controlled by Aran -- ie: direct eval call within instrumented code. This
* data structure is provided by Aran as argument to the `eval@before` aspect.
* @defaultValue `{ type: "global" }`
*/
situ: Situ;
/**
* An identifier for the file. It is passed to `conf.digest` to help
* generating unique node hash.
*/
path: GetDefault<param, "FilePath", string>;
};
/**
* Hashing function for estree nodes.
* @template P The type of `file.path`.
* @template H
* The type of the hash. Either a string or a number. Can be branded -- eg:
* `string & { __brand: "hash" }`.
* @param node The estree node to hash.
* @param node_path The json path of the node in starting from `file.root`. It
* is composed of the properties names that lead to the node concatenated with
* dots. So integers properties are within bracket. For instance:
* `"body.0.declarations.0.init"`.
* @param file_path The path of the file containing the node as provided by
* `file.path`.
* @param node_kind The kind of the node -- eg: `"Program`", `"Statement"`,
* `"Expression"`, ...
* @returns The hash of the node. It should be unique for each node in the
* program `file.root`. It is safe to simply returns `node_path`.
*/
export type Digest<
param extends { FilePath?: unknown; NodeHash?: string | number } = {},
> = (
node: EstreeNode<{}>,
node_path: EstreeNodePath,
file_path: GetDefault<param, "FilePath", string>,
node_kind: EstreeNodeKind,
) => GetDefault<param, "NodeHash", string>;
/**
* Configuration object for `transpile`.
*/
export type Config<
param extends { FilePath?: unknown; NodeHash?: string | number } = {},
> = {
/**
* Indicates whether the global declarative record should be emulated or not.
* NB: The global declarative record is a scope frame that sits right before
* the global object. For instance, in *script* code (not eval code nor module
* code): `let x = 123` will cause the creation of a binding in the global
* declarative record and not in the global object. Unfortunately, this record
* cannot be accessed inside the language and we are stuck with two imperfect
* options:
* - `"builtin"`: The builtin global declarative record is used, access to
* global variables will happen via parameter functions such as:
* `scope.read`, `scope.writeSloppy`, etc... Tracking values through these
* calls requires additional logic.
* - `"emulate"`: A plain object is used to emulate the global declarative
* record. That means that instrumented code will never access the builtin
* global declarative record. Hence, every single bit of code should be
* instrumented which might be a hard requirement to meet.
* @defaultValue `"builtin"`
*/
global_declarative_record: "builtin" | "emulate";
/**
* Hashing functions for nodes in `file.root`.
* @defaultValue `(node, node_path, file_path, node_kind) => file_path + "#" + node_path`
*/
digest: Digest<param>;
};