Skip to content

Conversation

@hs-lsong
Copy link
Collaborator

@hs-lsong hs-lsong commented Jan 5, 2026

A new execution mode for Jinjava that preserves unknown/undefined variables as their original template syntax instead of rendering them as empty strings. This enables multi-pass rendering scenarios where templates are processed in stages with different variable contexts available at each stage.

See preserve-undefined-execution-mode.md for details.

hs-lsong and others added 10 commits December 18, 2025 14:49
…ss rendering

Introduces a new execution mode that preserves unknown/undefined variables
as their original template syntax instead of rendering them as empty strings.
This enables multi-pass rendering scenarios where templates are processed
in stages with different variable contexts available at each stage.

Key behaviors:
- Undefined expressions preserved: {{ unknown }} → {{ unknown }}
- Defined expressions evaluated: {{ name }} with {name: "World"} → World
- Control structures (if/for) with undefined conditions preserved
- Variables explicitly set to null are also preserved

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Replace contains() assertions with isEqualTo() to make test expectations
explicit and show the exact preserved output format.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Instead of relying solely on eager mode's internal representations,
use a hybrid approach:
- Extend EagerExecutionMode to preserve control structures (if/for/set tags)
- Add PreserveUndefinedExpressionStrategy to preserve original expression
  syntax like {{ name | upper }} instead of {{ filter:upper.filter(...) }}

This gives the best of both worlds: tags with undefined variables are
preserved, and expressions maintain their readable original format.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Enable deferred execution mode to ensure set tags are preserved even
when the RHS is fully resolved. This is needed for multi-pass rendering
where variables need to remain defined in subsequent render passes.

Example: {% set x = name %} with {name: "World"}
Before: World (x would be undefined in next pass)
After: {% set x = 'World' %}World (x remains defined)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…ervation

Add a new context configuration flag that allows set tags with fully
resolved RHS values to be preserved in output, independent of macro
deferral behavior. This enables both:
- Macros to partially evaluate (preserving only undefined vars within)
- Set tags to be preserved as {% set x = 'value' %} for multi-pass rendering

Also adds import/from macro tests to verify partial macro evaluation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Document the new execution mode including:
- Use case and purpose
- Usage examples
- Behavior tables for expressions, control structures, set tags, macros
- Multi-pass rendering example
- Implementation details and new context flags

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Multi-pass rendering scenarios may need to preserve Jinjava comment tags
({# comment #}) for later processing stages. This adds a context flag
that, when enabled, outputs comments as-is instead of stripping them.

PreserveUndefinedExecutionMode now enables this flag by default.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
The static instance() method intentionally hides the parent class method
to return the correct singleton for this execution mode, matching the
pattern used in NonRevertingEagerExecutionMode.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
When extends tag path is undefined in PreserveUndefinedExecutionMode,
block tags were being processed normally causing their content to be
extracted without the block wrapper. This fix adds an isExtendsDeferred
flag that gets set when ExtendsTag throws DeferredValueException, allowing
BlockTag to also defer and preserve its original syntax.

This enables proper multi-pass rendering where templates with undefined
extends paths preserve both the extends and block tags for later resolution.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Verifies that when a grandchild template extends a child template
(resolved path), and the child extends a parent (undefined path),
the child's extends and block tags are correctly preserved.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
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