Skip to content

Extract code generation logic into internal/api package#4418

Draft
kyleconroy wants to merge 4 commits intomainfrom
claude/create-internal-api-package-INVIA
Draft

Extract code generation logic into internal/api package#4418
kyleconroy wants to merge 4 commits intomainfrom
claude/create-internal-api-package-INVIA

Conversation

@kyleconroy
Copy link
Copy Markdown
Collaborator

This refactoring extracts the core code generation logic from internal/cmd into a new internal/api package, establishing a programmatic API for sqlc that can be used independently of the CLI.

Summary

The change moves code generation orchestration and related utilities into a new internal/api package with a clean public interface (api.Generate), while the CLI continues to work through a thin wrapper. This enables programmatic use of sqlc without CLI dependencies.

Key Changes

  • New internal/api package with the following modules:

    • api.go: Package documentation describing the public API design
    • generate.go: Core Generate() function and GenerateOptions/GenerateResult types
    • process.go: Query set processing logic and resultProcessor interface
    • codegen.go: Code generation orchestration (plugin handling, format selection)
    • config.go: Configuration file reading and validation
    • parse.go: SQL parsing logic
    • shim.go: Protocol buffer message conversion helpers
  • CLI refactoring (internal/cmd/generate.go):

    • Generate() now delegates to api.Generate() with appropriate option translation
    • Removed ~180 lines of code generation logic that moved to api package
    • Maintains backward compatibility with existing CLI behavior
  • Test updates (internal/endtoend/endtoend_test.go):

    • Updated to use api.Generate() directly instead of CLI wrapper
    • Simplified test setup by removing CLI-specific option structures

Notable Implementation Details

  • GenerateOptions includes DisableProcessPlugins flag to support SQLCDEBUG=processplugins=0 environment variable handling
  • GenerateResult always returns a non-nil Files map (empty on error) and collects errors in an Errors slice
  • Configuration mutation hook (MutateConfig) provided for testing purposes
  • Process-based plugin validation extracted into validateProcessPluginsDisabled() for reuse
  • Internal outputPair type (renamed from OutputPair) encapsulates SQL configuration with generation targets

https://claude.ai/code/session_01RCzB2JR5Y5ScFDUmwcxGVZ

claude added 4 commits April 28, 2026 05:33
The new package mirrors esbuild's Build API: a single api.Generate(ctx,
api.GenerateOptions{}) call returns a GenerateResult containing the
generated files and any errors. Most of cmd/generate.go's logic moves
here as unexported helpers; the only exported names are Generate,
GenerateOptions, and GenerateResult.

cmd.Generate is now a thin wrapper that translates the CLI's Options
struct into api.GenerateOptions. The endtoend tests call api.Generate
directly for TestExamples, TestReplay (generate command), and the
benchmarks.

https://claude.ai/code/session_01RCzB2JR5Y5ScFDUmwcxGVZ
The two new boolean options let api.Generate cover the writefiles loop
and the diff comparison that previously lived in cmd. The compile
command becomes Generate with neither flag set, generate maps to
Write: true, and diff maps to Diff: true.

While simplifying GenerateOptions:
* Drop MutateConfig — tests now express config mutations by writing a
  temporary configuration file via writeMutatedConfig and pointing
  GenerateOptions.File at it. The mutated config is parsed (always to
  v2 shape), forced to version "2", and round-tripped via yaml.
* Drop DisableProcessPlugins from the API surface; we will revisit how
  to express that constraint.
* Add MarshalJSON/MarshalYAML to AnalyzerDatabase so the parsed Config
  round-trips through yaml.Marshal cleanly, which is what the new test
  helper relies on.

cmd/diff.go is gone and cmd/generate.go is left with only the helpers
(readConfig, parse, printFileErr) other cmd commands still use.

https://claude.ai/code/session_01RCzB2JR5Y5ScFDUmwcxGVZ
Add an explicit allowlist of process-based plugin names to
api.GenerateOptions. Generate fails before any parse or codegen runs if
the configuration declares a process plugin whose name is not in the
list. The "Insecure" prefix mirrors crypto/tls.Config.InsecureSkipVerify
to flag the trust decision callers are making — process plugins execute
arbitrary local commands.

The CLI populates the allowlist by scanning the user's own config for
declared process plugins, so `sqlc generate`, `sqlc compile`, and
`sqlc diff` keep working. SQLCDEBUG=processplugins=0 still disables
process plugins by leaving the allowlist nil.

https://claude.ai/code/session_01RCzB2JR5Y5ScFDUmwcxGVZ
The struct collapses to five fields: Config (io.Reader), Stderr, Write,
Diff, InsecureProcessPluginNames. api.Generate parses the config from
the reader and treats every relative path in it as relative to the
current working directory.

CLI: each command opens the config file, reads its bytes, parses it once
to extract declared process-plugin names, then chdirs to the config's
directory before invoking api.Generate. Single-process so chdir is fine.

Tests: a new mutatedConfigBytes helper parses the test's sqlc.yaml,
forces version "2", rewrites every schema/queries/output path to be
absolute relative to the test directory, and re-encodes as YAML — so
api.Generate works without knowing the source directory. Optional
mutate callback applies extra changes (managed-db servers etc.) and
also drops a temp file alongside the original for cmd.Vet which still
takes a config path.

cmd/process.go and cmd/vet.go now skip joining their dir parameter when
the config-supplied path is already absolute.

KNOWN ISSUE: TestReplay parse-error tests and the diff_output tests
fail because the api now emits absolute paths in error messages and
unified-diff labels (no config-dir context to strip). Either add a
BaseDir hint back to GenerateOptions or update the affected test
expectations to match.

https://claude.ai/code/session_01RCzB2JR5Y5ScFDUmwcxGVZ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants