Skip to content

feat(viewer): inline HCL/Terraform syntax highlighting#11

Open
vnz wants to merge 1 commit intoCSCSoftware:masterfrom
vnz:feat/hcl-syntax-highlighting
Open

feat(viewer): inline HCL/Terraform syntax highlighting#11
vnz wants to merge 1 commit intoCSCSoftware:masterfrom
vnz:feat/hcl-syntax-highlighting

Conversation

@vnz
Copy link
Copy Markdown
Contributor

@vnz vnz commented Apr 27, 2026

Summary

Adds HCL/Terraform syntax highlighting in the Viewer. Previously .tf/.tfvars/.hcl files rendered as plaintext because highlight.js's CDN bundle doesn't include an HCL grammar module.

Approach

Register a minimal inline HCL grammar in the viewer's HTML head, right after the highlight.js script tag. No new dependency — the grammar is ~50 lines of JavaScript that uses highlight.js's existing APIs (hljs.registerLanguage, hljs.HASH_COMMENT_MODE, hljs.NUMBER_MODE, etc.).

The grammar covers:

Feature Coverage
Block keywords resource, variable, module, output, locals, data, provider, terraform, backend, provisioner, connection, lifecycle, dynamic, etc.
Meta-arguments count, for_each, depends_on, providers, source, version
Type constraints string, number, bool, list, map, set, object, tuple, any, optional
Literals true, false, null
Built-in functions ~80 common ones (lookup, merge, file, jsonencode, length, etc.) — get the built_in highlight class
Comments #, //, /* */
Strings Double-quoted with ${var.foo} interpolation as subst class
Heredoc strings <<EOF and <<-EOF indented form
Numbers Standard highlight.js NUMBER_MODE
Function calls name( matched as title.function class

Also flips the viewer's extension map for .tf/.tfvars/.hcl from 'plaintext' back to 'hcl' now that the language is registered.

Why inline instead of a dependency

  • highlight.js loads from a CDN already; adding another CDN dependency increases page weight and adds a failure mode if the second CDN is down
  • npm packages like @taga3s/highlightjs-terraform would need to be bundled into the viewer build and served — that's a build-system change for ~50 lines of grammar
  • The grammar is small and stable (HCL syntax doesn't churn)
  • Easy to extend: just add a keyword to the appropriate string

Notes on regex escaping

The viewer's HTML is generated via a TypeScript template literal. Each \ in a runtime regex needs \\\\ in the source (template literal eats one layer, JS string parser eats another). All regex patterns are written as strings ('\\\\$\\\\{') rather than regex literals (/\\$\\{/) to avoid template-literal quirks with \$ and \{ (template literals interpret \$ as escape for interpolation).

Test plan

  • Open the viewer and browse a .tf file — verify keywords like resource, variable, module are colored
  • Verify ${var.environment} interpolation is highlighted as a substitution
  • Verify heredoc strings render correctly
  • Verify hash comments (# ...) are styled as comments
  • Verify function calls like templatefile(...), lookup(...) get the function class

🤖 Generated with Claude Code

The viewer loads highlight.js from CDN, which doesn't bundle an HCL
language module. Files with `.tf`, `.tfvars`, `.hcl` extensions previously
fell back to plaintext rendering.

Register a minimal HCL grammar inline (in the page's <script>) covering:
- Block keywords: resource, variable, module, output, locals, data,
  provider, terraform, etc.
- Meta-arguments: count, for_each, depends_on, etc.
- Type constraints: string, number, bool, list, map, etc.
- Built-in functions: lookup, merge, file, jsonencode, length, etc.
- Hash, line, and block comments
- Strings with `${...}` interpolation
- Heredoc strings (<<EOF and <<-EOF)
- Numbers
- Function call recognition for the title.function class

Switches the viewer's extension map for `.tf`/`.tfvars`/`.hcl` from
'plaintext' back to 'hcl' now that the language is registered.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant