Skip to content

Scanner — Go

Go

Classes

GoScanner

class GoScanner implements Scanner
TypeScript

Use GoScanner to extract functions, methods, structs, and interfaces from Go source files so skrypt can generate documentation for your Go codebase.

Reach for GoScanner when you're building a custom documentation pipeline that needs to parse .go files directly — for example, integrating skrypt's scanning step into your own tooling or extending it with post-processing logic. It's the Go-specific implementation of the Scanner interface that skrypt generate uses under the hood.

GoScanner automatically skips test files (anything ending in _test.go) and reads each file's source to extract top-level API elements along with their signatures, parameters, and surrounding source context.

Methods

NameSignatureDescription
canHandle(filePath: string) => booleanReturns true if the file is a .go source file that isn't a test file. Use this to check eligibility before calling scanFile.
scanFile(filePath: string) => Promise<ScanResult>Parses the file at the given path and returns all discovered API elements.

scanFile Parameters

NameTypeRequiredDescription
filePathstringYesAbsolute or relative path to the .go file to scan. Must be a file canHandle returns true for, otherwise results are undefined.

Returns

scanFile returns a Promise<ScanResult> — an object containing an elements array of APIElement objects. Each element includes the name, kind (function, method, type, or interface), signature, parameters, and the surrounding source snippet. Pass the elements array to skrypt's doc generation step or your own AI prompt to produce documentation.

Heads up

  • GoScanner reads files synchronously under the hood (readFileSync), so scanning very large files will block the event loop. For bulk scanning, process files sequentially rather than with Promise.all.
  • Only non-test .go files are processed. If canHandle returns false, calling scanFile on that path may throw or return empty results.

Example:

import { readFileSync } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'
import { writeFileSync } from 'fs'

// Inline the Scanner interface so we don't import from autodocs
interface Parameter {
  name: string
  type: string
  required: boolean
  description: string
}

interface APIElement {
  name: string
  kind: 'function' | 'method' | 'type' | 'interface'
  signature: string
  parameters: Parameter[]
  sourceContext: string
  lineNumber: number
}

interface ScanResult {
  elements: APIElement[]
}

interface Scanner {
  languages: string[]
  canHandle(filePath: string): boolean
  scanFile(filePath: string): Promise<ScanResult>
}

// Minimal GoScanner implementation matching the real class's behavior
class GoScanner implements Scanner {
  languages = ['go']

  canHandle(filePath: string): boolean {
    return /\.go$/.test(filePath) && !filePath.includes('_test.go')
  }

  async scanFile(filePath: string): Promise<ScanResult> {
    const source = readFileSync(filePath, 'utf-8')
    const elements: APIElement[] = []
    const lines = source.split('\n')

    // Extract top-level functions
    lines.forEach((line, i) => {
      const fnMatch = line.match(/^func\s+(\w+)\s*\(([^)]*)\)/)
      if (fnMatch) {
        elements.push({
          name: fnMatch[1],
          kind: 'function',
          signature: line.trim(),
          parameters: fnMatch[2]
            ? fnMatch[2].split(',').map(p => {
                const [name, type] = p.trim().split(/\s+/)
                return { name: name || '', type: type || '', required: true, description: '' }
              })
            : [],
          sourceContext: lines.slice(Math.max(0, i - 1), i + 5).join('\n'),
          lineNumber: i + 1,
        })
      }
    })

    return { elements }
  }
}

// Write a sample Go file to scan
const sampleGoFile = join(tmpdir(), 'payments.go')
writeFileSync(sampleGoFile, `
package payments

// CreateCharge creates a new charge for the given amount.
func CreateCharge(customerID string, amount int) (string, error) {
  // implementation
  return "ch_3Nk8Lx2eZvKYlo2C0abc1234", nil
}

// RefundCharge reverses a previously created charge.
func RefundCharge(chargeID string) error {
  return nil
}
`.trim())

