Why There Isn't a Generic Hcl Formatter
Sometimes, people ask why there isn’t a generic formatter for HCL (HashiCorp Configuration Language). The short answer is that HCL was designed as a framework for building languages, not as a standalone language, so it’s up to each application to define how formatting should work. Tools like Terraform and Packer include their own formatters, which extend basic HCL conventions with domain-specific rules. This ensures formatting aligns not only with general HCL syntax but also with the specific idiomatic patterns and best practices of the application.
For example, an application might reorder attributes, adjust indentation, or enforce conventions for expressions and relationships between blocks. These details go beyond generic formatting, reflecting the unique ways each tool uses HCL to model its domain.
Applications that provide their own formatters typically start with generic HCL formatting logic and layer on additional rules to handle their specific use cases. This lets them produce configurations that feel natural within their ecosystem, rather than forcing users into a one-size-fits-all approach.
In our own applications, we use the hclwrite package to parse HCL into a hybrid syntax tree. This allows for precise, targeted edits—whether reordering blocks or rewriting expressions into an idiomatic form. The result is then serialized back into HCL, ensuring the output is clean and consistent.
Bear in mind that any normalization process must be idempotent, meaning running the formatter multiple times on the same input should always produce the same result. If it doesn’t, treat it as a bug that needs to be addressed.
“Isn’t it all just HCL?”
@apparentlymart explains this far better than I ever could:
Unlike some other formats like JSON and YAML, a HCL file is more like a program to be executed than a data structure to be parsed, and so there’s considerably more application-level interpretation to be done than you might be accustomed to with other grammars.
HCL is designed as a toolkit for building languages rather than as a language in its own right, but it’s true that a bunch of the existing HCL-based languages aren’t doing that much above what HCL itself offers, aside from defining their expected block types and attributes.
The languages that allow for e.g. creating relationships between declared objects via expressions, or writing “libraries” like Terraform’s modules, will tend to bend HCL in more complicated ways than where HCL is being used mainly just as a serialization of a flat data structure. To be specific, I would expect the Terraform Language, the Packer Language and the Waypoint Language to all eventually benefit from application-specific extensions with their own formatters, but something like Vault’s policy language or Consul’s agent configuration files would probably suffice with a generic HCL extension and generic formatter.
hclfmt
Above all, if you’re looking to better understand how an HCL formatter works, I created a reference implementation called hclfmt, which you can find on GitHub. It’s a simple implementation that should give you a good starting point for building your own.