{"version":3,"file":"code-builder.mjs","names":[],"sources":["../../src/codegen/code-builder.ts"],"sourcesContent":["import type { CodeBuilder, CodeBuildResult } from './types.ts';\n\ninterface ImportEntry {\n  specifiers: Set<string>;\n  defaultSpecifier?: string;\n  typeOnly: boolean;\n}\n\ninterface CodeLine {\n  type: 'line' | 'raw' | 'block-open' | 'block-close';\n  content: string;\n}\n\nclass CodeBuilderImpl implements CodeBuilder {\n  private imports = new Map<string, ImportEntry>();\n  private lines: CodeLine[] = [];\n  private indent: number;\n\n  constructor(indent = 0) {\n    this.indent = indent;\n  }\n\n  import(specifier: string | string[], source: string): CodeBuilder {\n    const specs = Array.isArray(specifier) ? specifier : [specifier];\n    const existing = this.imports.get(source);\n    if (existing) {\n      for (const s of specs) existing.specifiers.add(s);\n      // If we're adding a value import and existing was type-only, downgrade to value\n      existing.typeOnly = false;\n    } else {\n      this.imports.set(source, { specifiers: new Set(specs), typeOnly: false });\n    }\n    return this;\n  }\n\n  importDefault(name: string, source: string): CodeBuilder {\n    const existing = this.imports.get(source);\n    if (existing) {\n      existing.defaultSpecifier = name;\n      existing.typeOnly = false;\n    } else {\n      this.imports.set(source, { specifiers: new Set(), defaultSpecifier: name, typeOnly: false });\n    }\n    return this;\n  }\n\n  importType(specifier: string | string[], source: string): CodeBuilder {\n    const specs = Array.isArray(specifier) ? specifier : [specifier];\n    const existing = this.imports.get(source);\n    if (existing) {\n      for (const s of specs) existing.specifiers.add(s);\n      // Don't downgrade: if existing is value import, keep it as value\n    } else {\n      this.imports.set(source, { specifiers: new Set(specs), typeOnly: true });\n    }\n    return this;\n  }\n\n  line(code?: string): CodeBuilder {\n    this.lines.push({ type: 'line', content: code ?? '' });\n    return this;\n  }\n\n  block(\n    openOrBuilder: string | ((b: CodeBuilder) => CodeBuilder),\n    builderOrClose?: string | ((b: CodeBuilder) => CodeBuilder),\n    closeOrBuilder?: string | ((b: CodeBuilder) => CodeBuilder),\n  ): CodeBuilder {\n    let open: string | undefined;\n    let close: string | undefined;\n    let builder: (b: CodeBuilder) => CodeBuilder;\n\n    if (typeof openOrBuilder === 'function') {\n      // block(builder)\n      builder = openOrBuilder;\n    } else if (typeof builderOrClose === 'function') {\n      // block(open, builder, close?)\n      open = openOrBuilder;\n      builder = builderOrClose;\n      close = typeof closeOrBuilder === 'string' ? closeOrBuilder : undefined;\n    } else if (typeof closeOrBuilder === 'function') {\n      // block(open, close, builder)\n      open = openOrBuilder;\n      close = typeof builderOrClose === 'string' ? builderOrClose : undefined;\n      builder = closeOrBuilder;\n    } else {\n      throw new Error('Invalid block() arguments');\n    }\n\n    // Always push block-open to increment indent\n    this.lines.push({ type: 'block-open', content: open ?? '' });\n\n    const inner = new CodeBuilderImpl(this.indent + 1);\n    builder(inner);\n\n    // Merge inner imports into ours\n    for (const [source, entry] of inner.imports) {\n      const existing = this.imports.get(source);\n      if (existing) {\n        for (const s of entry.specifiers) existing.specifiers.add(s);\n        if (entry.defaultSpecifier) existing.defaultSpecifier = entry.defaultSpecifier;\n        if (!entry.typeOnly) existing.typeOnly = false;\n      } else {\n        this.imports.set(source, {\n          specifiers: new Set(entry.specifiers),\n          defaultSpecifier: entry.defaultSpecifier,\n          typeOnly: entry.typeOnly,\n        });\n      }\n    }\n\n    // Merge inner lines with extra indentation\n    for (const line of inner.lines) {\n      this.lines.push(line);\n    }\n\n    // Always push block-close to decrement indent\n    this.lines.push({ type: 'block-close', content: close ?? '' });\n\n    return this;\n  }\n\n  comment(text: string): CodeBuilder {\n    this.lines.push({ type: 'line', content: `// ${text}` });\n    return this;\n  }\n\n  docComment(text: string): CodeBuilder {\n    const lines = text.split('\\n');\n    if (lines.length === 1) {\n      this.lines.push({ type: 'line', content: `/** ${text} */` });\n    } else {\n      this.lines.push({ type: 'line', content: '/**' });\n      for (const line of lines) {\n        this.lines.push({ type: 'line', content: ` * ${line}` });\n      }\n      this.lines.push({ type: 'line', content: ' */' });\n    }\n    return this;\n  }\n\n  todoComment(text: string): CodeBuilder {\n    this.lines.push({ type: 'line', content: `// TODO: ${text}` });\n    return this;\n  }\n\n  raw(code: string): CodeBuilder {\n    this.lines.push({ type: 'raw', content: code });\n    return this;\n  }\n\n  build(): CodeBuildResult {\n    const parts: string[] = [];\n\n    // Emit imports first\n    if (this.imports.size > 0) {\n      const typeImports: string[] = [];\n      const valueImports: string[] = [];\n\n      for (const [source, entry] of this.imports) {\n        const specs = [...entry.specifiers].sort();\n        const namedPart =\n          specs.length > 0 ? (specs.length === 1 && !specs[0]!.includes(' ') ? `{ ${specs[0]} }` : `{ ${specs.join(', ')} }`) : null;\n        const specStr = entry.defaultSpecifier\n          ? namedPart\n            ? `${entry.defaultSpecifier}, ${namedPart}`\n            : entry.defaultSpecifier\n          : namedPart!;\n\n        const line = entry.typeOnly ? `import type ${specStr} from '${source}'` : `import ${specStr} from '${source}'`;\n\n        if (entry.typeOnly) {\n          typeImports.push(line);\n        } else {\n          valueImports.push(line);\n        }\n      }\n\n      // Value imports first, then type imports\n      parts.push([...valueImports, ...typeImports].join('\\n'));\n      parts.push('');\n    }\n\n    // Emit code lines\n    let currentIndent = this.indent;\n    for (const line of this.lines) {\n      if (line.type === 'raw') {\n        parts.push(line.content);\n      } else if (line.type === 'block-open') {\n        if (line.content) {\n          const indent = '  '.repeat(currentIndent);\n          parts.push(`${indent}${line.content}`);\n        }\n        currentIndent++;\n      } else if (line.type === 'block-close') {\n        currentIndent--;\n        if (line.content) {\n          const indent = '  '.repeat(currentIndent);\n          parts.push(`${indent}${line.content}`);\n        }\n      } else {\n        // Regular line\n        if (line.content === '') {\n          parts.push('');\n        } else {\n          const indent = '  '.repeat(currentIndent);\n          parts.push(`${indent}${line.content}`);\n        }\n      }\n    }\n\n    return {\n      text: parts.join('\\n'),\n      imports: new Map(\n        [...this.imports].map(([source, entry]) => [source, { specifiers: new Set(entry.specifiers), typeOnly: entry.typeOnly }]),\n      ),\n    };\n  }\n}\n\n/**\n * Create a new CodeBuilder for constructing TypeScript source files.\n */\nexport function createCodeBuilder(): CodeBuilder {\n  return new CodeBuilderImpl();\n}\n"],"mappings":"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"}