@@ -2,6 +2,7 @@ package plugin2host
22
33import (
44 "context"
5+ stdjson "encoding/json"
56 "errors"
67 "fmt"
78 "os"
@@ -205,6 +206,69 @@ func (w *nativeWalker) Exit(node hclsyntax.Node) hcl.Diagnostics {
205206 return nil
206207}
207208
209+ // extractJSONKeys extracts attribute names from JSON bytes using encoding/json.
210+ // This works for both object-based JSON {"foo": ...} and array-based JSON [{"foo": ...}].
211+ func extractJSONKeys (bytes []byte ) ([]string , error ) {
212+ // Try to unmarshal as an object first
213+ var obj map [string ]any
214+ if err := stdjson .Unmarshal (bytes , & obj ); err == nil {
215+ keys := make ([]string , 0 , len (obj ))
216+ for k := range obj {
217+ keys = append (keys , k )
218+ }
219+ return keys , nil
220+ }
221+
222+ // Try as an array of objects
223+ var arr []map [string ]any
224+ if err := stdjson .Unmarshal (bytes , & arr ); err != nil {
225+ return nil , err
226+ }
227+
228+ // Collect all unique keys from all objects in the array
229+ keysMap := make (map [string ]bool )
230+ for _ , obj := range arr {
231+ for k := range obj {
232+ keysMap [k ] = true
233+ }
234+ }
235+
236+ keys := make ([]string , 0 , len (keysMap ))
237+ for k := range keysMap {
238+ keys = append (keys , k )
239+ }
240+ return keys , nil
241+ }
242+
243+ // getJSONAttributes gets all attributes from a JSON body, supporting both object
244+ // and array-based syntax. For array-based JSON like [{"import": {...}}], it
245+ // extracts attribute names using encoding/json and builds a schema to extract them.
246+ func getJSONAttributes (body hcl.Body , bytes []byte ) (hcl.Attributes , hcl.Diagnostics ) {
247+ // First, try JustAttributes (works for object-based JSON)
248+ attrs , diags := body .JustAttributes ()
249+ if ! diags .HasErrors () {
250+ return attrs , nil
251+ }
252+
253+ // Extract keys using encoding/json
254+ keys , err := extractJSONKeys (bytes )
255+ if err != nil {
256+ return attrs , diags // Return original JustAttributes error
257+ }
258+
259+ // Build a schema with all discovered keys
260+ schema := & hcl.BodySchema {
261+ Attributes : make ([]hcl.AttributeSchema , len (keys )),
262+ }
263+ for i , key := range keys {
264+ schema .Attributes [i ] = hcl.AttributeSchema {Name : key }
265+ }
266+
267+ // Use PartialContent to get proper *json.expression objects
268+ content , _ , partialDiags := body .PartialContent (schema )
269+ return content .Attributes , partialDiags
270+ }
271+
208272// WalkExpressions traverses expressions in all files by the passed walker.
209273// Note that it behaves differently in native HCL syntax and JSON syntax.
210274//
@@ -236,7 +300,7 @@ func (c *GRPCClient) WalkExpressions(walker tflint.ExprWalker) hcl.Diagnostics {
236300 }
237301
238302 // In JSON syntax, everything can be walked as an attribute.
239- attrs , jsonDiags := file .Body . JustAttributes ( )
303+ attrs , jsonDiags := getJSONAttributes ( file .Body , file . Bytes )
240304 if jsonDiags .HasErrors () {
241305 diags = diags .Extend (jsonDiags )
242306 continue
0 commit comments