{"id":16014687,"url":"https://github.com/green-coder/vrac","last_synced_at":"2025-12-12T01:17:34.562Z","repository":{"id":145686964,"uuid":"216054751","full_name":"green-coder/vrac","owner":"green-coder","description":"[WIP] Declarative html template library from the future.","archived":false,"fork":false,"pushed_at":"2024-01-08T06:48:20.000Z","size":125,"stargazers_count":67,"open_issues_count":0,"forks_count":1,"subscribers_count":7,"default_branch":"diy-furry","last_synced_at":"2024-10-15T15:17:43.958Z","etag":null,"topics":["clojure","clojurescript","dsl","front-end","library","vrac","wip-do-not-use"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/green-coder.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}},"created_at":"2019-10-18T15:29:17.000Z","updated_at":"2024-05-31T07:51:00.000Z","dependencies_parsed_at":"2024-01-02T09:37:21.038Z","dependency_job_id":null,"html_url":"https://github.com/green-coder/vrac","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/green-coder%2Fvrac","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/green-coder%2Fvrac/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/green-coder%2Fvrac/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/green-coder%2Fvrac/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/green-coder","download_url":"https://codeload.github.com/green-coder/vrac/tar.gz/refs/heads/diy-furry","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221661149,"owners_count":16859489,"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":["clojure","clojurescript","dsl","front-end","library","vrac","wip-do-not-use"],"created_at":"2024-10-08T15:04:47.036Z","updated_at":"2025-12-12T01:17:34.513Z","avatar_url":"https://github.com/green-coder.png","language":"Clojure","readme":"# Vrac\n\n\u003e Declarative html template library from the future.\n\n## Status\n\nVrac is still an early work in progress. It is not ready to be used yet.\n\nThe following documentation is \"from the future\", the library does not work in that way yet.\n\n## Introduction\n\nVrac is a Clojure(Script) library for creating front end applications in a truly\ndeclarative and expressive way.\n\nVrac components are not render functions, they are mostly a data structure which\ncaptures how data should be manipulated before being mixed with HTML patterns.\n\nVrac components resemble a Clojure functions which output Hiccup, but keep in mind that:\n- there is a `(quote ...)` around the render expression that keeps it as data,\n- their content is not really Clojure expressions. Instead, it is a small\n  Domain Specific Language designed to look familiar to Clojure programmers\n  and easy to learn.\n\nThe directing idea of Vrac is to let the user say what he wants to display in the\nbrowser in a declarative way, instead of asking the user to write a rendering process.\nThe DSL of the components was designed to capture as much of the user's intent as possible,\nso that automated systems have sufficient information to turn it into a working web app.\n\nIn this way, the hardest part of the work (the mechanical plumbing) just have to be done once,\nby the implementors of those automated systems.\nThe user can then focus on the business logic and appearance of his web app.\n\n## Usage\n\nVrac is made of a collection of namespaces which provide functionalities for\nmany different aspects of a front end application. While mostly decoupled and optional,\nthose namespaces are made to play well together and complement each other.\n\nThere are namespaces for:\n- describing and validating Vrac components using a DSL defined by a spec,\n- traversing, manipulating and combining the templates of the Vrac components,\n- deriving EQL queries from Vrac components,\n- maintaining a normalized local database,\n- handling events, local database reducers and other effects,\n- deriving a flow graph of derived data from Vrac components,\n- implementing cascading reactivity in the flow graph,\n- deriving renderers from Vrac components,\n- deriving translation resources from Vrac components,\n- interoperability with 3rd party renderers, like React.\n\n### The components\n\nThis is what a Vrac component looks like:\n\n```clojure\n(ns my-app.core\n  (:require [vrac.core :as v :refer [defc]]))\n\n(defc user-profile-component [user]\n\n  ;; Optional meta data.\n  {:id :my-app/user-profile}\n\n  ;; The template, wrapped into (quote ...) by the defc macro.\n  [:.user-profile\n   [:img.picture {:src (:user/picture-url user)}]\n   [:.name \"Name: \" (:user/name user)]\n   [:.bio \"Bio: \" (:user/bio user)]])\n```\n\nNotes:\n- The `:id` value for a component is how it can be referred to from other components.\n  It has to be a namespaced keyword.\n- `:.user-profile` is a shortcut for `:div.user-profile` in Hiccup, a `div` html node with the class `user-profile`.\n- `(:user/picture-url user)` represents the usage of the field `:user/picture-url` on\n  the data passed to the component under the key `:user`.\n\nThis last point is **very important** because this data construct gives\nthe system the information of which fields are required to display this component.\n\n### The DSL's directive\n\nAdditionally to documenting the data usage, the DSL in the components supports those directives:\n- `if` and `when` allow to specify conditional rendering,\n- `for` allows to specify rendering on elements of collection,\n- `let` allows you to specify local names to refer to different data parts.\n\nExample:\n\n```clojure\n(let [blog-url (:user/blog-url user)\n      friends (:user/friends user)]\n  [:div (if blog-url\n          \"No blog\"\n          [:a {:href blog-url} \"Link to my blog\"])\n        [:div \"My Clojurian friends:\"\n              [:ul (for [friend friends]\n                     (when (:user/clojurian? friend)\n                       [:li (:user/name friend)]))]]])\n```\n\nThose directives (and the fact that we read them as data) provides a lot of semantic\ninformation about the intent of the user.\nNot only the system knows what data is required, now it knows **in which situations**\nit is required.\n\nFrom that kind of template, Vrac can derive an EQL query representing the data required\nby a tree of components in any given state. We can then download that data from a server\nif it is not already available locally.\n\nVrac can also help the user understanding his own source code by providing answers\nto questions like:\n- Which components use a given data?\n- In which state do I need to be to have the component `:my-ns/my-component` displayed?\n- Why do I have a given component's instance displayed?\n- Where is in the local database the data displayed at a specific place in the web page?\n\n### Components composition\n\n\u003e Imagine there's no countries\u003cbr\u003e\n\u003e It isn't hard to do\u003cbr\u003e\n\u003e Nothing to kill or die for\u003cbr\u003e\n\u003e And no religion too\u003cbr\u003e\n\u003e Imagine all the people living life in peace, you\u003cbr\u003e\n\u003e\u003cbr\u003e\n\u003e You may say I'm a dreamer\u003cbr\u003e\n\u003e But I'm not the only one\u003cbr\u003e\n\u003e I hope some day you'll join us\u003cbr\u003e\n\u003e And the world will be as one\u003cbr\u003e\n\u003e\n\u003e -- John Lennon\n\nIn Vrac, the only purpose of components is to be reusable by the user.\nTheir boundaries is up to the user and are not meant to be used has hint\nfor improving performances during the rendering process.\n\nComponents which refer to each other are conceptually \"inlined\" one into\nanother, and the values passed as parameter are also inlined.\n\nThey are treated the same as macros.\n\nVrac provides functions which rename conflicting names during the component\nexpansion, so that the user does not have to worry about it.\n\nExample of component composition:\n\n```clojure\n(defc todo-item-comp [todo-item]\n  {:id :my-app/todo-item}\n  [:.title {:class {:done? (:todo-item/done? todo-item)}}\n           (:todo-item/title todo-item)])\n\n(defc todo-list-comp [todo-list]\n  {:id :my-app/todo-list}\n  [:div [:h3 (:todo-list/title todo-list)]\n        [:ul (for [item (:todo-list/items todo-list)]\n                    ; component reference and param passing.\n               [:li [:my-app/todo-item {:todo-item item}]])]])\n```\n\nThe expanded template would look like:\n\n```clojure\n[:div [:h3 (:todo-list/title todo-list)]\n      [:ul (for [item (:todo-list/items todo-list)]\n             [:li [:.title {:class {:done? (:todo-item/done? item)}}\n                           (:todo-item/title item)])\n```\n\n### Data-flow graph and derived data\n\nSometimes the data in the local database needs some processing before being\ndisplayed on a web page.\nVrac allows the user to specify those data processings by describing\nClojure functions calls directly inside a component's template.\n\nHowever since the functions' implementation are not readable, the user\nneeds to annotation in the metadata the fields which are used in the function,\nto make sure that Vrac still have the full picture of any component's requirements.\n\nExample:\n\n```clojure\n(defn {^:eql [:todo-item/done?]} only-done-items\n  [items]\n  (into [] (filter :todo-item/done?) items))\n\n(defc todo-list-comp [todo-list]\n  {:id :my-app/todo-list}\n  [:div [:ul (for [done-item (only-done-items (:todo-list/items todo-list))]\n               [:li [:my-app/todo-item {:todo-item done-item}]])]\n        [:div (clojure.core/count (only-done-items (:todo-list/items todo-list)))\n              \" item(s) done\"])\n```\n\nAlternatively, derived data can also be calculated on demand by using some specific\nkeys which are resolved dynamically. Both approach are equivalent, but using a\nfunction call allows more flexibility on specifying the process' inputs.\n\n```clojure\n(defc todo-list-comp [todo-list]\n  {:id :my-app/todo-list}\n  [:div [:ul (for [done-item (:todo-list/done-items todo-list))]\n               [:li [:my-app/todo-item {:todo-item done-item}]])]\n        [:div (:todo-list/done-items-count todo-list)))\n              \" item(s) done\"])\n```\n\n**Important note:** It does not matter if the same process on the same data\nis repeated multiple times in the components. The user only have to make sure\nthat the processing is correct. The system, once feed with this kind of information,\ncan make sure all by itself that the data is processed without redundancies,\nand updated only when necessary.\n\nSimilarly, prop drilling in Vrac is a **non-issue** in terms of performances,\nas the semantic of rendering is decoupled from the process of rendering.\n\n### Custom semantic in templates\n\nVrac's template format is open, it can incorporate custom kinds of information\nwhich can be interpreted as the user wish.\n\nFor instance, expressions to be translated could be specified in this way:\n\n```clojure\n(defc my-page-component []\n  [:.menu\n    [:.item (translation \"Homepage\")])\n    [:.item (translation \"News (%1)\" (:news/count nil)})])\n```\n\nThe key is to customize how the template is read, and act accordingly.\n\n\u003e \"It's just data.\"\n\nIn the case of translations, because it is additional information which when\nfeed into the system, Vrac would then be able to answer these questions:\n- What are my translations in my whole app?\n- If I want to visualize a given translation, which component should I display?\n- What should be the state of the local database so see it?\n- What resources do I need to have translated in order to display a given\n  page without missing text?\n\n\n### No technology lock in\n\nThe way Vrac components are designed ensures that they do not depend on any specific implementation.\n\nAs of today:\n- Vrac uses data queries in the [EQL](https://edn-query-language.org) format,\n- the app's data is fetched by the [Pathom](https://github.com/wilkerlucio/pathom) library,\n- and the html is rendered via [React](https://reactjs.org/).\n\nHowever, the Vrac components are not limited or strongly coupled to those libraries.\n\n## Getting started\n\nThere is no release on Clojars at the moment. To use it via Clojure Deps:\n\n```clojure\n{:deps {vrac {:git/url \"https://github.com/green-coder/vrac.git\"\n              :sha \"\u003ccommit-sha\u003e\"}}}\n```\n\nAlternatively, you can use the [Vrac template](https://github.com/green-coder/vrac-template)\nto generate a Shadow-CLJS project to get started more quickly.\n\n## See also\n\n- You can read the [Vrac samples](https://github.com/green-coder/vrac-samples),\n  they are updated each time new Vrac features are developed.\n\n## License\n\nThe Vrac library is developed by Vincent Cantin.\nIt is distributed under the terms of the Eclipse Public License version 2.0.\n","funding_links":[],"categories":["Clojure"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreen-coder%2Fvrac","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgreen-coder%2Fvrac","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreen-coder%2Fvrac/lists"}