feat(generate): add vendor_runtime mode for self-contained output#378
feat(generate): add vendor_runtime mode for self-contained output#378
Conversation
Add `gen.gleam.vendor_runtime` to the config schema. When enabled, `sqlode generate` copies `sqlode/runtime.gleam` verbatim into the output directory as `runtime.gleam` and rewrites the generated `params.gleam` / `queries.gleam` / `<driver>_adapter.gleam` imports to point at `<module_path>/runtime` instead of the shared `sqlode/runtime`. The resulting package no longer needs sqlode as a runtime dependency — only as a dev dependency to run the generator itself. - `src/sqlode/model.gleam`: add `vendor_runtime: Bool` to `GleamOutput` (default `False`, backwards compatible). - `src/sqlode/config.gleam`: parse the new field and accept it in the `sql.gen.gleam` unknown-keys guard. - `src/sqlode/codegen/common.gleam`: add a `runtime_import_path` helper that returns `<module_path>/runtime` when vendoring is on and `sqlode/runtime` otherwise. Used by every generated module that references the runtime. - `src/sqlode/codegen/queries.gleam` / `adapter.gleam` / `params.gleam`: replace the hard-coded `import sqlode/runtime` lines with `common.runtime_import_path(gleam)` so both modes produce matching imports. `params.render/5` gains a `runtime_import` parameter; the single caller in `generate.gleam` plumbs the correct path through. - `src/sqlode/generate.gleam`: when `vendor_runtime` is `True`, add a `runtime.gleam` file to the generated set whose content is read from the sqlode source on disk. Tries a development checkout first, then the extracted-hex layout a user project has after `gleam add sqlode`, and finally the built dev artefact path. - `test/generate_test.gleam`: pin both modes — default keeps the `sqlode/runtime` imports and emits no vendored file; enabled emits `runtime.gleam` identical to the tracked source and rewrites every `params.gleam` / `queries.gleam` import. - `test/codegen_test.gleam`: fill the new `params.render` runtime-import argument on existing call sites. - `integration_test/cases.sh`: add `case_compile_vendor_runtime` that writes a `gleam.toml` with NO `sqlode` dependency and then builds the generated project end-to-end, proving the vendored output stands alone. - `README.md`: document the new option, the trade-off, and the build-time dependency reduction. Verified `just all` (format-check + check + build + `gleam test` + ShellSpec) and the full `integration_test/run.sh` (all seven cases) pass end-to-end. Closes #302
ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (11)
📝 WalkthroughWalkthroughA new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
Summary
Add
gen.gleam.vendor_runtimeto the config schema. When enabled,sqlode generatecopiessqlode/runtime.gleaminto the output directory asruntime.gleamand rewrites the generated imports to point at the local copy, so the target project no longer needssqlodeas a runtime dependency. Matches what sqlc users expect from code generation tools: the emitted package stands on its own. Closes #302.Changes
src/sqlode/model.gleam: addvendor_runtime: BooltoGleamOutput(defaultFalse, backwards compatible with every existingmodel.GleamOutput(...)literal in the tests).src/sqlode/config.gleam: acceptvendor_runtimein thesql.gen.gleamunknown-keys guard and parse it as an optional bool.src/sqlode/codegen/common.gleam: addruntime_import_path(gleam)returning<module_path>/runtimewhen vendoring andsqlode/runtimeotherwise. Used from every generated module that references the runtime.src/sqlode/codegen/queries.gleam/codegen/adapter.gleam: replace the hard-codedimport sqlode/runtimeline withcommon.runtime_import_path(gleam).src/sqlode/codegen/params.gleam:render/5gains aruntime_importargument so the existing.{type Value}suffix can be attached to either import path. Existing call sites passsqlode/runtime.src/sqlode/generate.gleam: whenvendor_runtimeisTrue, add aruntime.gleamfile to the generated set whose content is read from the sqlode source on disk. The loader tries a development checkout, then the extracted-hex layout a user project has aftergleam add sqlode, and finally the dev artefact path.test/generate_test.gleam: pin both modes — default keepsimport sqlode/runtimeand emits no vendored file; enabled emitsruntime.gleambyte-for-byte identical to the tracked source and rewrites every generated-module import.test/codegen_test.gleam: fill the newparams.renderruntime-import argument on existing call sites.integration_test/cases.sh: addcase_compile_vendor_runtimewhich writes agleam.tomlwith NOsqlodedependency and then builds the generated project end-to-end, proving the vendored output stands alone. Registered inALL_INTEGRATION_CASES.README.md: document the new option, the trade-off (smaller shared dependency vs. self-contained output), and how the import rewriting works.Design Decisions
runtime.gleamis exactly the file sqlode itself ships. That means any API that the generated code already uses (placeholder markers,PlaceholderStyle,RawQuery,prepare, encoding helpers) is guaranteed to be available, without the risk of a hand-written minimal copy drifting from the real one. The trade-off — vendored output needssqlode generateto re-run when the runtime changes — is documented in the README.src/sqlode/(dev checkout),build/packages/sqlode/src/sqlode/(user rangleam add sqlode), orbuild/dev/erlang/sqlode/src/sqlode/(another Gleam build layout). Each is tried in order and the first readable file wins. On a genuine not-found, the vendored file is simply omitted rather than written with garbage — the flag defaults off, so users who opt in will notice the missing file.common.runtime_import_path. Centralising the decision in one place means when the runtime module ever moves or renames, only one function needs to change. Each generated module already importedcommon, so no new dependency edges.sqlode-freegleam.toml. The whole point of the feature is that the generated project does not need sqlode as a runtime dep. The new case asserts that by literally not listing sqlode ingleam.toml— andgleam buildstill succeeds.Limitations
vendor_runtime: truewill not find the runtime file when invoked from an escript-only install. The next logical step is to embed the source as a Gleam string constant or ship it viapriv/; kept out of scope here because it does not affect the commongleam run -m sqlodeor dev-checkout flows.sqlode generatetime. Users must re-run the generator to pick up a new sqlode runtime; the trade-off is spelled out in the README.pog/sqlight) are unaffected. Vendoring only removes thesqlode/runtimeedge; native adapters still need their chosen driver package.Summary by CodeRabbit
New Features
vendor_runtime: trueconfiguration option, which bundles the runtime module directly into generated code and automatically updates imports to reference the local copysqlodeas a required runtime dependency when vendor mode is enabledDocumentation