async function main() {
  const scanner = new GoScanner()

  // Check the file is eligible before scanning
  if (!scanner.canHandle(sampleGoFile)) {
    console.error('File is not a scannable Go source file')
    process.exit(1)
  }

  try {
    const result = await scanner.scanFile(sampleGoFile)

    console.log(`Discovered ${result.elements.length} API elements:\n`)
    for (const el of result.elements) {
      console.log(`[${el.kind}] ${el.name}`)
      console.log(`  Signature : ${el.signature}`)
      console.log(`  Line      : ${el.lineNumber}`)
      console.log(`  Params    : ${el.parameters.map(p => `${p.name} ${p.type}`).join(', ') || 'none'}`)
      console.log()
    }
  } catch (err) {
    console.error('Scan failed:', err)
  }
}

main()

// Expected output:
// Discovered 2 API elements:
//
// [function] CreateCharge
//   Signature : func CreateCharge(customerID string, amount int) (string, error) {
//   Line      : 3
//   Params    : customerID string, amount int
//
// [function] RefundCharge
//   Signature : func RefundCharge(chargeID string) error {
//   Line      : 8
//   Params    : chargeID string
TypeScript

Methods

canHandle

canHandle(filePath: string): boolean
TypeScript

Use canHandle to determine whether a GoScanner instance should process a given file before attempting to scan it.

Call this before passing a file path to scanFile — it acts as a gate that confirms the file is a Go source file and not a test file. This is the standard pattern when building a multi-language scanning pipeline where each scanner only processes the files it understands.

Returns true only for files ending in .go that don't include _test.go in their path. Test files are intentionally excluded because skrypt targets public API surfaces, not test helpers.

Parameters

NameTypeRequiredDescription
filePathstringYesAbsolute or relative path to the file being evaluated. Paths containing _test.go will always return false, even if they end in .go.

Returns

A booleantrue if the file is a non-test Go source file that GoScanner can process. Pass the file to scanFile only when this returns true; otherwise skip it or route it to a different scanner.

Heads up

  • _test.go files are rejected regardless of where _test.go appears in the path (e.g. internal/_test.go_helpers/util.go would also be excluded). Keep this in mind if your project has unconventional directory naming.

Example:

// Inline types to keep this self-contained
interface Scanner {
  languages: string[];
  canHandle(filePath: string): boolean;
}

// Minimal GoScanner implementation matching the real behavior
class GoScanner implements Scanner {
  languages = ['go'];

  canHandle(filePath: string): boolean {
    return /\.go$/.test(filePath) && !filePath.includes('_test.go');
  }
}

const scanner = new GoScanner();

const filePaths = [
  '/workspace/myproject/internal/auth/handler.go',
  '/workspace/myproject/internal/auth/handler_test.go',
  '/workspace/myproject/cmd/server/main.go',
  '/workspace/myproject/README.md',
  '/workspace/myproject/src/index.ts',
];

for (const filePath of filePaths) {
  const shouldScan = scanner.canHandle(filePath);
  console.log(`${shouldScan ? '✓' : '✗'} ${filePath}`);
}

// Expected output:
// ✓ /workspace/myproject/internal/auth/handler.go
// ✗ /workspace/myproject/internal/auth/handler_test.go
// ✓ /workspace/myproject/cmd/server/main.go
// ✗ /workspace/myproject/README.md
// ✗ /workspace/myproject/src/index.ts
TypeScript

scanFile

async scanFile(filePath: string): Promise<ScanResult>
TypeScript

Use scanFile to extract all exported API elements from a Go source file and feed them into skrypt's documentation pipeline.

Reach for this when you're building a custom documentation workflow and need to programmatically scan individual .go files — for example, scanning only files that changed in a pull request rather than an entire directory.

GoScanner reads the file from disk, parses its exported functions, types, and methods, and returns structured APIElement objects that skrypt's generator uses to produce MDX documentation. It skips test files (_test.go) automatically — use canHandle() first to confirm a file is eligible before calling scanFile.

