Skip to content

fix: resolve Templater lazily so its syntax is processed in templates - #497

Merged
danielo515 merged 1 commit into
masterfrom
claude/obsidian-modal-form-411-nok4tq
Aug 8, 2026
Merged

danielo515 merged 1 commit into
masterfrom
claude/obsidian-modal-form-411-nok4tq

Conversation

@danielo515

@danielo515 danielo515 commented Aug 8, 2026 •

Copy link
Copy Markdown
Owner

Closes #411

The bug

getTemplateService was called exactly once, from onload:

this.templateService = getTemplateService(this.app, logger);

and it decides which service to use by reading:

const templaterApi = app.plugins.plugins["templater-obsidian"]?.templater;

Obsidian enables plugins in the order they appear in community-plugins.json, and Templater only assigns this.templater = new Templater(this) partway through its own onload. Whenever Modal Form loads before Templater, that lookup finds nothing and we bind BasicTemplateService for the entire session.

BasicTemplateService writes the note content verbatim and its replaceVariablesInFile is a literal no-op, so templater syntax is left untouched in:

  • Insert template: <form> and Insert form template (the replaceVariablesInFile call after saving does nothing)
  • Create note from template: <form> and Create new note from a form (vault.create with the raw content)

Because it depends only on plugin ordering, it reproduces for some vaults and not others. This repo's own EXAMPLE_VAULT/.obsidian/community-plugins.json has modalforms listed before templater-obsidian, which is the broken order.

Why it isn't a template syntax error on the user's side

If Templater had run and failed to parse, create_new_note_from_template deletes the created note and returns undefined, which TemplaterService already detects and turns into the retry form. Reporters instead get a note containing the raw <% ... %> text and no retry form, including for trivially valid snippets like <% "---" %> — Templater was never invoked.

The fix

Resolve the service at the moment it is used instead of at load time, via a new makeTemplateServiceResolver. It re-checks for Templater while only the basic fallback is available, and caches the service once Templater is actually there (Templater cannot vanish without a plugin reload). main.ts keeps a templateService getter, so every existing call site is unchanged.

Testing

  • New src/core/template/getTemplateService.test.ts covers both services being selected correctly, the resolver picking up Templater when it loads after the resolver was created, and the caching behaviour.
  • npm run build and npm run test pass locally (230 tests).

Not verified against a real Obsidian vault — the diagnosis is derived from the plugin load-order behaviour and Templater's source.

Noted but not changed

setImmediate(this.templateService.replaceVariablesInFile(file)) (src/main.ts, both insert commands) discards the Left and relies on setImmediate, which does not exist on Obsidian mobile. Worth a separate fix.

The template service was resolved once, during our onload, by reading
`app.plugins.plugins["templater-obsidian"]?.templater`. Obsidian enables
plugins in the order they appear in `community-plugins.json`, and Templater
only exposes that API partway through its own onload, so whenever we happen
to load first the lookup found nothing and we silently fell back to the
basic template service for the whole session.

The basic service creates notes with the content untouched and its
`replaceVariablesInFile` is a no-op, so templater syntax was left
unprocessed in both the "Insert template" and "Create note from template"
commands, for the whole session, depending only on plugin load order.

Resolve the service on every access instead, caching it once Templater is
actually there.

Closes #411
@greptile-apps

greptile-apps Bot commented Aug 8, 2026

Copy link
Copy Markdown

Greptile Summary

The PR replaces eager template-service selection with a lazy resolver so Modal Form can discover Templater after plugin startup ordering initially makes its API unavailable.

  • Adds a resolver that rechecks availability until Templater is found, then caches the Templater-backed service.
  • Routes existing template-service access through a getter without changing command call sites.
  • Adds tests for fallback selection, delayed Templater discovery, and caching.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete blocking or independently actionable non-blocking issues identified.

The lazy resolver preserves fallback behavior while allowing subsequent template operations to discover and cache Templater after its API becomes available.

Important Files Changed

Filename Overview
src/core/template/getTemplateService.ts Adds lazy service resolution that repeatedly checks for Templater and caches it once available; no actionable defect was established.
src/main.ts Replaces the eagerly assigned service field with a getter backed by the lazy resolver while preserving existing call sites.
src/core/template/getTemplateService.test.ts Covers basic and Templater selection, delayed API availability, and successful-service caching.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Template operation accesses templateService] --> B{Templater service cached?}
    B -->|Yes| C[Return cached TemplaterService]
    B -->|No| D{Templater API currently available?}
    D -->|Yes| E[Create and cache TemplaterService]
    D -->|No| F[Return BasicTemplateService without caching]
    E --> G[Execute template operation]
    C --> G
    F --> G
Loading

Reviews (1): Last reviewed commit: "fix: resolve Templater lazily so its syn..." | Re-trigger Greptile

@danielo515
danielo515 merged commit d40edaf into master Aug 8, 2026
2 checks passed
@danielo515
danielo515 deleted the claude/obsidian-modal-form-411-nok4tq branch August 8, 2026 09:54
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.

[BUG] Inserting a modal form template does not resolve templater syntax.

1 participant