{"id":20389312,"url":"https://github.com/positiondev/larceny","last_synced_at":"2025-07-29T20:35:25.370Z","repository":{"id":142264387,"uuid":"56939165","full_name":"positiondev/larceny","owner":"positiondev","description":"An HTML5 templating language.","archived":false,"fork":false,"pushed_at":"2023-09-21T21:56:33.000Z","size":783,"stargazers_count":3,"open_issues_count":14,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-12T10:55:48.669Z","etag":null,"topics":["haskell","html","templating-language"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/positiondev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null}},"created_at":"2016-04-23T20:21:16.000Z","updated_at":"2021-08-04T15:24:28.000Z","dependencies_parsed_at":null,"dependency_job_id":"791ae46a-4bbd-43d5-b084-9142b55e0c4d","html_url":"https://github.com/positiondev/larceny","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/positiondev/larceny","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/positiondev%2Flarceny","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/positiondev%2Flarceny/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/positiondev%2Flarceny/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/positiondev%2Flarceny/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/positiondev","download_url":"https://codeload.github.com/positiondev/larceny/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/positiondev%2Flarceny/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267754985,"owners_count":24139438,"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","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["haskell","html","templating-language"],"created_at":"2024-11-15T03:17:19.105Z","updated_at":"2025-07-29T20:35:25.362Z","avatar_url":"https://github.com/positiondev.png","language":"HTML","readme":"# Larceny\n\nLarceny is Haskell HTML templating based on [Heist](heist).\n\nWith Larceny, you write templates that look like this:\n\n```\n\u003cbind tag=\\\"sport\\\"\u003e\n  Roller Derby\n\u003c/bind\u003e\n\u003ch1\u003e\n  \u003cname/\u003e \u003csport/\u003e\n\u003c/h1\u003e\n\u003cul\u003e\n  \u003cskaters\u003e\n    \u003cli\u003e\n      \u003ch2\u003e\u003cname/\u003e\u003c/h2\u003e\n      \u003cp\u003e\u003cposition/\u003e\u003c/p\u003e\n      \u003cp\u003e\u003cbio length=\"30\" text=\"${longBio}\"/\u003e\u003c/p\u003e\n      \u003cp\u003e\u003ca href=\"skater/${id}/\"\u003eRead more!\u003c/a\u003e\u003c/p\u003e\n    \u003c/li\u003e\n  \u003c/skaters\u003e\n\u003c/ul\u003e\n```\n\nAnd \"substitutions\" that look like this:\n\n```\nteamPageSubs :: Substitutions ()\nteamPageSubs =\n  subs [ (\"name\", textFill \"Gotham Girls\")\n       , (\"skaters\", mapSubs\n                     (\\(i,n,p,b) -\u003e\n                       subs [ (\"id\", textFill i)\n                            , (\"name\", textFill n)\n                            , (\"position\", textFill p)\n                            , (\"longBio\", textFill b)])\n                    [ (\"1\", \"Bonnie Thunders\", \"jammer\", longBio)\n                    , (\"2\", \"Donna Matrix\", \"blocker\", longBio)\n                    , (\"3\", \"V-Diva\", \"jammer\", longBio) ] )\n       , (\"bio\", useAttrs (a\"length\" %\n                           a\"text\")\n                           bioFill))\n  where longBio = \"Some example bio that is really long!\"\n        bioFill maybeNumber fullBio = textFill $\n          case maybeNumber of\n            Just numChars -\u003e T.take numChars fullBio \u003c\u003e \"...\"\n            Nothing -\u003e fullBio\n```\n\nYou end up with HTML like this:\n\n```\n\u003ch1\u003e\n  Gotham Girls Roller Derby\n\u003c/h1\u003e\n\u003cul\u003e\n  \u003cli\u003e\n    \u003ch2\u003eBonnie Thunders\u003c/h2\u003e\n    \u003cp\u003ejammer\u003c/p\u003e\n    \u003cp\u003eSome example bio that is rea...\u003c/p\u003e\n    \u003cp\u003e\u003ca href=\"skaters/1/\"\u003eRead more!\u003c/a\u003e\u003c/p\u003e\n  \u003c/li\u003e\n  \u003cli\u003e\n    \u003ch2\u003eDonna Matrix\u003c/h2\u003e\n    \u003cp\u003eblocker\u003c/p\u003e\n    \u003cp\u003eSome example bio that is rea...\u003c/p\u003e\n    \u003cp\u003e\u003ca href=\"skaters/2/\"\u003eRead more!\u003c/a\u003e\u003c/p\u003e\n  \u003c/li\u003e\n  \u003cli\u003e\n    \u003ch2\u003eV-Diva\u003c/h2\u003e\n    \u003cp\u003ejammer\u003c/p\u003e\n    \u003cp\u003eSome example bio that is rea...\u003c/p\u003e\n    \u003cp\u003e\u003ca href=\"skaters/3/\"\u003eRead more!\u003c/a\u003e\u003c/p\u003e\n  \u003c/li\u003e\n```\n\n## Why another templating language? Why not Heist?\n\nPosition Dev loves Heist templates!\n\nBut then we needed unescaped HTML in our templates... so we had to\nuse Compiled Heist. Compiled Heist is really hard to undestand.\n\nWe wrote Larceny as an alternative to compiled Heist that is easier to\nunderstand and use (if slower).\n\n## Differences from Heist\n\nThe Haskell code you write to fill in your templates is very different\nfrom Heist, but we tried to make the templates themselves as similar\nas possible, with a couple notable exceptions:\n\nLarceny is different from Interpreted Heist (but similar to Compiled\nHeist) in that, by default, it doesn't escape any text. The `textFill`\nhelper function which you'll most commonly use does escape t, but\nif you write your own more complicated Fills, you'll need to remember\nto escape the text yourself.\n\nIn Heist, `\u003cbind\u003e`s inside of nested template application can be used\nin the outer templates. We found that confusing, so Larceny doesn't\nallow that.\n\n# Troubleshooting\n\n## Locale/`hGetContents`\n\nIf your app lives in an environment like Alpine Linux or other\n`locale`-less environment, you may run into this error when templates\nare loaded:\n\n```\nyour_app: some_template.tpl: hGetContents: invalid argument (invalid byte sequence)\n```\n\nThis probably means that your template has UTF-8 characters in it, but there's no locale set.\n\nYou can remedy this by using\n[`setLocaleEncoding`](https://hackage.haskell.org/package/base-4.9.0.0/docs/GHC-IO-Encoding.html#v:setLocaleEncoding)\nfrom GHC.IO.Encoding, along with an encoding like\n[`utf8`](https://hackage.haskell.org/package/base-4.9.0.0/docs/GHC-IO-Encoding.html#v:utf8).\n\nAn example would be:\n\n```\ninitializeApp :: IO AppCtxt\ninitializeApp = do\n  setLocaleEncoding utf8\n  templates \u003c- loadTemplates \"templates\" defaultOverrides\n  ...\n```\n\n# Development Tips\n\nUse `stack ghci --ghci-options -isrc --ghci-options -itest larceny:test` to\nstart ghci with the ability to reload tests when you change the library code.\n\nCI runs `stack test --pedantic` on branches, which will fail if there are any\nerrors. Run `stack test --pedantic` before pushing.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpositiondev%2Flarceny","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpositiondev%2Flarceny","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpositiondev%2Flarceny/lists"}