{"id":15010357,"url":"https://github.com/fabricate-site/adorn","last_synced_at":"2025-08-16T22:32:19.280Z","repository":{"id":246714148,"uuid":"776810269","full_name":"fabricate-site/adorn","owner":"fabricate-site","description":"Extensible conversion of Clojure code to Hiccup forms.","archived":false,"fork":false,"pushed_at":"2024-07-15T13:02:28.000Z","size":292,"stargazers_count":11,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-09T18:48:45.208Z","etag":null,"topics":["clojure","clojurescript","syntax-highlighting"],"latest_commit_sha":null,"homepage":"https://adorn.fabricate.site/","language":"Clojure","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/fabricate-site.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":"2024-03-24T14:11:33.000Z","updated_at":"2024-09-23T11:23:02.000Z","dependencies_parsed_at":"2024-10-12T10:20:44.253Z","dependency_job_id":"6e3aa7dc-1670-49af-98f9-41e8f4e92be4","html_url":"https://github.com/fabricate-site/adorn","commit_stats":null,"previous_names":["fabricate-site/adorn"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/fabricate-site/adorn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabricate-site%2Fadorn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabricate-site%2Fadorn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabricate-site%2Fadorn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabricate-site%2Fadorn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fabricate-site","download_url":"https://codeload.github.com/fabricate-site/adorn/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabricate-site%2Fadorn/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270781196,"owners_count":24643804,"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-08-16T02:00:11.002Z","response_time":91,"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":["clojure","clojurescript","syntax-highlighting"],"created_at":"2024-09-24T19:33:40.978Z","updated_at":"2025-08-16T22:32:19.242Z","avatar_url":"https://github.com/fabricate-site.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"![\u003cimg src=\"dev/logo.svg\"\u003e](https://adorn.fabricate.site/media/logo.svg)\n\n# Adorn\n## Extensible conversion of Clojure code to Hiccup forms.\nAdaptation of ideas described by Michiel Borkent in [\"Writing a Clojure Highlighter from Scratch\"](https://blog.michielborkent.nl/writing-clojure-highlighter.html). Uses [`rewrite-clj`](https://github.com/clj-commons/rewrite-clj/) to parse Clojure source code and forms, and leverages multimethods to allow for user-extensibility.\n\n[**GitHub project**](https://github.com/fabricate-site/adorn)\n\n## Rationale\nIf you want to add syntax highlighting to your project without using a JavaScript library like [Prism](https://prismjs.com/) or [Highlight.js](https://highlightjs.org/) and you use Hiccup to generate HTML, this library might help you. I wrote it in part because I wanted more control over how my source code was displayed in HTML and CSS while developing [fabricate](https://github.com/fabricate-site/fabricate) - which this code was factored out of - than was allowed by other tools. \n\nMost source code highlighters focus on syntax. Adorn goes further than this and allows you the ability to highlight elements based on their meaning.\n\nMore broadly, I don't think Clojure should have to rely on other language ecosystems for good display of our source code. I think it can be done better in Clojure, because Clojure code is Clojure data and we have powerful facilities for working with it, especially with the widespread use of the excellent rewrite-clj library.\n\n## Quickstart\n\nAdorn provides a minimal API in the `site.fabricate.adorn` namespace. Use it to produce Hiccup elements - nested `:span` vectors.\n\n```clojure\n(require\n '[site.fabricate.adorn :as adorn\n   :refer [clj-\u003ehiccup]])\n```\n\nYou can pass quoted Clojure forms:\n\n```clojure\n(clj-\u003ehiccup '[:vector {:map-key :map-val} symbol])\n```\n\nThis results in the following data structure:\n\n```clojure\n([:span\n  {:class \"language-clojure vector\"}\n  [:span {:class \"bracket-open\"} \"[\"]\n  [:span\n   {:class \"language-clojure keyword\",\n    :data-java-class \"clojure.lang.Keyword\",\n    :data-clojure-keyword \":vector\"}\n   \":vector\"]\n  [:span {:class \"language-clojure whitespace\"} \" \"]\n  [:span\n   {:class \"language-clojure map\"}\n   [:span {:class \"brace-open\"} \"{\"]\n   [:span\n    {:class \"language-clojure keyword\",\n     :data-java-class \"clojure.lang.Keyword\",\n     :data-clojure-keyword \":map-key\"}\n    \":map-key\"]\n   [:span {:class \"language-clojure whitespace\"} \" \"]\n   [:span\n    {:class \"language-clojure keyword\",\n     :data-java-class \"clojure.lang.Keyword\",\n     :data-clojure-keyword \":map-val\"}\n    \":map-val\"]\n   [:span {:class \"brace-close\"} \"}\"]]\n  [:span {:class \"language-clojure whitespace\"} \" \"]\n  [:span\n   {:class \"language-clojure symbol\",\n    :data-java-class \"clojure.lang.Symbol\",\n    :data-clojure-symbol \"symbol\"}\n   \"symbol\"]\n  [:span {:class \"bracket-close\"} \"]\"]])\n\n```\n\nWhen converted, it results in HTML like this:\n\n\u003cpre class=\"language-clojure\"\u003e\n\u003cspan class=\"language-clojure vector\"\u003e\u003cspan class=\"bracket-open\"\u003e[\u003c/span\u003e\u003cspan class=\"language-clojure keyword\" data-java-class=\"clojure.lang.Keyword\" data-clojure-keyword=\":vector\"\u003e:vector\u003c/span\u003e\u003cspan class=\"language-clojure whitespace\"\u003e \u003c/span\u003e\u003cspan class=\"language-clojure map\"\u003e\u003cspan class=\"brace-open\"\u003e{\u003c/span\u003e\u003cspan class=\"language-clojure keyword\" data-java-class=\"clojure.lang.Keyword\" data-clojure-keyword=\":map-key\"\u003e:map-key\u003c/span\u003e\u003cspan class=\"language-clojure whitespace\"\u003e \u003c/span\u003e\u003cspan class=\"language-clojure keyword\" data-java-class=\"clojure.lang.Keyword\" data-clojure-keyword=\":map-val\"\u003e:map-val\u003c/span\u003e\u003cspan class=\"brace-close\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"language-clojure whitespace\"\u003e \u003c/span\u003e\u003cspan class=\"language-clojure symbol\" data-java-class=\"clojure.lang.Symbol\" data-clojure-symbol=\"symbol\"\u003esymbol\u003c/span\u003e\u003cspan class=\"bracket-close\"\u003e]\u003c/span\u003e\u003c/span\u003e\n\u003c/pre\u003e\n\n\nIt also works on strings. A plain string will be assumed to contain one or more Clojure forms, and parsed with `rewrite-clj.parser/parse-string-all`.\n\n```clojure\n(clj-\u003ehiccup \"[:vector {:map-key :map-val} 'symbol]\")\n```\n\nAnd it works on  `rewrite-clj` nodes:\n```clojure\n(require '[rewrite-clj.parser :as p])\n\n(clj-\u003ehiccup (p/parse-string \"[:vector {:map-key :map-val} 'symbol]\"))\n\n```\n\n## Extending adorn\n\n`site.fabricate.adorn/clj-\u003ehiccup` uses the multimethod `site.fabricate.adorn/form-\u003ehiccup` to dispatch, which means it can be extended to new form types. \n\n## Form-level API\n\nFor more information, see the [API docs](https://adorn.fabricate.site/API/adorn/forms.html).\n\n## Using adorn to highlight elements\n\nI included the resulting Hiccup form in the code examples because it demonstrates an important idea mentioned above: highlighting based on semantics. Adorn produces Hiccup elements, so if you display them by converting them to HTML you have all the power of CSS to display them as you see fit.\n\nSay you want to assign a different color to definitions: `def`, `defn`, `defmulti`, `defprotocol`, and so on. Assigning different highlight rules to each of these terms is very difficult in other syntax highlighters - to the extent that it's possible at all.\n\nWhere appropriate, Adorn sets Clojure information, like the symbol in a form, as form-level [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes). So you can simply assign rules based on this attribute.\n\n```css\nspan[data-clojure-symbol^=\"def\"] {\n    font-weight: 900;\n}\n```\n\nNow everything that begins with `def` is covered - even if you forgot about a different one, like `clojure.test/deftest` - you'll be covered. \n\nI use a similar rule to highlight instances of the most important function in this intro: \u003ccode\u003e\u003cspan data-clojure-symbol=\"clj-\u003ehiccup\"\u003eclj-\u003ehiccup\u003c/span\u003e\u003c/code\u003e. I hope Adorn gives you the data you need to highlight what makes the most sense for the code you're displaying, rather than forcing you into a predetermined set of options. \n\n\n## Other libraries with overlapping aims\n- [Glow](https://github.com/venantius/glow) is another server-side syntax highlighting library for Clojure. It only runs on the JVM because it uses [ANTLR](https://www.antlr.org/) to parse Clojure. It also uses [Enlive](https://github.com/cgrand/enlive) instead of Hiccup for its intermediate representation of parsed Clojure code.\n- [Clygments](https://github.com/bfontaine/clygments) wraps the [Pygments](https://pygments.org/) Python library, which obviously means this library introduces a dependency on Python. \n\n## Status\nPre-alpha. Moving towards a stable API, but does not yet have clearly defined contracts. Not yet implemented across all target platforms, does not yet have a fully robust extension mechanism, and still requires a unified data model for \"forms.\"\n\nThe `site.fabricate.adorn.forms` namespace has a fairly complete set of functions that are used as building blocks.\n\n## Goals\n- [ ] CLJC compatibility; generation of Hiccup forms using:\n  - [x] Clojure \n  - [x] ClojureScript \n  - [ ] Babashka\n- [ ] provide sensible defaults and an example of styling using plain CSS\n  - [ ] including at least one useful Flexbox example\n- [x] provide an override mechanism for users who want to display particular forms in special ways\n  - [ ] provide a nested, map-based override mechanism for subforms in a Clojure form\n- [x] provide an extension mechanism for special symbols (e.g. `def`, `defn`, `def-my-custom-def`)\n- [ ] compatibility across Hiccup implementations\n\n## Non-goals\n- Conversion of Hiccup to HTML. While this conversion will be necessary in order to verify the output of `adorn`, this will strictly be a dev-time dependency. What generates HTML from the Hiccup produced by adorn is up to the user.\n- Validation of output HTML.\n- Support for other languages.\n\n## License\n\nLicensed under the [MIT license](https://github.com/fabricate-site/adorn/blob/main/LICENSE).\n\n# Development\n\n## Clojure\nAssuming that the Clojure CLI is installed:\n\nTo execute the project's tests:\n```bash\nclojure -X:dev:test\n```\n\n\n## ClojureScript\nAssuming that npm (globally) and shadow-cljs (locally) are installed:\n\nTo execute the project's tests:\n```bash\nnpx shadow-cljs compile test\n```\n\n\n# Acknowledgements\n\n- Thanks to [Michiel Borkent](https://github.com/borkdude) for the initial idea and excellent writeup\n- Thanks to [Lee Read](https://github.com/lread), Michiel Borkent and the other contributors to `rewrite-clj` for such an excellent library, and for answering my questions about it - this library wouldn't be possible without the well-designed machinery that `rewrite-clj` provides.\n- Thanks to John Newman for answering my novice questions about CLJS.\n- [Oleksandr Yakushev](https://github.com/alexander-yakushev) has written a plethora of invaluable tools and information that helped me measure the performance of this code in a rigorous way.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabricate-site%2Fadorn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffabricate-site%2Fadorn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabricate-site%2Fadorn/lists"}