Skip to content

fix(markdown-template): preserve parent scope in conditional and optional blocks - #683

Merged
mttrbrts merged 1 commit into
mainfrom
claude/fix-conditional-optional-scope
Oct 11, 2026
Merged

mttrbrts merged 1 commit into
mainfrom
claude/fix-conditional-optional-scope

Conversation

@mttrbrts

Copy link
Copy Markdown
Member

Ports the fix from #647 by @yashhzd to the TypeScript TypeVisitor.ts introduced in #677. #647 can't merge now because it edits the removed src/TypeVisitor.js and lib/TypeVisitor.js. The commit credits @yashhzd as co-author.

Fixes accordproject/template-playground#2 (Cannot use variables inside an #if block)

Problem

TypeVisitor set the scope of an {{#if}} body, and of a primitive {{#optional}} body, to the guarded field itself. A field has no properties, so any named variable inside the block failed type-checking:

{{#if age}}You are {{age}}{{/if}}           -> Unknown property: age
{{#if age}}Hi {{name}}{{/if}}               -> Unknown property: name
{{#optional age}}Hi {{name}}{{/optional}}   -> Unknown property: name

The {{else}} branches (whenFalse and whenNone) were visited with model: null, so every variable in them failed as well.

Changes

All changes are in packages/markdown-template/src/TypeVisitor.ts.

  • ConditionalDefinition: both branches keep the parent model.
  • OptionalDefinition:
    • A primitive optional keeps the parent model in both branches.
    • A complex optional still scopes whenSome to the nested class.
  • {{this}}: a new thisProperty parameter carries the guarded field. {{this}} inside {{#if age}} or {{#optional age}} therefore still resolves to Integer, as it does today.

One difference from #647: #647 dropped the {{this}} handling for #if, and this PR keeps it.

Tests

New Jest cases in src/TemplateMarkTransformer.test.ts cover:

  • named and sibling variables in #if, #optional and their {{else}} branches
  • unknown variables being rejected in each of those positions
  • element types of variables inside #if
  • {{this}} in #if and in a primitive #optional
  • a complex #optional still scoping to the nested class

Results:

  • With the TypeVisitor.ts change reverted, 8 of the new tests fail.
  • markdown-template passes: 26 tests.
  • markdown-transform passes: 31 tests.
  • tsc --noEmit is clean.
  • ESLint reports no errors; its warnings were already there.

🤖 Generated with Claude Code

…onal blocks

Variables inside {{#if}} and primitive {{#optional}} blocks were resolved
against the guarded property instead of the template model, so templates
such as `{{#if age}}You are {{age}}{{/if}}` failed with "Unknown property".
The {{else}} branches had no model at all.

Both branches now keep the parent scope. {{this}} still resolves to the
guarded property, and complex optionals still scope to the nested class.

Ported to TypeVisitor.ts from #647.

Co-authored-by: Yash Goel <162511050+yashhzd@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Matt Roberts <code@rbrts.uk>
@mttrbrts
mttrbrts merged commit 6a41107 into main Oct 11, 2026
7 checks passed
@mttrbrts
mttrbrts deleted the claude/fix-conditional-optional-scope branch October 11, 2026 10:51
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 38133185014

Coverage increased (+3.9%) to 82.866%

Details

  • Coverage increased (+3.9%) from the base build.
  • Patch coverage: 3 of 3 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 2411
Covered Lines: 2041
Line Coverage: 84.65%
Relevant Branches: 869
Covered Branches: 677
Branch Coverage: 77.91%
Branches in Coverage %: Yes
Coverage Strength: 1108.25 hits per line

💛 - Coveralls

mttrbrts added a commit that referenced this pull request Oct 11, 2026
property.isPrimitive() is false for enum fields, so {{#optional billingFrequency}}
on an enum property still scoped its body to the enum declaration and
rejected {{billingFrequency}}, {{this}} and sibling variables. Treat enum optionals
as scalars, like primitives.

Follow-up to #683, raised in review of #647.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Matt Roberts <code@rbrts.uk>
mttrbrts added a commit that referenced this pull request Oct 11, 2026
property.isPrimitive() is false for enum fields, so {{#optional billingFrequency}}
on an enum property still scoped its body to the enum declaration and
rejected {{billingFrequency}}, {{this}} and sibling variables. Treat enum optionals
as scalars, like primitives.

Follow-up to #683, raised in review of #647.

Signed-off-by: Matt Roberts <code@rbrts.uk>
Co-authored-by: Claude Opus 5.5 <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.

Cannot use variables inside an #if block

2 participants