src/render owns Docker Compose interpolation for stack data after compose generation and override
merging, before deployment output is written or applied. Its main entry point is
renderStack(options: RenderOptions), which accepts parsed ComposeData, builds per-service
variable scopes, substitutes environment references inside service definitions, rewrites relative
paths to absolute paths, and returns a RenderResult with rendered data plus warnings.
The module handles these rendering responsibilities:
- Parse simple dotenv files through
parseEnvFile(path). - Resolve
env_filereferences throughresolveEnvPath(relPath, projectDir, repoRoot). - Normalize
service.environmentthroughcoerceEnvironmentToDict(env). - Build service-specific interpolation scopes through
buildServiceScope(service, baseEnv, projectDir, repoRoot). - Substitute
${VAR},${VAR-default},${VAR:-default}, and$VARforms throughsubstitute(s, vars). - Recursively interpolate nested compose structures through
deepInterpolate(obj, vars). - Convert relative service
env_fileand bind mount sources throughabsolutizeServicePaths(service, projectDir, repoRoot).
- Functional pipeline:
renderStackcomposes smaller pure or mostly pure helpers in a fixed order, from scope construction to interpolation to path normalization. - Non-mutating transforms:
renderStackshallow-clonesComposeData, creates a newservicesmap, andabsolutizeServicePathsreturns a copiedServiceDefinstead of mutating the input service. - Layered configuration scope:
buildServiceScopeapplies shell environment first, then serviceenv_filevalues in declaration order, thenservice.environmentwith highest priority. - Recursive structural traversal:
deepInterpolatepreserves scalar types and only transforms string leaves while walking arrays and plain object entries. - Regex-driven parsing:
INTERP_RE,PLAIN_VAR_RE,UNRESOLVED_RE, andREL_PATH_REcentralize interpolation matching, strict-mode detection, and relative path classification. - Warning accumulation:
renderStackrecords missingenv_filereferences and unresolved variables inRenderResult.warningsrather than throwing for ordinary render issues. Strict mode annotatesRenderResult.hasUnresolvedwhen service values still contain${VAR}patterns.
- Callers pass
RenderOptionstorenderStack, including parsedComposeData,projectDir,repoRoot, and optionalstrict. renderStackreads the shell environment withDeno.env.toObject()and shallow-clones the input compose data.- For each service in
data.services,renderStackcallsbuildServiceScope. buildServiceScopestarts with the shell environment, resolves each serviceenv_filewithresolveEnvPath, parses readable files withparseEnvFile, and overlays values fromcoerceEnvironmentToDict(service.environment).renderStackindependently checks declaredenv_filepaths withDeno.statand appends warnings for missing files.deepInterpolatewalks the service definition and callssubstitutefor each string value.substitutefirst processes braced expressions withINTERP_RE, then plain$VARexpressions withPLAIN_VAR_RE. Unresolved variables remain unchanged unless a default expression provides a replacement.absolutizeServicePathsnormalizes the interpolated service. It delegates env file paths to the privateabsolutizePath, short-form volume strings toabsolutizeBindMountString, long-form volume objects toabsolutizeVolumeMount, and relative path construction toresolvePath.- After service processing,
renderStackinterpolates non-service top-level compose keys with only the shell environment as scope. renderStackscans rendered service JSON for unresolved${VAR}patterns. In strict mode it setshasUnresolvedand records strict warnings. In non-strict mode it records warnings while leaving values unchanged.- The function returns
RenderResultcontaining the renderedComposeData, warnings, and optional strict-mode unresolved status.
src/compose/types.ts: SuppliesComposeData,ServiceDef, andVolumeMounttype contracts used by all render operations.- CLI render and deploy flows: Call
renderStackafter compose generation and override merging, then consumeRenderResult.datafor YAML output, deployment, or downstream validation. - Environment files:
parseEnvFile,resolveEnvPath, andbuildServiceScoperead service-level dotenv files declared by Composeenv_filefields. - Host process environment:
renderStackusesDeno.env.toObject()as the base scope for services and as the only scope for top-level non-service compose keys. - Filesystem:
resolveEnvPath,parseEnvFile, andrenderStackuse Deno filesystem APIs to resolve, read, and validate env file paths. - Docker Compose path semantics:
absolutizeServicePathspreserves named volumes and absolute paths while converting relativeenv_fileand bind mount sources so rendered YAML remains stable when executed from a different output directory.