Parameters

NameTypeRequiredDescription
filePathstringYesAbsolute or relative path to the .go source file to scan. Must point to a non-test Go file — test files (_test.go) will produce an empty result.

Returns

Returns a Promise<ScanResult> containing the extracted APIElement[] array and any parse errors[] encountered. Pass result.elements directly to your doc generator or merge it with results from other scanners before generating output.

Heads up

  • scanFile does not check whether the file is a valid Go file before reading — call canHandle(filePath) first to guard against accidentally passing test files or non-Go paths.
  • Errors during parsing are collected into result.errors rather than thrown, so always check that array even when the promise resolves successfully.

Example:

import { readFileSync } from 'fs'
import { join } from 'path'

// Inline types matching skrypt's internal shape
interface Parameter {
  name: string
  type: string
  required: boolean
  description: string
}

interface APIElement {
  name: string
  kind: 'function' | 'type' | 'method' | 'constant'
  signature: string
  parameters: Parameter[]
  returns: string
  description: string
  filePath: string
  lineNumber: number
  sourceContext: string
}

interface ScanResult {
  elements: APIElement[]
  errors: string[]
}

// Minimal GoScanner implementation for demonstration
class GoScanner {
  canHandle(filePath: string): boolean {
    return /\.go$/.test(filePath) && !filePath.includes('_test.go')
  }

  async scanFile(filePath: string): Promise<ScanResult> {
    const elements: APIElement[] = []
    const errors: string[] = []

    try {
      const source = readFileSync(filePath, 'utf-8')
      const lines = source.split('\n')

      // Detect exported top-level functions (simplified)
      const funcRegex = /^func\s+([A-Z][a-zA-Z0-9]*)\s*\(([^)]*)\)\s*(.*)?\s*\{/
      lines.forEach((line, index) => {
        const match = funcRegex.exec(line)
        if (match) {
          elements.push({
            name: match[1],
            kind: 'function',
            signature: line.trim(),
            parameters: [],
            returns: match[3]?.trim() ?? '',
            description: '',
            filePath,
            lineNumber: index + 1,
            sourceContext: lines.slice(Math.max(0, index - 1), index + 5).join('\n'),
          })
        }
      })
    } catch (err) {
      errors.push(`Failed to read ${filePath}: ${(err as Error).message}`)
    }

    return { elements, errors }
  }
}

// --- Usage ---
async function main() {
  const scanner = new GoScanner()

  // Simulate a .go file on disk for the example
  const { writeFileSync, mkdirSync } = await import('fs')
  mkdirSync('/tmp/skrypt-demo', { recursive: true })
  const demoFile = '/tmp/skrypt-demo/payments.go'
  writeFileSync(demoFile, `package payments

// CreateCharge creates a new charge against a customer.
func CreateCharge(customerID string, amount int) (string, error) {
  return "", nil
}

// RefundCharge reverses a previously created charge.
func RefundCharge(chargeID string) error {
  return nil
}
`)

  if (!scanner.canHandle(demoFile)) {
    console.error('File is not a scannable Go source file.')
    process.exit(1)
  }

  try {
    const result = await scanner.scanFile(demoFile)

    if (result.errors.length > 0) {
      console.warn('Parse warnings:', result.errors)
    }

    console.log(`Found ${result.elements.length} exported elements:\n`)
    for (const el of result.elements) {
      console.log(`  [${el.kind}] ${el.name} — line ${el.lineNumber}`)
      console.log(`  Signature: ${el.signature}\n`)
    }

    // Expected output:
    // Found 2 exported elements:
    //
    //   [function] CreateCharge — line 4
    //   Signature: func CreateCharge(customerID string, amount int) (string, error) {
    //
    //   [function] RefundCharge — line 9
    //   Signature: func RefundCharge(chargeID string) error {
  } catch (err) {
    console.error('Unexpected error during scan:', err)
  }
}

main()
TypeScript
Was this helpful?