{"version":3,"sources":["../src/core/base-renderer.ts"],"names":[],"mappings":"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","file":"chunk-7VUAKEER.cjs","sourcesContent":["import type {\n  AttributorHandler,\n  BlockDescriptor,\n  BlockHandler,\n  MarkHandler,\n  NodeOverrideHandler,\n  RendererConfig,\n  SimpleTagMark,\n  TNode,\n} from './ast-types';\n\n/**\n * Type guard — check if a mark config is a {@link SimpleTagMark} descriptor.\n */\nfunction isSimpleTagMark<Output, Attrs>(\n  mark: MarkHandler<Output, Attrs> | SimpleTagMark,\n): mark is SimpleTagMark {\n  return typeof mark === 'object' && 'tag' in mark;\n}\n\n/**\n * Type guard — check if a block config is a {@link BlockDescriptor}.\n */\nfunction isBlockDescriptor<Output, Attrs>(\n  block: BlockHandler<Output, Attrs> | BlockDescriptor,\n): block is BlockDescriptor {\n  return typeof block === 'object' && 'tag' in block;\n}\n\n/**\n * Abstract base class for all renderers.\n *\n * Handles:\n * - Recursive AST traversal\n * - Mark (inline formatting) priority sorting with attributor support\n * - Block attribute resolution\n * - Declarative block/mark descriptors\n * - Node overrides for intercepting specific node types\n * - Immutable extension via `withBlock()` / `withMark()` / `withAttributor()` / `withNodeOverride()`\n *\n * Subclasses must implement:\n * - `joinChildren()` — how to combine rendered children into a single output\n * - `renderText()` — how to render a plain text leaf node\n * - `emptyAttrs()` — return the identity/empty value for the Attrs type\n * - `mergeAttrs()` — merge two Attrs objects into one\n * - `hasAttrs()` — check whether an Attrs object has any content\n * - `wrapWithAttrs()` — how to wrap content in a default element with collected attrs\n * - `renderSimpleTag()` — how to render a simple tag mark with optional collected attrs\n * - `renderBlockFromDescriptor()` — how to render a declarative block descriptor\n *\n * @typeParam Output - The rendered output type (string, ReactNode, etc.)\n * @typeParam Attrs - The collected attribute type. Each renderer defines\n *   its own shape (e.g., HTML uses `ResolvedAttrs` with style/classes/attrs;\n *   React could use a props object; Markdown uses `never`).\n */\nexport abstract class BaseRenderer<Output, Attrs = unknown> {\n  protected blocks: Record<string, BlockHandler<Output, Attrs> | BlockDescriptor>;\n  protected marks: Record<string, MarkHandler<Output, Attrs> | SimpleTagMark>;\n  protected attributors: Record<string, AttributorHandler<Attrs>>;\n  protected markPriorities: Record<string, number>;\n  protected attributorTarget: 'innermost' | 'outermost';\n  protected blockAttributeResolvers: Array<(node: TNode) => Attrs>;\n  protected nodeOverrides: Record<string, NodeOverrideHandler<Output>>;\n  protected onUnknownNode: ((node: TNode) => Output | undefined) | undefined;\n\n  constructor(config: RendererConfig<Output, Attrs>) {\n    this.blocks = { ...config.blocks };\n    this.marks = { ...config.marks };\n    this.attributors = { ...config.attributors };\n    this.markPriorities = { ...config.markPriorities };\n    this.attributorTarget = config.attributorTarget ?? 'innermost';\n    this.blockAttributeResolvers = [...(config.blockAttributeResolvers ?? [])];\n    this.nodeOverrides = { ...config.nodeOverrides };\n    this.onUnknownNode = config.onUnknownNode;\n  }\n\n  /**\n   * Render a full AST tree (typically starting from the root node).\n   */\n  render(node: TNode): Output {\n    return this.renderNode(node);\n  }\n\n  // ─── Immutable Extension API ────────────────────────────────────────\n\n  /**\n   * Return a shallow clone of this renderer with a block handler added or replaced.\n   * The original renderer is not modified.\n   */\n  withBlock(type: string, handler: BlockHandler<Output, Attrs> | BlockDescriptor): this {\n    const clone = this.shallowClone();\n    clone.blocks = { ...this.blocks, [type]: handler };\n    return clone;\n  }\n\n  /**\n   * Return a shallow clone of this renderer with a mark handler added or replaced.\n   * The original renderer is not modified.\n   */\n  withMark(name: string, handler: MarkHandler<Output, Attrs> | SimpleTagMark): this {\n    const clone = this.shallowClone();\n    clone.marks = { ...this.marks, [name]: handler };\n    return clone;\n  }\n\n  /**\n   * Return a shallow clone of this renderer with an attributor added or replaced.\n   * The original renderer is not modified.\n   */\n  withAttributor(name: string, handler: AttributorHandler<Attrs>): this {\n    const clone = this.shallowClone();\n    clone.attributors = { ...this.attributors, [name]: handler };\n    return clone;\n  }\n\n  /**\n   * Return a shallow clone of this renderer with a mark priority set or replaced.\n   * The original renderer is not modified.\n   */\n  withMarkPriority(name: string, priority: number): this {\n    const clone = this.shallowClone();\n    clone.markPriorities = { ...this.markPriorities, [name]: priority };\n    return clone;\n  }\n\n  /**\n   * Return a shallow clone of this renderer with an additional block attribute resolver.\n   * The original renderer is not modified.\n   */\n  withBlockAttributeResolver(resolver: (node: TNode) => Attrs): this {\n    const clone = this.shallowClone();\n    clone.blockAttributeResolvers = [...this.blockAttributeResolvers, resolver];\n    return clone;\n  }\n\n  /**\n   * Return a shallow clone of this renderer with a node override added or replaced.\n   * The original renderer is not modified.\n   */\n  withNodeOverride(type: string, handler: NodeOverrideHandler<Output>): this {\n    const clone = this.shallowClone();\n    clone.nodeOverrides = { ...this.nodeOverrides, [type]: handler };\n    return clone;\n  }\n\n  /**\n   * Create a shallow clone of this renderer instance, preserving the\n   * prototype chain so that subclass overrides remain intact.\n   */\n  private shallowClone(): this {\n    const clone = Object.create(Object.getPrototypeOf(this)) as this;\n    Object.assign(clone, this);\n    // Deep-copy mutable collections so the clone doesn't share references\n    clone.blocks = { ...this.blocks };\n    clone.marks = { ...this.marks };\n    clone.attributors = { ...this.attributors };\n    clone.markPriorities = { ...this.markPriorities };\n    clone.attributorTarget = this.attributorTarget;\n    clone.blockAttributeResolvers = [...this.blockAttributeResolvers];\n    clone.nodeOverrides = { ...this.nodeOverrides };\n    clone.onUnknownNode = this.onUnknownNode;\n    return clone;\n  }\n\n  // ─── Abstract Methods (must be implemented by subclasses) ───────────────\n\n  /**\n   * Combine an array of rendered children into a single output value.\n   * For strings: concatenation. For React: array of elements.\n   */\n  protected abstract joinChildren(children: Output[]): Output;\n\n  /**\n   * Render a plain text string as the output type.\n   * This is the leaf-level conversion (e.g., escaping HTML entities for HTML renderers).\n   */\n  protected abstract renderText(text: string): Output;\n\n  /**\n   * Return the empty/identity value for the `Attrs` type.\n   * Used as the starting point for attribute collection.\n   */\n  protected abstract emptyAttrs(): Attrs;\n\n  /**\n   * Merge two `Attrs` objects into one. Source values take precedence\n   * over target values on conflict.\n   */\n  protected abstract mergeAttrs(target: Attrs, source: Attrs): Attrs;\n\n  /**\n   * Check whether an `Attrs` object has any meaningful content.\n   * Returns false for the empty/identity value.\n   */\n  protected abstract hasAttrs(attrs: Attrs): boolean;\n\n  /**\n   * Wrap content in a default element (e.g. `<span>`) with the given\n   * collected attributor attrs. Used when there are attributor marks\n   * but no element marks on a text node.\n   */\n  protected abstract wrapWithAttrs(content: Output, attrs: Attrs): Output;\n\n  /**\n   * Render a simple tag mark. For HTML: `<tag [attrs]>content</tag>`.\n   *\n   * @param tag - The resolved tag name\n   * @param content - The already-rendered inner content\n   * @param collectedAttrs - Optional attrs from attributors (only for target mark)\n   */\n  protected abstract renderSimpleTag(tag: string, content: Output, collectedAttrs?: Attrs): Output;\n\n  /**\n   * Render a block from a declarative {@link BlockDescriptor}.\n   * For HTML: `<tag [resolvedAttrs]>{children || emptyContent}</tag>`.\n   */\n  protected abstract renderBlockFromDescriptor(\n    descriptor: BlockDescriptor,\n    node: TNode,\n    childrenOutput: Output,\n    resolvedAttrs: Attrs,\n  ): Output;\n\n  // ─── Tree Traversal ────────────────────────────────────────────────────\n\n  protected renderNode(node: TNode): Output {\n    // Node overrides — checked first, can override any node type including root/text\n    const override = this.nodeOverrides[node.type];\n    if (override) {\n      const defaultRender =\n        node.type === 'root'\n          ? () => this.renderChildren(node)\n          : node.type === 'text'\n            ? () => this.renderTextNode(node)\n            : () => this.defaultRenderBlock(node);\n\n      return override(node, {\n        defaultRender,\n        renderNode: (child: TNode) => this.renderNode(child),\n        renderChildren: (parent: TNode) => this.renderChildren(parent),\n      });\n    }\n\n    // Root node: just render children\n    if (node.type === 'root') {\n      return this.renderChildren(node);\n    }\n\n    // Inline text leaf\n    if (node.type === 'text') {\n      return this.renderTextNode(node);\n    }\n\n    return this.defaultRenderBlock(node);\n  }\n\n  /**\n   * Standard block rendering path: looks up the block handler,\n   * resolves attributes, and renders. Falls back to `onUnknownNode`\n   * if configured, then to rendering children only.\n   */\n  protected defaultRenderBlock(node: TNode): Output {\n    const blockConfig = this.blocks[node.type];\n    if (blockConfig) {\n      const childrenOutput = this.renderChildren(node);\n      const resolvedAttrs = this.resolveBlockAttributes(node);\n\n      if (isBlockDescriptor(blockConfig)) {\n        return this.renderBlockFromDescriptor(blockConfig, node, childrenOutput, resolvedAttrs);\n      }\n\n      return blockConfig(node, childrenOutput, resolvedAttrs);\n    }\n\n    // Unknown node hook — lets consumers handle custom/unknown types\n    if (this.onUnknownNode) {\n      const result = this.onUnknownNode(node);\n      if (result !== undefined) return result;\n    }\n\n    // Fallback: render children only (transparent wrapper)\n    return this.renderChildren(node);\n  }\n\n  protected renderChildren(node: TNode): Output {\n    const renderedChildren = node.children.map((child) => this.renderNode(child));\n    return this.joinChildren(renderedChildren);\n  }\n\n  /**\n   * Render a text node with two-phase mark application:\n   * 1. Collect attributor contributions into a single `Attrs`\n   * 2. Apply element marks from innermost to outermost\n   *    - The innermost element mark receives the collected attrs\n   *    - If no element marks, wrap in a default element with collected attrs\n   */\n  protected renderTextNode(node: TNode): Output {\n    let output = this.renderText(typeof node.data === 'string' ? node.data : '');\n\n    // Phase 1: Collect attributor contributions\n    const collectedAttrs = this.collectAttributorAttrs(node);\n\n    // Phase 2: Collect and sort element marks\n    const elementMarks = this.getApplicableElementMarks(node);\n    elementMarks.sort((a, b) => a.priority - b.priority);\n\n    const hasCollected = this.hasAttrs(collectedAttrs);\n\n    if (elementMarks.length > 0) {\n      const attrTargetIdx = this.attributorTarget === 'outermost' ? elementMarks.length - 1 : 0;\n\n      // Apply element marks from innermost to outermost\n      for (const [i, entry] of elementMarks.entries()) {\n        const { mark, value } = entry;\n        const attrs = i === attrTargetIdx && hasCollected ? collectedAttrs : undefined;\n\n        if (isSimpleTagMark(mark)) {\n          const tag = typeof mark.tag === 'function' ? mark.tag(value) : mark.tag;\n          output = this.renderSimpleTag(tag, output, attrs);\n        } else {\n          output = mark(output, value, node, attrs);\n        }\n      }\n    } else if (hasCollected) {\n      // No element marks, but attributors contributed attrs — wrap in default element\n      output = this.wrapWithAttrs(output, collectedAttrs);\n    }\n\n    return output;\n  }\n\n  /**\n   * Resolve block attributes by running all configured resolvers\n   * and merging their results.\n   */\n  protected resolveBlockAttributes(node: TNode): Attrs {\n    let result = this.emptyAttrs();\n\n    for (const resolver of this.blockAttributeResolvers) {\n      const contribution = resolver(node);\n      if (this.hasAttrs(contribution)) {\n        result = this.hasAttrs(result) ? this.mergeAttrs(result, contribution) : contribution;\n      }\n    }\n\n    return result;\n  }\n\n  /**\n   * Collect inline attributor contributions from a node's attributes.\n   */\n  private collectAttributorAttrs(node: TNode): Attrs {\n    let result = this.emptyAttrs();\n\n    for (const [attrName, attrValue] of Object.entries(node.attributes)) {\n      const handler = this.attributors[attrName];\n      if (handler) {\n        const contribution = handler(attrValue, node);\n        if (this.hasAttrs(contribution)) {\n          result = this.hasAttrs(result) ? this.mergeAttrs(result, contribution) : contribution;\n        }\n      }\n    }\n\n    return result;\n  }\n\n  /**\n   * Get applicable element marks (from `marks` config) for a text node.\n   * Excludes attributes handled by `attributors`.\n   */\n  private getApplicableElementMarks(node: TNode): Array<{\n    name: string;\n    mark: MarkHandler<Output, Attrs> | SimpleTagMark;\n    value: unknown;\n    priority: number;\n  }> {\n    const result: Array<{\n      name: string;\n      mark: MarkHandler<Output, Attrs> | SimpleTagMark;\n      value: unknown;\n      priority: number;\n    }> = [];\n\n    for (const [attrName, attrValue] of Object.entries(node.attributes)) {\n      const mark = this.marks[attrName];\n      if (mark) {\n        const priority = this.markPriorities[attrName] ?? 0;\n        result.push({\n          name: attrName,\n          mark,\n          value: attrValue,\n          priority,\n        });\n      }\n    }\n\n    return result;\n  }\n}\n"]}