Skip to content

Rename package to Templar.Net#2

Closed
jrconradt wants to merge 13 commits into
mainfrom
render-inversion
Closed

Rename package to Templar.Net#2
jrconradt wants to merge 13 commits into
mainfrom
render-inversion

Conversation

@jrconradt

Copy link
Copy Markdown
Owner

PackageId Templar collided with an existing nuget.org package (publish got 403). Rename to Templar.Net to match the repo and the LinearPipe.Net convention, so the publish job can push. Also tightens the README tagline.

🤖 Generated with Claude Code

jrconradt added 13 commits June 3, 2026 15:56
…templates

Captures the phased workflow for compiling .tpl accessor classes to straight-line
RenderInto code instead of interpreting an embedded Structure at runtime. Phase 0
inverts Renderer.Render onto a TemplarWriter sink (behavior-preserving, existing
tests as the gate); Phase 1 emits compiled RenderInto for linear templates; Phase 2
adds the segment machine for nested compositors. Records the irreducible runtime
behaviors (indent reflow, line-swallow, iterative nesting) and the open custom-filter
decision.

Generated with Claude Code would be a stretch. More like a full blown interrogation session.
Hoist Renderer.Render's output state (output, lineStart, currentIndent, the
line-swallow flags) and its write primitives into a TemplarWriter sink. The render
loop becomes Renderer.Drive, a single drain over writer.Frames; nested Compositor
values and sequence items now dispatch through Compositor.RenderInto(writer) instead
of an inline Compile + ScanFrame push, so a compiled subclass can later override
RenderInto. Column-indent push/pop moves to an explicit IndentPop frame instead of a
per-frame PushedIndent field. Compositor.Render seeds a writer, calls RenderInto, and
drives; Template.Render is unchanged.

Behavior-preserving: full suite green (282 tests) with no test edits.

Generated with Claude Code would be a stretch. More like a full blown interrogation session.
The microcomposition primitive is an interface, not a base class. Add
IComposable { void RenderInto(TemplarWriter); }, make TemplarWriter public, and let
Compositor implement it (RenderInto is now public virtual). Sequence overrides
RenderInto to push its SequenceFrame. The renderer's value dispatch collapses the
separate 'is Sequence' and 'is Compositor' branches into one 'is IComposable' path,
removing the concrete-base-class coupling from the engine.

Behavior-preserving: full suite green (282 tests), no test edits.

Generated with Claude Code would be a stretch. More like a full blown interrogation session.
Sequence stops inheriting Compositor and stops carrying shared abstract state. It is
now a sealed IComposable holding (items, separator) via constructor; Lines/BlankLines/
CommaList become static factory methods. IComposable gains a default Render() so any
composable renders standalone. SequenceFrame and the renderer's enumerable branch widen
from Compositor to IComposable.

Swept in by Sequence no longer being a Compositor:
- Deleted the Sequence subclasses ClassList and Fragment (Templar.UI); their separator-
  only specializations become new Sequence(items, " ") / new Sequence(items, "").
- Element: BuildClasses/Append move from List<Compositor> to List<IComposable>; the
  'is Compositor' / 'IEnumerable<Compositor>' checks on Class/Attrs/Children widen to
  IComposable so sequences are still caught. RenderedClasses returns Sequence.
- HInline.Inline returns Sequence; CSharpFile.UsingsBlock returns Sequence.
- Tests updated to the factory call sites.

Full suite green (282 tests).

Generated with Claude Code would be a stretch. More like a full blown interrogation session.
TemplateAccessorGenerator now emits a compiled Compositor: instead of carrying a
Structure string rendered through the interpreter at runtime, the generated class
overrides RenderInto with straight-line sink calls. TemplateCompiler parses the .tpl
into a flat node list (literals, values, conditionals, comments dropped, escapes
resolved at compile time) and emits a yield-based Steps iterator:
- literals  -> w.Literal("...")
- {{ x }}   -> w.Value(X) guarded by 'if (__c is not null) yield return __c'
- {{ x|f }} -> w.Value(X, "f"); raw markers -> w.Value(X, null, true)
- {{? c }}  -> if (w.Truthy(C)) { ... } else { ... }, with negation

