@@ -109,35 +109,26 @@ func writeDependencyBlock(outBody *hclwrite.Body, dep AutoIncludeDependency, ori
109109}
110110
111111// writeNonDependencyContent writes non-dependency attributes and blocks from
112- // the autoinclude body. For each attribute:
113- // - If it references dependency.* variables, copy verbatim from source bytes
114- // - Otherwise, evaluate using evalCtx and write the literal value
115- // - Fallback to source bytes if evaluation fails
112+ // the autoinclude body. Each attribute expression is partially evaluated:
113+ // resolvable parts (locals, pure refs) become literals, while deferred parts
114+ // (dependency.*) keep their original source text. This enables mixed
115+ // expressions like "${local.env}-${dependency.vpc.outputs.vpc_id}" to be
116+ // partially resolved.
116117//
117118// Non-dependency blocks are always copied verbatim from source bytes.
118119func writeNonDependencyContent (outBody * hclwrite.Body , body * hclsyntax.Body , srcBytes []byte , evalCtx * hcl.EvalContext ) {
119120 for _ , attr := range sortedAttributes (body .Attributes ) {
120- if hasDeferredVarRefs (attr .Expr ) {
121- // Has dependency.* refs — copy verbatim.
122- exprBytes := rangeBytes (srcBytes , attr .Expr .Range ())
123- outBody .SetAttributeRaw (attr .Name , rawTokens (exprBytes ))
124- } else if evalCtx != nil {
125- // No deferred refs — try to evaluate.
126- val , diags := attr .Expr .Value (evalCtx )
127- if ! diags .HasErrors () {
128- outBody .SetAttributeValue (attr .Name , val )
129- } else {
130- // Fallback to source bytes.
131- exprBytes := rangeBytes (srcBytes , attr .Expr .Range ())
132- outBody .SetAttributeRaw (attr .Name , rawTokens (exprBytes ))
133- }
134- } else {
121+ if evalCtx == nil {
135122 exprBytes := rangeBytes (srcBytes , attr .Expr .Range ())
136123 outBody .SetAttributeRaw (attr .Name , rawTokens (exprBytes ))
124+
125+ continue
137126 }
127+
128+ result := PartialEval (attr .Expr , srcBytes , evalCtx , deferredRoots )
129+ outBody .SetAttributeRaw (attr .Name , rawTokens (result ))
138130 }
139131
140- // Non-dependency blocks are still copied verbatim.
141132 for _ , block := range body .Blocks {
142133 if block .Type == "dependency" {
143134 continue
@@ -147,18 +138,6 @@ func writeNonDependencyContent(outBody *hclwrite.Body, body *hclsyntax.Body, src
147138 }
148139}
149140
150- // hasDeferredVarRefs returns true if the expression references any
151- // dependency.* variables that cannot be evaluated at generation time.
152- func hasDeferredVarRefs (expr hclsyntax.Expression ) bool {
153- for _ , traversal := range expr .Variables () {
154- if traversal .RootName () == "dependency" {
155- return true
156- }
157- }
158-
159- return false
160- }
161-
162141// copyBlockFromSource copies a block from the original AST to hclwrite output,
163142// using source bytes for all attribute expressions.
164143func copyBlockFromSource (outBody * hclwrite.Body , block * hclsyntax.Block , srcBytes []byte ) {
0 commit comments