{"id":15131494,"url":"https://github.com/jstachio/jstachio","last_synced_at":"2025-07-26T03:04:15.966Z","repository":{"id":62336331,"uuid":"551030676","full_name":"jstachio/jstachio","owner":"jstachio","description":"Java type safe statically compiled mustache","archived":false,"fork":false,"pushed_at":"2025-03-10T19:42:34.000Z","size":2735,"stargazers_count":289,"open_issues_count":31,"forks_count":11,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-12T14:55:27.667Z","etag":null,"topics":["annotation-processor","dropwizard","java","jooby","mustache","spring","template-engine","type-safe"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jstachio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-10-13T18:18:17.000Z","updated_at":"2025-04-03T10:31:59.000Z","dependencies_parsed_at":"2023-12-26T23:29:48.516Z","dependency_job_id":"8c561e81-d028-4e49-b535-47e2a246f57d","html_url":"https://github.com/jstachio/jstachio","commit_stats":null,"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jstachio%2Fjstachio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jstachio%2Fjstachio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jstachio%2Fjstachio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jstachio%2Fjstachio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jstachio","download_url":"https://codeload.github.com/jstachio/jstachio/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553958,"owners_count":22090417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotation-processor","dropwizard","java","jooby","mustache","spring","template-engine","type-safe"],"created_at":"2024-09-26T03:42:13.592Z","updated_at":"2025-05-16T15:08:33.400Z","avatar_url":"https://github.com/jstachio.png","language":"Java","funding_links":[],"categories":["项目","模板引擎","java","Projects"],"sub_categories":["模板引擎","Template Engine"],"readme":"[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.jstach/jstachio/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.jstach/jstachio)\n[![Github](https://github.com/jstachio/jstachio/actions/workflows/maven.yml/badge.svg)](https://github.com/jstachio/jstachio/actions)\n\n\u003cimg src=\"etc/social-media.svg\" alt=\"jstachio\"\u003e\n\nA type-safe Java Mustache templating engine.\n\nTemplates are compiled into readable Java source code and value bindings are statically checked.\n\n\n## Documentation\n\n* **[Latest SNAPSHOT JStachio doc](https://jstach.io/jstachio/)**\n* **[Current released JStachio doc](https://jstach.io/doc/jstachio/current/apidocs)** \n\nThe doc is also on javadoc.io but is not aggregated like the above.\nThe aggregated javadoc is the preferred documentation and the rest of this readme\nis mainly for ~~propaganda~~ marketing purposes.\n\nFor previous releases:\n\n    https://jstach.io/doc/jstachio/VERSION/apidocs\n\nWhere `VERSION` is the version you want.\n\n## Why choose JStachio\n\nCovered in [why_jstachio_is_better.md](why_jstachio_is_better.md).\n\n## Features\n\n * Logicless [Mustache (v 1.3)](https://github.com/mustache/spec) syntax.\n\n   * Full support of non-optional Mustache spec v1.3.0 requirements (including whitespace)\n   * Optional inheritance support with some caveats\n   * Optional lambda support with some differences due to static nature\n \n * Get [JEP 430](https://openjdk.org/jeps/430) like support today but wth even more power.\n \n * Templates are compiled into Java code\n\n * Value bindings are statically checked.\n\n * Methods, fields and getter-methods can be referenced in templates.\n\n * Friendly error messages with context.\n\n * Zero configuration. No plugins or tweaks are required.\n   Everything is done with standard javac with any IDE and/or build-system.\n \n * Non-HTML templates are supported. Set of supported escaping content types is extensible.\n * Layouts are supported via the Mustache inheritance spec.\n * Fallback render service extension point via ServiceLoader\n\n   * Seamlessly Fallback to reflection based runtime rendering via [JMustache](https://github.com/samskivert/jmustache) and [mustache.java](https://github.com/spullara/mustache.java) (useful for development and changing templates in real time)\n   * If you are not a fan of generated code you can still use JStachio to type check your mustache templates.\n \n * Customize allowed types that can be outputted otherwise compiler error (to avoid toString on classes that do not have a friendly toString).\n * Formatter for custom `toString` of variables at runtime\n * Add extra `implements` interfaces to generated code for trait like add ons (`@JStacheInterfaces`)\n * Powerful Lambda support\n * `Map\u003cString, ?\u003e` support\n * `Optional\u003c?\u003e` support\n * Compatible with [JMustache](https://github.com/samskivert/jmustache#-first-and--last) and [Handlebars](https://handlebarsjs.com/api-reference/data-variables.html#root) list index extensions (like `-first`, `-last`, `-index`)\n * It is by far the [fastest Java Mustache-like template engine as well one of the fastest in general](#performance).\n * Zero dependencies other than JStachio itself\n * An absolutely zero runtime dependency option is avaialable (as in all the code needed is generated and not even jstachio is needed during runtime). No need to use Maven shade for annotation processors and other zero dep projects. Also useful for Graal VM native projects for as minimal footprint as possible.\n * First class support for Spring Framework (as in the project itself will provide plugins as opposed to an aux project)\n\n## Performance\n\n**It is not a goal of this project to be the fastest java templating engine!**\n\n\u003csub\u003e\u003csup\u003e(however it is currently the fastest that I know when this readme was last updated)\u003c/sup\u003e\u003c/sub\u003e\n\nNot that peformance matters much with templating languges \n(it is rarely the bottleneck) but JStachio is very fast:\n\nhttps://github.com/agentgt/template-benchmark\n\n### String Output\n\n![Template Comparison](https://github.com/agentgt/template-benchmark/raw/utf8/results.png)\n\n### UTF-8 byte Output with extended characters\n\n![Template Comparison](https://github.com/agentgt/template-benchmark/raw/utf8/results-utf8.png)\n\n## Quick Example\n\n```java\n\n@JStache(template = \"\"\"\n        {{#people}}\n        {{message}} {{name}}! You are {{#ageInfo}}{{age}}{{/ageInfo}} years old!\n        {{#-last}}\n        That is all for now!\n        {{/-last}}\n        {{/people}}\n        \"\"\")\npublic record HelloWorld(String message, List\u003cPerson\u003e people) implements AgeLambdaSupport {\n}\n\npublic record Person(String name, LocalDate birthday) {\n}\n\npublic record AgeInfo(long age, String date) {\n}\n\npublic interface AgeLambdaSupport {\n\n    @JStacheLambda\n    default AgeInfo ageInfo(Person person) {\n        long age = ChronoUnit.YEARS.between(person.birthday(), LocalDate.now());\n        String date = person.birthday().format(DateTimeFormatter.ISO_DATE);\n        return new AgeInfo(age, date);\n    }\n\n}\n\n@Test\npublic void testPerson() throws Exception {\n    Person rick = new Person(\"Rick\", LocalDate.now().minusYears(70));\n    Person morty = new Person(\"Morty\", LocalDate.now().minusYears(14));\n    Person beth = new Person(\"Beth\", LocalDate.now().minusYears(35));\n    Person jerry = new Person(\"Jerry\", LocalDate.now().minusYears(35));\n    String actual = JStachio.render(new HelloWorld(\"Hello alien\", List.of(rick, morty, beth, jerry)));\n    String expected = \"\"\"\n            Hello alien Rick! You are 70 years old!\n            Hello alien Morty! You are 14 years old!\n            Hello alien Beth! You are 35 years old!\n            Hello alien Jerry! You are 35 years old!\n            That is all for now!\n                            \"\"\";\n    assertEquals(expected, actual);\n\n}\n```\n\n## Installation\n\n\n### Maven\n\n\n```xml\n\u003cproperties\u003e\n    \u003cio.jstach.version\u003e0.6.0-SNAPSHOT\u003c/io.jstach.version\u003e\n\u003c/properties\u003e\n...\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003eio.jstach\u003c/groupId\u003e\n        \u003cartifactId\u003ejstachio\u003c/artifactId\u003e\n        \u003cversion\u003e${io.jstach.version}\u003c/version\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n...\n\u003cbuild\u003e\n    \u003cplugins\u003e\n        \u003cplugin\u003e\n            \u003cgroupId\u003eorg.apache.maven.plugins\u003c/groupId\u003e\n            \u003cartifactId\u003emaven-compiler-plugin\u003c/artifactId\u003e\n            \u003cversion\u003e3.8.1\u003c/version\u003e\n            \u003cconfiguration\u003e\n                \u003csource\u003e17\u003c/source\u003e \u003c!-- 17 is the minimum --\u003e\n                \u003ctarget\u003e17\u003c/target\u003e \u003c!-- 17 is the minimum --\u003e\n                \u003cannotationProcessorPaths\u003e\n                    \u003cpath\u003e\n                        \u003cgroupId\u003eio.jstach\u003c/groupId\u003e\n                        \u003cartifactId\u003ejstachio-apt\u003c/artifactId\u003e\n                        \u003cversion\u003e${io.jstach.version}\u003c/version\u003e\n                    \u003c/path\u003e\n                    \u003c!-- other annotation processors --\u003e\n                \u003c/annotationProcessorPaths\u003e\n            \u003c/configuration\u003e\n        \u003c/plugin\u003e\n    \u003c/plugins\u003e\n\u003c/build\u003e\n```\n\n*N.B. The annotations jar (jstachio-annotation) is pulled in transitively*\n\n### Gradle\n\n```gradle\ndependencies {\n \n    implementation 'io.jstach:jstachio:VERSION'\n \n    annotationProcessor 'io.jstach:jstachio-apt:VERSION'\n}\n```\n\n\n\nExamples\n--------\n\n\n### user.mustache ###\n\n```hbs\n{{#name}}\n\u003cp\u003eName: {{.}}, Name Length is {{length}}\u003c/p\u003e\n{{/name}}\n\n\u003cp\u003eAge: {{  age  }}\u003c/p\u003e\n\n\u003cp\u003eAchievements:\u003c/p\u003e\n\n\u003cul\u003e\n{{#array}}\n  \u003cli\u003e{{.}}\u003c/li\u003e\n{{/array}}\n\u003c/ul\u003e\n\n{{^array}}\n\u003cp\u003eNo achievements\u003c/p\u003e\n{{/array}}\n\n\u003cp\u003eItems:\u003c/p\u003e\n\n\u003col\u003e\n{{#list1}}\n  \u003cli\u003e{{value}}\u003c/li\u003e\n{{/list1}}\n\u003c/ol\u003e\n```\n\n### User.java\n\nFollowing class can be used to provide actual data to fill into above template.\n\n```java\n@JStache(\n    // points to src/main/resources/user.mustache file\n    path = \"user.mustache\",\n   \n    // or alternatively you can inline the template\n    template = \"\", \n\n    )\npublic record User(String name, int age, String[] array, List\u003cItem\u003cString\u003e\u003e list) {\n\n   public static class Item\u003cT\u003e {\n        private final T value;\n        public Item(T value) {\n            this.value = value;\n        }\n        T value() {\n            return value;\n        }\n    }\n}\n```\n\n\n### Rendering\n\nNew class `UserRenderer` will be mechanically generated with the above code. \nThis class can be used to render template filled with actual data. To render template following code can be used:\n\n```java\nclass Main {\n    public static void main(String[] args) throws IOException {\n        User user = new User(\"John Doe\", 21, new String[] {\"Knowns nothing\"}, list);\n        StringBuilder appendable = new StringBuilder();\n        JStachio.render(user, appendable);\n    }\n}\n```\n\nThe result of running this code will be\n\n```html\n\u003cp\u003eName: John Doe, Name Length is 8\u003c/p\u003e\n\n\u003cp\u003eAge: 21\u003c/p\u003e\n\n\u003cp\u003eAchievements:\u003c/p\u003e\n\n\u003cul\u003e\n  \u003cli\u003eKnowns nothing\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eItems:\u003c/p\u003e\n\n\u003col\u003e\n  \u003cli\u003ehelmet\u003c/li\u003e\n  \u003cli\u003eshower\u003c/li\u003e\n\u003c/ol\u003e\n```\n\nReferencing non existent fields, or fields with non renderable type, all result in compile-time errors. These errors are reported at your project's compile-time alone with other possible errors in java sources.\n\n```\ntarget/classes/user.mustache:5: error: Field not found in current context: 'age1'\n  \u003cp\u003eAge: {{  age1  }} ({{birthdate}}) \u003c/p\u003e\n                  ^\n  symbol: mustache directive\n  location: mustache template\n```\n\n```\ntarget/classes/user.mustache:5: error: Unable to render field: type error: Can't render data.birthdate expression of java.util.Date type\n  \u003cp\u003eAge: {{  age  }} ({{birthdate}}) \u003c/p\u003e\n                                    ^\n  symbol: mustache directive\n  location: mustache template\n```\n\nSee `test/examples` project for more examples.\n\n\n\n## Java specific extensions\n\n### Enum matching support\n\nBasically enums have boolean keys that are the enums name (`Enum.name()`) that can be used as conditional sections.\n\nAssume `light` is an enum like:\n\n```java\npublic enum Light {\n  RED,\n  GREEN,\n  YELLOW\n}\n```\n\nYou can conditionally select on the enum like a pattern match:\n\n```hbs\n{{#light.RED}}\nSTOP\n{{/light.RED}}\n{{#light.GREEN}}\nGO\n{{/light.GREEN}}\n{{#light.YELLOW}}\nProceeed with caution\n{{/light.YELLOW}}\n```\n\n### Index support\n\nJStachio is compatible with both handlebars and JMustache index keys for iterable sections.\n\n* `-first` is boolean that is true when you are on the first item\n* `-last` is a boolean that is true when you are on the last item in the iterable\n* `-index` is a one based index. The first item would be `1` and not `0`\n\n### Lambda support\n\nJStachio supports lambda section calls in a similar manner to JMustache. Just tag your methods\nwith `@JStacheLambda` and the returned models will be used to render the contents of the lambda section.\nThe top of the context stack can be passed to the lambda.\n\nJStachio unlike the spec does not support returning dynamic templates that are then rendered against the context stack.\nHowever dynamic output can be achieved by the caller changing the contents of the lambda section as the contents of the\nsection act as an inline template.\n\n\nDesign\n------\n\nThe idea is to create templating engine combining [mustache](https://jgonggrijp.gitlab.io/wontache/mustache.5.html) logicless philosophy\nwith Java's single responsibility and static-typing.\nFull compile-time check of syntax and data-binding is the main requirement.\n\nCurrently Java-code is generated for templates. Generated Java-code should never fail to compile.\nIf it is impossible to generate valid Java-code from some template,\nfriendly compile-time error pointing to template file should be generated.\nUsers should never be exposed to generated Java-code.\n\nOriginal mustache uses Javascript-objects to define rendering context.\nFields of selected Javascript-objects are binded with template fields.\n\nStatic mustache uses Java-objects to define rendering context.\nBinding of template fields is defined and checked at compile-time.\nMissing fields are compile-time error.\n \n\nLicense\n-------\n\nJStachio is under BSD 3-clause license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjstachio%2Fjstachio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjstachio%2Fjstachio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjstachio%2Fjstachio/lists"}