Mustache Template Validation Support
November 2, 2025 · View on GitHub
This extension now supports validating JSON files that contain Mustache template syntax!
File Extension Requirement
Important: Mustache template validation only works with files ending in .json.mustache
- ✅
config.json.mustache- Will be processed - ✅
template.json.mustache- Will be processed - ❌
config.json- Will NOT be processed (even if it contains{{...}}) - ❌
template.mustache- Will NOT be processed (must end with.json.mustache)
Overview
The validator automatically processes .json.mustache files before validation:
- Detection: Checks if file ends with
.json.mustache - Cleaning: Replaces Mustache placeholders with appropriate sample values
- Validation: Runs schema validation on the cleaned JSON
- Error Mapping: Maps validation errors back to the original file locations
Supported Mustache Syntax
The validator handles the following Mustache constructs:
- Variables:
{{variableName}} - Conditionals:
{{#if condition}}...{{/if}} - Loops:
{{#each items}}...{{/each}} - Negation:
{{^condition}}...{{/condition}} - Comments:
{{! This is a comment }} - Partials:
{{> partialName}}
Smart Type Inference
The mustache processor intelligently infers appropriate replacement values based on:
Variable Names
- Boolean-like:
{{isEnabled}},{{hasAccess}},{{visible}}→true - Number-like:
{{count}},{{id}},{{index}},{{age}}→0 - Array-like:
{{items}},{{list}}→[] - Object-like:
{{config}},{{settings}},{{data}}→{} - String-like:
{{name}},{{title}},{{text}}→"sampleString"
Context Analysis
The processor also considers the surrounding context:
{
"name": "{{userName}}", // → "sampleString" (after quote)
"count": {{itemCount}}, // → 0 (number context)
"enabled": {{isEnabled}}, // → true (boolean-like name)
"items": [{{item}}], // → "sampleString" (array context)
"config": {{configObject}} // → {} (object-like name)
}
Example: Before and After Processing
Original File (with Mustache)
{
"componentName": "{{name}}",
"version": "{{version}}",
"enabled": {{isEnabled}},
"maxItems": {{maxCount}},
"items": [
"{{item1}}",
"{{item2}}"
],
"{{#if showAdvanced}}": {
"advanced": true
}
}
Processed for Validation
{
"componentName": "sampleString",
"version": "sampleString",
"enabled": true,
"maxItems": 0,
"items": [
"sampleString",
"sampleString"
],
"": {
"advanced": true
}
}
Position Mapping
The validator maintains accurate position mappings between the original and cleaned versions:
- Error locations point to the exact mustache expression or text in the original file
- Quick fixes work correctly with mustache templates
- Diagnostics are displayed at the correct positions in your editor
Debug Output
When validating a .json.mustache file, the output channel shows:
🎭 [DEBUG] Mustache file detected (.json.mustache) - preprocessing...
🎭 [DEBUG] Mustache preprocessing complete:
- Found 7 mustache expressions
- Original length: 342
- Cleaned length: 298
- Cleaned sample: {...}
If the file doesn't end with .json.mustache, mustache expressions will be treated as invalid JSON syntax.
How It Works
1. Detection Phase
const isMustacheFile = doc.uri.fsPath.endsWith('.json.mustache');
if (isMustacheFile && hasMustacheSyntax(raw)) {
// Process mustache template
}
2. Processing Phase
The processMustacheTemplate() function:
- Scans for all
{{...}}expressions using regex - Generates appropriate replacement values
- Maintains position mappings for each segment
3. Validation Phase
- Validates the cleaned JSON against the schema
- Uses AJV validation on the processed content
4. Error Mapping Phase
- Converts error positions from cleaned → original
- Maps offsets back through the position mappings
- Displays errors at correct locations in your file
Technical Details
Position Mapping Structure
interface PositionMapping {
originalStart: number; // Start position in original file
originalEnd: number; // End position in original file
cleanedStart: number; // Start position in cleaned file
cleanedEnd: number; // End position in cleaned file
type: 'mustache' | 'text'; // Type of segment
replacement?: string; // Replacement value (for mustache)
}
Mapping Algorithm
When an error occurs at position X in the cleaned file:
- Find the mapping segment containing position X
- If it's a
textsegment: map directly (offset preserved) - If it's a
mustachesegment: map to the start of the original mustache expression - Convert offset → Position in the original document
How It Works
Simple Quote Detection
The processor uses a simple rule:
- Inside quotes (odd number of
"after last:or[): Replace with unquotedsampleValue - Outside quotes (even number of
"): Replace with quoted"sampleValue"
Examples:
"name": "{{userName}}" → "name": "sampleValue" ✅
"count": {{itemCount}} → "count": "sampleValue" ✅
"enabled": {{isEnabled}} → "enabled": "sampleValue" ✅
"items": ["{{item}}"] → "items": ["sampleValue"] ✅
Block Helpers
{{#each items}}...{{/each}}→ Block tags removed, content kept{{#if condition}}...{{/if}}→ Block tags removed, content kept{{^unless}}...{{/unless}}→ Block tags removed, content kept
This means loop/conditional content is validated once (not per iteration).
Limitations
1. File Extension Required
Only files ending with .json.mustache are processed.
2. Complex Nested Conditionals
Nested conditions with multiple branches may produce invalid JSON:
// ❌ This pattern doesn't work well:
"label":
{{#isComingSoon}}
{{#isPreOrder}}
"preorder coming soon"
{{/isPreOrder}}
{{^isPreOrder}}
"coming soon"
{{/isPreOrder}}
{{/isComingSoon}}
After processing, ALL branches remain, creating invalid JSON. Workaround: Simplify to single-level conditions.
3. No Type Inference
The processor doesn't infer types - all values become "sampleValue" (string) or sampleValue (unquoted). Numbers, booleans, objects, and arrays are all replaced with strings.
4. Block Content Not Repeated
Loops like {{#items}}...{{/items}} keep only ONE instance of their content, not multiple iterations.
Configuration
No additional configuration is needed! The mustache support is automatic:
- ✅ Automatically processes
.json.mustachefiles - ✅ Works with existing schema validation
- ✅ Compatible with all validation features
- ✅ No performance impact on regular
.jsonfiles
How to Use
- Create or rename your mustache template file with
.json.mustacheextension - The validator will automatically detect and process it
- Edit the file as usual - validation happens automatically on save and while typing
Examples
Example 1: API Configuration
{
"apiUrl": "{{API_URL}}",
"apiKey": "{{API_KEY}}",
"timeout": {{TIMEOUT}},
"retries": {{MAX_RETRIES}},
"enabled": {{FEATURE_ENABLED}}
}
Example 2: Conditional Features
{
"features": {
"{{#if enableBeta}}": {
"beta": true
},
"{{#if enableAnalytics}}": {
"analytics": {
"trackingId": "{{TRACKING_ID}}"
}
}
}
}
Example 3: Dynamic Arrays
{
"users": [
{{#each users}}
{
"name": "{{name}}",
"email": "{{email}}"
}
{{/each}}
]
}
Troubleshooting
Issue: Validation fails after mustache processing
Solution: Check the output channel to see the cleaned JSON. The mustache replacement might have created invalid JSON structure.
Issue: Errors point to wrong locations
Solution: This is rare, but can happen with very complex mustache structures. The position mapping tries to point to the start of the mustache expression.
Issue: Type inference is incorrect
Solution: The inference is based on variable names and context. Consider adjusting your mustache variable names to match common patterns (e.g., isEnabled for booleans, itemCount for numbers).
Future Enhancements
Potential improvements for future versions:
- Custom type hints in comments:
{{variableName}} // @type number - User-configurable type inference rules
- Support for custom mustache helpers
- Better handling of complex nested structures
- Mustache expression validation (check if variables exist)
Implementation Files
The mustache support is implemented in:
src/mustacheProcessor.ts: Core mustache processing logicsrc/validation.ts: Integration with validation pipeline
Contributing
If you find issues with mustache validation or have suggestions:
- Check the output channel for debug information
- Note the specific mustache pattern causing issues
- Report the issue with a minimal example
Note: This feature is designed to be transparent and automatic. If your JSON doesn't contain mustache syntax, validation works exactly as before with zero overhead.