IOweb/node_modules/satteri/dist/hast/hast-visitor.d.ts
2026-07-03 15:07:38 -05:00

112 lines
5.8 KiB
TypeScript

import { type HastNode } from "./hast-materializer.js";
import type { HastRaw, Data } from "../types.js";
import type { Element, Text, Comment, Doctype, Parents as HastParents, Root as HastRoot } from "hast";
import type { Program } from "estree-jsx";
import type { MdxJsxFlowElementHast, MdxJsxTextElementHast } from "../mdx-types.js";
import type { MdxFlowExpressionHast, MdxTextExpressionHast } from "../mdx-types.js";
import type { MdxjsEsmHast } from "../mdx-types.js";
import type { HastHandle } from "../handles.js";
export type { HastHandle };
/** ESTree-compatible Program node returned by `parseExpression()`. */
export type EstreeProgram = Program;
export interface HastDiagnostic {
message: string;
nodeId?: number | undefined;
severity: "error" | "warning" | "info";
}
export interface HastVisitorContext {
readonly source: string;
/**
* The URL of the document being processed (the compile `fileURL` option),
* or `undefined` when none was given. Use `fileURLToPath(ctx.fileURL)` for a
* decoded filesystem path.
*/
readonly fileURL: URL | undefined;
/**
* Document-level data bag, shared across every plugin in the compile and
* across the mdast→hast phase boundary. Mutate keys directly
* (`ctx.data.foo = x`); the bag itself isn't reassignable. Values are kept
* on the JS side, so any value is allowed, including functions and class
* instances. Returned to the caller as `result.data`.
*/
readonly data: Data;
removeNode(node: Readonly<HastNode>): void;
replaceNode(node: Readonly<HastNode>, newNode: HastContent): void;
insertBefore(node: Readonly<HastNode>, newNode: HastContent | HastContent[]): void;
insertAfter(node: Readonly<HastNode>, newNode: HastContent | HastContent[]): void;
/**
* Wrap `node` in `parentNode`, making it `parentNode`'s first child. Any
* children `parentNode` declares are kept after it, so a `div` with an anchor
* child wraps a heading as `div > [heading, anchor]`.
*/
wrapNode(node: Readonly<HastNode>, parentNode: HastContent): void;
prependChild(node: Readonly<HastNode>, childNode: HastContent | HastContent[]): void;
appendChild(node: Readonly<HastNode>, childNode: HastContent | HastContent[]): void;
/** Insert one node or an array at `index`; clamps (`0` or less prepends, past the end appends). */
insertChildAt(node: Readonly<HastNode>, index: number, childNode: HastContent | HastContent[]): void;
/** Remove the `index`-th child of `node`; a no-op when there is no such child. */
removeChildAt(node: Readonly<HastNode>, index: number): void;
setProperty(node: Readonly<HastNode>, key: string, value: unknown): void;
/** Collect the concatenated text of all descendant text nodes (like DOM textContent). */
textContent(node: Readonly<HastNode>): string;
/**
* The parent of a node, or `undefined` at the root. Within a pass the same
* parent is always the same object, so visitors on sibling nodes can dedupe
* by identity.
*/
parent<N extends Exclude<HastNode, HastRoot>>(node: Readonly<N>): Readonly<HastParents>;
parent(node: Readonly<HastNode>): Readonly<HastParents> | undefined;
/**
* Index of `node` within its parent's children, or `undefined` at the root.
* Use this rather than `parent.children.indexOf(node)`, which won't find it.
*/
indexOf(node: Readonly<HastNode>): number | undefined;
report(opts: {
message: string;
node?: Readonly<HastNode>;
severity?: "error" | "warning" | "info";
}): void;
getDiagnostics(): HastDiagnostic[];
}
/** New content for a HAST structural mutation. Unlike [`MdastContent`], HAST has
* a `raw` node type, so it needs no raw/rawHtml escape hatch. */
export type HastContent = HastNode;
/** A filtered visitor: Rust filters by tag/component name, only matched nodes cross the boundary. */
export interface HastFilteredVisitor<N extends HastNode = HastNode> {
filter: string[];
visit(node: Readonly<N>, ctx: HastVisitorContext): HastNode | void | Promise<HastNode | void>;
}
type HastVisitorFn<N extends HastNode = HastNode> = (node: Readonly<N>, ctx: HastVisitorContext) => HastNode | void | Promise<HastNode | void>;
export interface HastVisitorInstance {
element?: HastFilteredVisitor<Element> | HastFilteredVisitor<Element>[];
mdxJsxFlowElement?: HastFilteredVisitor<MdxJsxFlowElementHast> | HastFilteredVisitor<MdxJsxFlowElementHast>[];
mdxJsxTextElement?: HastFilteredVisitor<MdxJsxTextElementHast> | HastFilteredVisitor<MdxJsxTextElementHast>[];
text?: HastVisitorFn<Text>;
comment?: HastVisitorFn<Comment>;
raw?: HastVisitorFn<HastRaw>;
doctype?: HastVisitorFn<Doctype>;
mdxFlowExpression?: HastVisitorFn<MdxFlowExpressionHast & {
parseExpression(): EstreeProgram | null;
}>;
mdxTextExpression?: HastVisitorFn<MdxTextExpressionHast & {
parseExpression(): EstreeProgram | null;
}>;
mdxjsEsm?: HastVisitorFn<MdxjsEsmHast & {
parseExpression(): EstreeProgram | null;
}>;
}
interface ResolvedSubscription {
nodeType: number;
tagFilter: string[];
visitFn: (node: HastNode, ctx: HastVisitorContext) => HastNode | void;
}
export declare function resolveSubscriptions(plugin: HastVisitorInstance): ResolvedSubscription[];
/**
* Walk a handle's arena in Rust, dispatch matched nodes to JS visitor functions,
* and apply mutations back to the handle. No arena buffers cross NAPI.
*
* Returns the number of patches dropped because their target was removed or
* replaced earlier in the same pass (the caller warns when non-zero), or a
* Promise of that count if any visitor is async.
*/
export declare function visitHastHandle(handle: HastHandle, plugin: HastVisitorInstance, subs: ResolvedSubscription[], source: string | (() => string), fileURL: URL | undefined, data?: Data): number | Promise<number>;