Skip to content

Conversation

@cerupcat
Copy link

@cerupcat cerupcat commented Oct 21, 2025

Integrate Inja Template Engine

Overview

This PR adds support for the Inja template engine as an alternative to Mustache, providing more powerful templating capabilities for code generation while maintaining full backward compatibility with existing Mustache templates.

Motivation

Mustache templating is limited in terms of logic and requires the data source to provide granular details (e.g., array size counts, whether a property exists, last element detection). Inja is a modern C++ templating language that provides more control flow and expressions without requiring the data source to be overly detailed.

Key Features

1. Dual Template Engine Support

  • Added -e, --engine flag to choose between mustache (default) and inja
  • Full backward compatibility - existing Mustache templates continue to work
  • New Inja template engine with advanced features:
    • Conditional logic: {% if condition %}
    • Loops with context: {% for item in items %} with loop.is_first, loop.is_last
    • Built-in functions: length(), upper(), lower(), etc.
    • Template includes: {% include "partial.inja" %}

2. Enhanced Metadata & Relationships

  • Added relationship information to track:
    • Default artboard, state machine, and view model chains
    • Whether elements are defaults
    • Original file names for proper naming
  • Improved null safety with comprehensive null pointer checks throughout the codebase

3. Private Element Filtering

  • New --ignore-private flag to exclude elements based on naming conventions:
    • Elements starting with _ (underscore)
    • Elements starting with internal (case-insensitive)
    • Elements starting with private (case-insensitive)
  • Applies to artboards, animations, state machines, view models, and properties

4. Modular Swift Templates

  • Example modular Swift template structure using Inja's {% include %} feature
  • Demonstrates separation of concerns with partials for:
    • Header documentation
    • Imports
    • Metadata structs
    • Properties and enums
    • Initializers
    • Type-safe switching extensions

5. Template Organization

  • Reorganized templates into subdirectories:
    • templates/mustache/ - Mustache templates
    • templates/inja/ - Inja templates
    • templates/custom/Swift/ - Modular Swift template example

Changes Summary

37 files changed, 32,265 insertions(+), 63 deletions(-)

Core Changes

  • src/main.cpp (+726 lines):
    • Integrated Inja template engine alongside Mustache
    • Added TemplateEngine enum and rendering logic
    • Enhanced metadata with relationship tracking
    • Added shouldIncludeElement() for private filtering
    • Comprehensive null pointer safety improvements

Template Infrastructure

  • external/inja.hpp (+3,075 lines): Inja template engine library
  • external/nlohmann/json.hpp (+25,677 lines): JSON library for Inja

Templates & Examples

  • templates/inja/: New Inja versions of dart, json, and viewmodel templates
  • templates/custom/Swift/: 15 modular Swift template files demonstrating advanced Inja features
  • templates/mustache/: Moved existing Mustache templates to subdirectory

Testing & Documentation

  • test/: Added 4 new tests for Inja templates (7 total tests, all passing)
  • examples/example.sh: Updated with Inja examples
  • README.md: Comprehensive documentation of new features

Usage Examples

Basic Inja Template Usage

# Generate code using Inja template
./rive_code_generator -i samples/rating.riv -o output.swift \
  -t templates/custom/Swift/swift_example.inja -e inja

With Private Filtering

# Exclude private elements
./rive_code_generator -i samples/ -o output.dart \
  -t templates/inja/dart_template.inja -e inja --ignore-private

Modular Templates with Includes

The Swift templates demonstrate using includes for modular organization:

{% for file in riv_files %}
{% include "templates/custom/Swift/_header.inja" %}
{% include "templates/custom/Swift/_imports.inja" %}

// MARK: - {{ file.riv_pascal_case }} ViewModel
{% if length(file.view_models) > 0 %}
{% include "templates/custom/Swift/_viewmodel_with_databinding.inja" %}
{% else %}
{% include "templates/custom/Swift/_viewmodel_no_databinding.inja" %}
{% endif %}
{% endfor %}

Testing

All 7 tests pass:

  • ✅ Help text generation
  • ✅ JSON output (Mustache)
  • ✅ Dart output (Mustache)
  • ✅ Swift output (Inja) - all files
  • ✅ Swift output (Inja) - single file
  • ✅ JSON output (Inja)
  • ✅ Dart output (Inja)

Backward Compatibility

  • ✅ Existing Mustache templates work unchanged
  • ✅ Default behavior remains Mustache
  • ✅ No breaking changes to existing functionality

Additional Improvements

  • Fixed null pointer crashes in view model property access
  • Fixed null pointer crashes in enum property handling
  • Fixed null pointer crashes in nested artboard instances
  • Added premake5 binary for easier builds
  • Updated rive-runtime submodule

Note: This implementation maintains full compatibility with upstream while adding powerful new templating capabilities that don't require data source modifications.

@cerupcat cerupcat changed the title Wn/codegen Integrate Inja Code Generation Oct 23, 2025
@cerupcat cerupcat changed the base branch from add-data-binding to main October 30, 2025 21:05
Seth Sandler added 5 commits November 7, 2025 13:15
…nV3.riv. The root cause was missing null pointer checks in three places:

1. Nested ViewModel Property Access (main.cpp:691-693)
Problem: Chained calls viewModel->createInstance()->propertyViewModel() without null checks
Fix: Added null checks for createInstance(), propertyViewModel(), instance(), and viewModel()
2. Enum Property Access (main.cpp:730-736)
Problem: Chained calls accessing enum properties without null checks
Fix: Added null checks for createViewModelInstance(), propertyValue(), and all subsequent property accesses
3. Default Value Property Access (main.cpp:786-818)
Problem: Accessing property values without null checks for boolean, number, string, and color types
Fix: Added null checks for each property value access
4. Nested Artboard Instance Access (main.cpp:525)
Problem: nested->artboardInstance() could return null causing crash during recursion
Fix: Added null check before recursively calling getNestedTextValueRunPathsFromArtboard()
5. Default Artboard Instance Access (main.cpp:882)
Problem: defaultArtboard->instance()->defaultStateMachine() without null check on instance()
Fix: Added null check for instance() before accessing defaultStateMachine()
Resolved conflicts:
- build/build.sh: Kept template paths with mustache/ subdirectory structure
- src/main.cpp: Kept Inja integration and metadata features
- Removed duplicate templates/viewmodel_template.mustache (we have templates/mustache/viewmodel_template.mustache)
@cerupcat cerupcat marked this pull request as ready for review January 6, 2026 00:47
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.

2 participants