I designed and built Rockplate as a compact, schema-aware language for creating controlled templates such as receipts, invoices, emails, SMS messages, and structured documents.
What began as a practical solution for generating receipt and invoice templates grew into an open-source language ecosystem with its own parser, linter, documentation, CodeMirror integration, language server, and Visual Studio Code extension.
TypeScript · Language Design · Parsing · Open Source · Developer Tools
Zero runtime dependencies Approximately 5 KB minified and gzipped 100% reported test coverage at release VS Code, CodeMirror, and language-server tooling
Why I built Rockplate
Template languages generally fall into two camps. Simple interpolation systems handle basic variable substitution but lack conditions, repetition, or structure. General-purpose embedded languages give templates full programming power — which is too much for documents that should only control presentation, not computation.
I needed something in between for generating receipt, invoice and email templates in client work. The templates followed predictable data structures — order references, line items, totals, dates — and needed to conditionally display sections, iterate over line items, and reference nested properties. They did not need to execute arbitrary code, query databases, or perform complex calculations.
Rockplate was designed for this specific niche: structured documents where the data model is known ahead of time and the template controls presentation within clearly defined boundaries.
My role
I designed the language syntax, defined the schema model, built the Builder and Parser, created the linter, wrote the documentation, contributed to the CodeMirror and Visual Studio Code tooling, and released the project as open source.
Designing a deliberately limited language
Rockplate’s syntax is deliberately minimal. All expressions are enclosed in square brackets and use natural-language keywords:
- Identifiers —
[order ref]prints a value from the data context - Conditionals —
[if order is paid] ... [else] ... [end if] - Iteration —
[repeat items] ... [end repeat] - Comments —
[-- this is a comment --]
The data model supports booleans (for conditionals with is / is not / are / are not operators), identifiers (strings and numbers) and arrays (for repeat blocks). Nested properties use dot notation — [order customer name].
This limitation was a design decision, not a shortcoming. By restricting what templates could do, the language made templates predictable, testable, and safe to use in applications where non-technical staff might edit them.
Technical architecture
Rockplate processes a template through a compact build-and-render pipeline:
Template source + optional schema
↓
Builder — scans expressions and constructs a nested block structure
↓
Schema-aware validation and scope resolution
↓
Parser — interprets the block structure using the supplied data
↓
Rendered output string
The linter reuses the same block and schema model to detect syntax errors, unavailable identifiers, invalid conditions, incorrect array expressions, and schema mismatches.
Builder
The Builder scans the template source, detects language expressions, and constructs a nested block structure. It tracks the boundaries of conditional and repetition blocks, resolves nested relationships, and associates each block with its position in the source and its schema scope.
The built block structure is retained by the Rockplate instance and can be reused for subsequent parses, avoiding repeated processing of the same template.
Parser
The Parser walks the block structure, resolves identifiers from the data context, evaluates conditions, repeats array blocks, removes comments, and produces the final rendered string.
Block types
The block system includes Block (base), IfBlock (conditionals with optional else branches), RepeatBlock (array iteration), LiteralBlock (static text), and CommentBlock (ignored content).
Linter
The linter reuses the same parsing and schema model to report syntax problems, unavailable identifiers, invalid conditions, misuse of arrays or scalar values, and schema mismatches — all with source positions.
Schema-aware validation
Schemas define the expected data structure — which properties exist, their types (identifier, boolean, array) and their nesting. During building, the schema is used to validate that the template only references properties that exist in the data model.
The schema also powers the editor tooling. The Visual Studio Code extension uses the schema to provide auto-completion for property names, diagnostics for unavailable identifiers, and hover information for recognised properties.
Developer experience
Rockplate shipped as a language with its own tooling ecosystem:
- Built-in linter — validates Rockplate syntax and reports errors with line numbers and descriptive messages, integrated into the VSCode extension and available as a standalone API
- VSCode extension — provides syntax highlighting, IntelliSense auto-completion based on the schema, linting and snippet support for
.rpland.rphtmlfiles - CodeMirror support — language mode for browser-based editors
- Language Server Protocol — provides editor assistance through a language server
- TextMate grammar — syntax definition for code editors
- External schema support — schemas can be embedded in the template file or loaded from an external JSON file, with synchronous and asynchronous schema resolver APIs
- API documentation — generated API docs hosted alongside the language documentation
The tooling was part of the language design, not an afterthought. A language without good editor support is difficult to adopt, regardless of its technical merits.
Quality and reliability
The released project reported 100% test coverage, with tests and coverage reporting integrated into the project’s continuous-integration workflow.
With no runtime dependencies, the core library avoided transitive production packages and kept the runtime surface small — approximately 5 KB minified and gzipped.
The language was used in production for receipt and invoice generation before being open-sourced, providing real-world validation of the design.
What I learned
Rockplate was my most complete language-design project. Building a programming language — even a small domain-specific one — requires thinking through syntax, semantics, parsing strategy, error reporting, tooling and documentation in a way that application development does not.
The tooling — VSCode extension, linter, documentation site, language server — was as important as the language itself. A language without good tooling is difficult to adopt regardless of its technical merits.
The character-by-character scanning approach made the parsing behaviour predictable and easier to reason about. I chose this deliberately to avoid the complexity and potential edge cases of regular-expression-based parsing.
Project status
Rockplate is presented here as a completed open-source language project and a case study in language design, schema-aware parsing, developer tooling, and open-source engineering. The project reflects the TypeScript and tooling ecosystem of its release period.
Links
- Rockplate website — documentation, examples and interactive demo
- GitHub repository — source code, issues and releases
- VS Code extension — language support for Visual Studio Code