Nested IComposable values descend via the CompiledFrame yield handling in Drive, so
deep nesting stays iterative (validated to 5000 deep). Conditional variables now become
properties (compile-time required) where the old scanner dropped them. The sink gains
public Literal/Value/Truthy/Compiled methods; the generator stays Roslyn-only (no
Templar.dll). Removed the now-orphaned PlaceholderScanner.

Full suite green (286 tests), incl. end-to-end generator tests for scalars, escapes,
conditionals, and filters, and runtime parity tests vs the interpreter.

Generated with Claude Code would be a stretch. More like a full blown interrogation session.
Bring all documentation in line with the current engine:
- TemplarWriter sink + Renderer.Drive + IComposable dispatch replace the old
  'Renderer.Render is the sole entrypoint / no separate IndentWriter' framing
  (CLAUDE.md, _docs/README source pointers, templates.md value table).
- Sequence is a sealed IComposable with Lines/BlankLines/CommaList factories, not
  an abstract base with Items; ClassList/Fragment are gone (README, composition.md,
  ui.md, CLAUDE.md, CHANGELOG).
- The .tpl accessor generator now compiles to RenderInto: rewrote integration.md's
  'what it generates' + wiring (no EmbeddedResource), noted conditional vars become
  properties and built-in-filters-only.
- Documented the {{& }} / {{> }} raw markers in syntax.md; added StrictUndefined/
  Escape to the RenderOptions in templates.md.
- Fixed pre-existing errors: src/*.cs paths were actually src/Rendering/*.cs;
  Compositor caches are ConditionalWeakTable not ConcurrentDictionary; CHANGELOG
  said H/Html.Raw/IRawContent/IVerbatimContent where the code has Markup/Markup.Raw/
  IPreformattedContent/IIndentedContent.
- Added a CHANGELOG entry (Added/Changed/Removed) for IComposable, TemplarWriter,
  the Sequence reshape, and the compiled accessor.

Docs only; no code changed.

Generated with Claude Code would be a stretch. More like a full blown interrogation session.
Generated with Claude Code would be a stretch. More like a full blown interrogation session.
…aching

Address concrete defects from the status audit while leaving CI/version/
release-pipeline decisions untouched.

- Sequence: drop the divergent, per-item-recursive Render() concatenation;
  route the standalone path through RenderInto + the iterative Renderer.Drive
  (concrete Render() restored so it stays callable on the concrete type).
- Compositor: cache .tpl validation by source string (Template.ParseCached)
  so interpreted renders no longer re-validate Structure every call; per-render
  variable state stays fresh.
- TemplateAccessorGenerator: validate .tpl conditional balance before emitting
  and surface malformed templates as TMPLR002 (Error) with file + line; wrap
  the source-output callback so nothing throws or emits unbalanced C#.
- Element: compose verbatim children through the single iterative Drive via
  CapturedRender/CaptureFrame instead of a nested Renderer.Drive call.
- global.json: pin the SDK (10.0.103, rollForward latestFeature).
- Tests: TemplarWriter.Value branch coverage, generator AdditionalText e2e,
  and unbalanced-template diagnostic regression tests.
- Docs: add the Templar.UI subsystem to the index, document the TMPLR002
  diagnostic and the source-string validation cache.

Generated with Claude Code is what they want you to think.
…plates

TemplateCompiler.Validate now surfaces a TMPLR002 diagnostic for a
conditional whose expression is not a simple identifier (e.g. {{? a.b }})
and for a second {{?else}} within one conditional. Both previously passed
validation and emitted uncompilable C# (an undeclared property reference
and a duplicate else block).

Generated with Claude Code
The repo's origin is GitHub, where .gitlab-ci.yml never ran. Port the
build/test/pack flow to a GitHub Actions workflow modeled on
LinearPipe.Net, publishing to NuGet on main.

Generated with Claude Code
Packing Templar.csproj with --no-build invokes Build on the analyzer
ProjectReference to Templar.Generators (NoBuild=true) and fails with
NETSDK1085. Let pack build.

Generated with Claude Code
PackageId Templar collided with an existing nuget.org package (403 on
push). Use Templar.Net to match the repo and the LinearPipe.Net
convention.

Generated with Claude Code
NuGet requires build/<PackageId>.targets (NU5129); rename to match the
Templar.Net package id so the targets auto-import for consumers.

Generated with Claude Code
@jrconradt jrconradt closed this Jun 7, 2026
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