{"id":21040716,"url":"https://github.com/l-briand/ktm","last_synced_at":"2025-05-15T16:33:24.368Z","repository":{"id":171623179,"uuid":"648186850","full_name":"L-Briand/KTM","owner":"L-Briand","description":"Mustache template engine in kotlin multiplatform","archived":false,"fork":false,"pushed_at":"2025-02-27T19:51:21.000Z","size":859,"stargazers_count":20,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-03T11:43:27.422Z","etag":null,"topics":["kotlin","kotlin-multiplatform","multiplatform","multiplatform-kotlin-library","mustache","template-engine","template-engine-html","template-engine-kotlin","template-processor"],"latest_commit_sha":null,"homepage":"https://mustache.github.io/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/L-Briand.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":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-01T11:57:11.000Z","updated_at":"2024-11-21T10:23:05.000Z","dependencies_parsed_at":"2023-12-07T16:26:52.544Z","dependency_job_id":"b14eeac5-ce3a-4d75-9f33-97a5c88242ff","html_url":"https://github.com/L-Briand/KTM","commit_stats":null,"previous_names":["l-briand/ktm"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L-Briand%2FKTM","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L-Briand%2FKTM/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L-Briand%2FKTM/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L-Briand%2FKTM/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/L-Briand","download_url":"https://codeload.github.com/L-Briand/KTM/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254377462,"owners_count":22061141,"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":["kotlin","kotlin-multiplatform","multiplatform","multiplatform-kotlin-library","mustache","template-engine","template-engine-html","template-engine-kotlin","template-processor"],"created_at":"2024-11-19T13:47:46.202Z","updated_at":"2025-05-15T16:33:23.974Z","avatar_url":"https://github.com/L-Briand.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KTM\n\nA [Mustache](https://mustache.github.io) implementation in pure Kotlin Multiplatform.\n\nTo see how it compares to [Mustache.java](https://github.com/spullara/mustache.java)\nlook [here](benchmark/README.MD)\n\n# Import from maven\n\n### Library\n\n\u003cdetails\u003e \n\u003csummary\u003eMultiplatform:\u003c/summary\u003e\n\n```kotlin\nrepositories {\n    mavenCentral()\n}\nval commonMain by getting {\n    dependencies {\n        implementation(\"net.orandja.ktm:core:2.0.0\")\n    }\n}\n```\n\n\u003c/details\u003e\n\nJvm:\n\n```kotlin\nrepositories {\n    mavenCentral()\n}\ndependencies {\n    implementation(\"net.orandja.ktm:core:2.0.0\")\n}\n```\n\n### Ksp code generator plugin\n\nEnable ksp plugin:\n\n```kotlin\nplugins {\n    // ...\n    id(\"com.google.devtools.ksp\") version \"1.9.22-1.0.17\" // or later\n}\n```\n\n\u003cdetails\u003e \n\u003csummary\u003eMultiplatform:\u003c/summary\u003e\n\n```kotlin\nrepositories {\n    mavenCentral()\n}\ndependencies {\n    add(\"kspJvm\", \"net.orandja.ktm:ksp:2.0.0\")\n    // add(\"kspJs\", \"net.orandja.ktm:ksp:2.0.0\")\n    // add(\"kspNative\", \"net.orandja.ktm:ksp:2.0.0\")\n    // ...\n}\n```\n\n\u003c/details\u003e\n\nJvm :\n\n```kotlin\ndependencies {\n    // ...\n    ksp(\"net.orandja.ktm:ksp:2.0.0\")\n}\n\n```\n\n\u003e [!NOTE]\n\u003e If you provide ksp arguments.\n\u003e\n\u003e ```kotlin\n\u003e ksp {\n\u003e     arg(\"ktm.auto_adapters_package\", \"com.example\")\n\u003e }\n\u003e ```\n\u003e\n\u003e This will generate the `com.example.AutoKtmAdaptersModule` with all generated\n\u003e adapters from `@KtmContext` annotated classes.\n\u003e\n\u003e Then set it as default with\n\u003e ```kotlin \n\u003e Ktm.setDefaultAdapters(AutoKtmAdaptersModule)\n\u003e ```\n\n# Examples\n\nRender a document with automatically generated adapter\n\n```kotlin\n@KtmContext\ndata class User(val firstName: String, val lastName: String) {\n    @KtmName(\"name\")\n    fun fullName() = \"$firstName $lastName\"\n}\n\nKtm.setDefaultAdapters {\n    +UserKtmAdapter\n}\n\n// or Ktm.setDefaultAdapters(AutoKtmAdaptersModule) \n// if you have setup a package name for it\n\nval document = \"Hello {{ name }}\".toMustacheDocument()\nval data = User(\"John\", \"Doe\")\n\nassert(\"Hello John Doe\" == document.render(data))\n```\n\nRender a document with custom build contexts\n\n```kotlin\nval document = \"Hello {{ name }}\".toMustacheDocument()\nval context = Ktm.ctx.make {\n    \"firstName\" by \"John\"\n    \"lastName\" by \"Doe\"\n    \"name\" by delegateValue { \"${findValue(\"firstName\")} ${findValue(\"lastName\")}\" }\n}\nassert(\"Hello John Doe\" == document.render(data))\n```\n\nCustom context can be useful when composing.\n\n```kotlin\n@KtmContext\ndata class User(val name: String)\nKtm.setDefaultAdapters { +UserKtmAdapter }\n\nval john = User(\"John\")\n\nval documents = Ktm.ctx.make {\n    \"content\" by \"Hello {{ name }}\".toMustacheDocument()\n    // If you do not transform it to a mustache document,\n    // it will be done on the fly when rendering\n    \"header\" by \"Header for {{ name }}\"\n}\n\nval template = \"{{\u003e header }}\\n\\n{{\u003e content }}\".toMustacheDocument()\n\nval context = Ktm.ctx.make {\n    like(documents)\n    like(john)\n}\n\nassert(\"Header for John\\nHello John\" == template.render(context))\n```\n\n# QuickStart\n\nMustache API is bundled into the `Ktm` object.\nYou will use it when you create documents, contexts, or use adapters.\n\nTo render a document, you need two things:\n\n## 1. A template\n\nSomething like `Hello {{ name }}`. This needs to be parsed to be used.\n\nUse the `Ktm.doc` object to read and parse the documents into usable objects.\nIf you are running kotlin with a JVM, you can use extension functions to parse files\nand IO with `.file(File)`, `.path(Path)`, `.resource(name: String)`,\n`.inputStream(InputStream)` or `.reader(Reader)`.\n\nExample:\n\n```kotlin\nval mustacheTemplate: String = \"Hello {{ name }}\"\nvar document: MDocument\n// default way\ndocument = Ktm.doc.string(mustacheTemplate)\n// quick way, only work on strings\ndocument = mustacheTemplate.toMustacheDocument()\n```\n\n## 2. A context\n\nYou generally need some kind of map `key: value` to match your mustache tags.\nTo create such map, use the `Ktm.ctx` object.\nWith it, you can create template contexts anywhere in your code.\n\nLet's assume this document:\n\n```handlebars\n{{# greeting }}\nHello {{ name }},\n{{/ greeting }}\nToday's tasks:\n{{# tasks }}\n    - {{ . }}\n{{/ tasks }}\n```\n\n### Creating context by hand\n\nYou can build a context by hand with methods in `Ktm.ctx`.\n\n```kotlin\nval context = Ktm.ctx.ctxMap(\n    \"greeting\" to Ktm.ctx.ctxMap(\n        \"name\" to Ktm.ctx.string(\"John\")\n    )\n)\n```\n\nThis method is quite cumbersome, to mitigate this, and write things more quickly you\ncan use the `make` function.\n\nThe `by` keyword can be used to associate key value pairs in the context.\nAlso, every method in `Ktm.ctx` can be used in the `make` scope.\n\n```kotlin\nval context = Ktm.ctx.make {\n    \"greeting\" by make {\n        \"name\" by \"Jon\"\n    }\n    \"tasks\" by ctxList(value(\"Eat\"), value(\"Work\"))\n}\n```\n\nIn a more simple way, you can also use kotlin's maps and list to define your data\n\n```kotlin\nval context = Ktm.ctx.make {\n    \"greeting\" by mapOf(\"name\" to \"Jon\")\n    \"tasks\" by listOf(\"Eat\", \"Work\")\n}\n```\n\nMaybe you don't have the full scope of your context, and some parts need to be\ndynamic.\n\nHere we have a function that defines the tasks without knowing `name`.\nWe then add it to the main context.\nDuring render, it will call the lambda and search for the `name` tag.\n\n```kotlin\nval tasks = Ktm.ctx.makeList {\n    +\"Call for lunch.\"\n    +delegateValue {\n        \"Welcome ${findValue(\"name\")} to the office.\"\n    }\n}\n\nval context: MContext = Ktm.ctx.make {\n    \"name\" by \"John\"\n    \"tasks\" by tasks\n}\n```\n\n### Creating context with ksp\n\nCreate a class representing your data and annotate it with `@KtmContext`.\n\n```kotlin\n@KtmContext\ndata class User(@KtmName(\"name\") val user: String)\n```\n\nThe ksp plugin will take these classes and create\nadapters (`UserKtmAdapter`, `TasksKtmAdapter`) in the same package.\nIt will create bindings for any property or function inside it.\n\nAdapters are key components for Ktm. From a given type (here `User`) it has the\nability convert it into `MContext`.\n\n```kotlin\nval context: MContext = TasksKtmAdapter.toMustacheContext(User(\"John\"))\nassert(\"John\" == \"{{ name }}\".render(context))\n```\n\nGenerally, you want to set up sets of adapters to transform all your objects\ninto context easily. Then use it when you build contexts.\n\n```kotlin\nval adapters = Ktm.adapters.make {\n    +UserKtmAdapter\n}\nval context = Ktm.ctx.make(adapters) {\n    \"content\" by User(\"John\")\n}\nassert(\"John\", \"{{ content.name }}\".render(context))\n```\n\nOr set a new default set of adapters altogether for every time you create a context.\n\n```kotlin\nKtm.setDefaultAdapters {\n    +UserKtmAdapter\n}\n// now 'Ktm.adapters' contains adapter for User.\n// and by default 'Ktm.ctx.make' use 'Ktm.adapters'\nval userContext = Ktm.adapters.contextOf(User(\"John\"))\nval contentContext = Ktm.ctx.make {\n    \"content\" by User(\"John\")\n}\nassert(\"John\" == \"{{ name }}\".render(userContext))\nassert(\"John\" == \"{{ content.name }}\".render(userContext))\n```\n\nYou can always add your own adapter on top of others.\n\n```kotlin\n@KtmContext\ndata class User(val user: String)\nKtm.setDefaultAdapters { +UserKtmAdapter }\n\nval customAdapters = Ktm.adapters.make {\n    +KtmAdapter\u003cUser\u003e { adapters, value -\u003e\n        Ktm.ctx.make {\n            \"userName\" by value.user\n        }\n    }\n}\nval context = customAdapters.contextOf(User(\"John\"))\nassert(\"John\", \"{{userName}}\".render())\n```\n\n#### KtmAdapter Modules\n\nIf you set the `ksp` argument:\n\n```kotlin\nksp {\n    arg(\"ktm.auto_adapters_package\", \"my.pkg\")\n}\n```\n\nThis will generate the `my.pkg.AutoKtmAdaptersModule` with all generated\nadapters from `@KtmContext` annotated classes. You can then set it as default with:\n\n```kotlin \nKtm.setDefaultAdapters(AutoKtmAdaptersModule)\n```\n\nExtends the class `KtmAdapterModule` to create custom modules. You can later create\nsets of adapters with them.\n\n# Deep dive\n\n- [Create documents](docs/mdocs/create_documents.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl-briand%2Fktm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fl-briand%2Fktm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl-briand%2Fktm/lists"}