Skip to content

feat: migrate template engine from Stencil to swift-jinja - #60

Merged
alexey1312 merged 11 commits into
mainfrom
feature/stencil-to-jinja
Feb 23, 2026
Merged

feat: migrate template engine from Stencil to swift-jinja#60
alexey1312 merged 11 commits into
mainfrom
feature/stencil-to-jinja

Conversation

@alexey1312

@alexey1312 alexey1312 commented Feb 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Replace Stencil + StencilSwiftKit + PathKit with swift-jinja (HuggingFace) for template rendering across all 4 export modules
  • Rename 34 templates .stencil.jinja with Jinja syntax adaptations ({% include %} → context variables, forloop.lastloop.last)
  • Rewrite 4 base exporters from Stencil Environment to renderTemplate() + loadTemplate() with recursive Any → Value conversion
  • Update ~27 call sites across 15 exporter files with contextWithHeader() / contextWithHeaderAndBundle() helpers

Dependency impact

Before After
Stencil, StencilSwiftKit, PathKit*, Komondor, PackageConfig, ShellOut, Spectre swift-jinja (depends on swift-collections — already present)

Net: −6 packages from the dependency graph

*PathKit remains as transitive dep of XcodeProj

Breaking changes for custom templates

Users with custom templatesPath templates need to:

  1. Rename .stencil.jinja
  2. Replace {% include "header.stencil" %}{{ header }}
  3. Replace forloop.lastloop.last (if used)

Test plan

  • All 138 tests pass (template outputs verified via string comparison)
  • Build succeeds on macOS
  • CI passes (Linux build + tests)

🤖 Generated with Claude Code

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @alexey1312, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request undertakes a significant refactoring of the project's templating infrastructure by replacing the Stencil engine with swift-jinja. This migration streamlines template rendering, reduces external dependencies, and modernizes the codebase. The changes involve updating template file extensions, adapting template syntax, and modifying core exporter logic to integrate with the new Jinja-based system, ultimately improving maintainability and performance.

Highlights

  • Template Engine Migration: The project's template engine has been migrated from Stencil to swift-jinja across all four export modules (Android, Flutter, Web, Xcode).
  • Template File Renaming and Syntax Adaptation: 34 template files were renamed from .stencil to .jinja and their syntax was adapted for Jinja compatibility, including changes like {% include %} to context variables and forloop.last to loop.last.
  • Exporter Logic Refactoring: Base exporter classes were refactored to utilize swift-jinja's renderTemplate() and loadTemplate() methods, incorporating recursive Any → Value conversion for context data.
  • Call Site Updates: Approximately 27 call sites across 15 exporter files were updated to use new contextWithHeader() and contextWithHeaderAndBundle() helper functions for template rendering.
  • Dependency Reduction: Project dependencies were reduced by removing Stencil, StencilSwiftKit, Komondor, PackageConfig, and ShellOut, resulting in a net reduction of 6 packages.
  • Breaking Changes for Custom Templates: Users with custom templates will need to rename .stencil files to .jinja and update their include ({% include "header.stencil" %} to {{ header }}) and loop (forloop.last to loop.last) syntax.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request successfully migrates the template engine from Stencil to swift-jinja, resulting in a significant reduction of dependencies. The migration includes renaming templates, updating syntax, and rewriting the base exporter logic. My feedback focuses on improving maintainability by reducing code duplication across the four export modules and optimizing performance in loops where templates are repeatedly parsed and rendered. I also noted some minor inconsistencies in error handling and context filtering.

Comment thread Sources/WebExport/WebIconsExporter.swift Outdated
Comment thread Sources/WebExport/WebImagesExporter.swift Outdated
Comment thread Sources/AndroidExport/AndroidExporter.swift Outdated
Comment thread Sources/AndroidExport/AndroidExporter.swift
alexey1312 added a commit that referenced this pull request Feb 22, 2026
Add renderTemplate(source:context:) overload that accepts a pre-loaded
template string. Use it in WebIconsExporter and WebImagesExporter loops
to load the template once instead of on every iteration.

