import type { Element, ElementContent, Parent, Root, RootContent, Text, } from "hast"; import type { Transformer } from "unified"; import { SKIP, visitParents } from "unist-util-visit-parents"; import { transformView } from "./index.js"; import { TRANSFORM_OPTION_KEYS, type TransformOptions, } from "./transform-options.js"; import { MAX_RECURSION_DEPTH } from "./constants.js"; import { assertKnownOptionKeys, formatErrorString } from "./utils.js"; import { buildProseView, type ProsePass, type ProseView, splitAtIndices, } from "./prose-view.js"; export type ElementPredicate = (node: Element) => boolean; /** Per-text-node skip predicate, called after element-level `shouldSkip`. */ export type TextNodeSkipPredicate = ( textNode: Text, ancestors: readonly Element[], ) => boolean; export interface ElementTransformOptions { /** * Optional per-text-node skip predicate. When it returns `true` for a * given text node, that node is excluded from the flattened output, is * not passed to the transform function, and its `.value` is left * untouched. Applied after element-level `shouldSkip`. */ shouldSkipText?: TextNodeSkipPredicate; } export interface RehypePunctilioOptions extends TransformOptions, ElementTransformOptions { /** * HTML tag names to skip when applying transformations. * Content inside these elements won't have formatting improvements applied. * * Default: ["code", "pre", "script", "style", "kbd", "var", "samp", "template", "math", "svg"] */ skipTags?: string[]; /** * CSS class names that indicate content should skip formatting. * Elements with any of these classes (or descendants of such elements) * will be skipped. * * Default: [] */ skipClasses?: string[]; /** * Invert the element model: transform text inside every element except the * skip-list (`skipTags`/`skipClasses`) plus `textarea`/`input`/`select`, * whose text is a literal form-control value rather than prose. The default * (`false`) keeps the `TRANSFORMABLE_ELEMENTS` allowlist, transforming only * known prose-bearing tags (and custom elements). * * Default: false */ transformAllElements?: boolean; } const DEFAULT_SKIP_TAGS = [ "code", "pre", "script", "style", "kbd", "var", "samp", "template", "math", "svg", ]; // Form elements whose text content is a literal control value, not prose. // Hard-skipped (whole subtree) under `transformAllElements`, on top of the // user skip-list, so their values are protected even when nested inside a // transformable element. `textarea` is additionally skipped in the default // mode (see RAW_TEXT_FORM_TAG): unlike void `input`, it holds real text // children, so leaving it out of the default skip-list let a transformable // ancestor flatten and rewrite its value. const FORM_VALUE_TAGS = ["textarea", "input"]; // Escapable-raw-text form control whose text is a literal value, never prose. // Skipped in BOTH modes — the allowlist default does not reach it as its own // unit, but a transformable ancestor would otherwise pull its text into a // shared view and transform it. const RAW_TEXT_FORM_TAG = "textarea"; // `