Addresses review feedback from PR #60.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alexey1312
alexey1312 force-pushed the feature/stencil-to-jinja branch from a4b1f1a to ae83930 Compare February 22, 2026 10:23
alexey1312 and others added 2 commits February 22, 2026 15:23
Replace Stencil + StencilSwiftKit + PathKit with swift-jinja (HuggingFace)
for template rendering across all 4 export modules.

Changes:
- Package.swift: swap Stencil/StencilSwiftKit deps for swift-jinja
- 34 templates: .stencil → .jinja with syntax adaptations
  - {% include "header.stencil" %} → {{ header }} context variable
  - {% include "Bundle+extension..." %} → {{ bundleExtension }} pre-rendered
  - forloop.last → loop.last (Jinja standard)
  - Image/UIImage .include partials inlined into parent templates
- 4 base exporters: Stencil Environment → renderTemplate() + loadTemplate()
  with recursive Any→Value conversion
- 15 exporter files: updated all ~27 call sites
- contextWithHeader() / contextWithHeaderAndBundle() helpers for DRY injection

Net effect: -6 packages from dependency graph (Stencil, StencilSwiftKit,
PathKit*, Komondor, PackageConfig, ShellOut, Spectre → +1 swift-jinja)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add renderTemplate(source:context:) overload that accepts a pre-loaded
template string. Use it in WebIconsExporter and WebImagesExporter loops
to load the template once instead of on every iteration.

Addresses review feedback from PR #60.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alexey1312
alexey1312 force-pushed the feature/stencil-to-jinja branch from ae83930 to 19f8d7c Compare February 22, 2026 10:24
alexey1312 and others added 9 commits February 22, 2026 15:35
Eliminates ~130 lines of duplicated template loading, rendering, and
error handling code across AndroidExport, FlutterExport, WebExport,
and XcodeExport by centralizing into a shared JinjaTemplateRenderer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- testExportJSON now parses JSON to validate loop.last comma placement
- testExportJSONLightOnly verifies JSON without dark section
- testExportImageComponentContent verifies Jinja brace escaping in JSX
- testExportIconComponentContent verifies icon component structure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace `try?` with proper error discrimination: catch only
  file-not-found, propagate permission/encoding/IO errors immediately
- Fix regression: custom templatesPath now uses `try` (not `try?`)
  to propagate errors instead of silently falling back to bundle
- Guard against nil Bundle.module.resourcePath with descriptive error
- Include searched paths in TemplateLoadError for easier debugging
- Remove unnecessary context filtering in contextWithHeaderAndBundle

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Jinja renders arrays with single quotes (['a', 'b']) which is
invalid Swift. Use explicit for loop to produce double-quoted
Swift string literals: ["a", "b"].

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…m path

When templatesPath is set in PKL config, .jinja.include files (like
Bundle+extension.swift.jinja.include) were resolved only from the custom
directory. Now loadTemplate falls back to bundle resources when a file
is not found in the custom path, matching user expectations.

Also updates hk.pkl to exclude .jinja files from trailing-whitespace
and newline checks (replaces stale .stencil excludes).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…S/Android entries

Add Web platform templates section to CustomTemplates.md with context variables
and example. Document missing iOS templates (LabelStyle, CodeConnect) and Android
CodeConnect template. Add note about .jinja.include partial templates. Update
ExFig.md and Development.md to mention Web platform.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Surface real errors (permissions, I/O) from custom template path instead
of silently falling back to bundle. Add templateName parameter through
the renderer chain for better error diagnostics. Update CLAUDE.md docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alexey1312
alexey1312 merged commit d4b1775 into main Feb 23, 2026
9 checks passed
@alexey1312
alexey1312 deleted the feature/stencil-to-jinja branch February 23, 2026 07:30
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.

1 participant