├── .gitignore ├── html ├── assets │ ├── css │ │ └── screen.css │ ├── img │ │ └── kit.png │ ├── error.html │ ├── home.html │ └── src │ │ ├── pages.clj │ │ └── layout.clj └── config.edn ├── sql ├── assets │ └── queries.sql └── config.edn ├── tailwind ├── assets │ ├── input.css │ ├── tailwind.config.js │ ├── package.json │ └── README.tailwind.md └── config.edn ├── dominoui-postgres ├── assets │ ├── migrations │ │ ├── 20240104143537-user-info.down.sql │ │ └── 20240104143537-user-info.up.sql │ ├── src │ │ ├── user.clj │ │ ├── home.clj │ │ └── domino.clj │ └── sql │ │ └── queries.sql └── config.edn ├── cljs ├── assets │ ├── package.json │ ├── package.uix.json │ ├── src │ │ ├── core.cljs │ │ └── core.uix.cljs │ ├── shadow-cljs.edn │ └── build.clj └── config.edn ├── dominoui ├── assets │ └── src │ │ ├── core.clj │ │ ├── ws.clj │ │ ├── disp │ │ └── home.clj │ │ ├── htmx.clj │ │ ├── home.clj │ │ ├── ui.clj │ │ └── domino.clj └── config.edn ├── devcontainer ├── assets │ ├── compose │ │ ├── docker-compose.yml │ │ └── devcontainer.json │ ├── Dockerfile.dev │ └── docker │ │ └── devcontainer.json └── config.edn ├── auth ├── config.edn └── assets │ └── src │ └── auth.clj ├── datomic ├── assets │ └── src │ │ └── datomic.clj └── config.edn ├── codox └── config.edn ├── htmx ├── assets │ └── src │ │ ├── htmx.clj │ │ └── ui.clj └── config.edn ├── simpleui ├── assets │ └── src │ │ ├── hello.clj │ │ ├── htmx.clj │ │ └── ui.clj └── config.edn ├── nrepl └── config.edn ├── hato └── config.edn ├── LICENSE ├── metrics └── config.edn ├── sente ├── assets │ ├── ws.cljs │ └── ws.clj └── config.edn ├── modules.edn └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /html/assets/css/screen.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sql/assets/queries.sql: -------------------------------------------------------------------------------- 1 | -- Place your queries here. Docs available https://www.hugsql.org/ 2 | -------------------------------------------------------------------------------- /html/assets/img/kit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kit-clj/modules/HEAD/html/assets/img/kit.png -------------------------------------------------------------------------------- /tailwind/assets/input.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /dominoui-postgres/assets/migrations/20240104143537-user-info.down.sql: -------------------------------------------------------------------------------- 1 | --;; 2 | DROP TABLE user_info; -------------------------------------------------------------------------------- /cljs/assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "shadow-cljs": "^2.18.0" 4 | }, 5 | "dependencies": { 6 | "react": "^17.0.2", 7 | "react-dom": "^17.0.2" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tailwind/assets/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.clj"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | } 9 | -------------------------------------------------------------------------------- /dominoui-postgres/assets/migrations/20240104143537-user-info.up.sql: -------------------------------------------------------------------------------- 1 | --;; 2 | CREATE TABLE user_info ( 3 | user_id bigint UNIQUE NOT NULL, 4 | height double precision, 5 | weight double precision 6 | ); -------------------------------------------------------------------------------- /cljs/assets/package.uix.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "shadow-cljs": "^2.22.9" 4 | }, 5 | "dependencies": { 6 | "react": "^18.2.0", 7 | "react-dom": "^18.2.0", 8 | "react-refresh": "^0.14.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tailwind/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? false 3 | :actions 4 | {:assets 5 | [["assets/input.css" "tailwind/input.css"] 6 | ["assets/tailwind.config.js" "tailwind.config.js"] 7 | ["assets/package.json" "package.json"] 8 | ["assets/README.tailwind.md" "README.tailwind.md"]]}}} 9 | -------------------------------------------------------------------------------- /tailwind/assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "tailwindcss": "^3.4.17" 4 | }, 5 | "scripts": { 6 | "tailwind": "tailwindcss -m -i tailwind/input.css -o resources/public/output.css", 7 | "tailwind:watch": "tailwindcss -i tailwind/input.css -o resources/public/output.css --watch" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tailwind/assets/README.tailwind.md: -------------------------------------------------------------------------------- 1 | # Kit Tailwind 2 | 3 | The Kit Tailwind extension parses your hiccup source to extract tailwind classes before running tailwind. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | npm i 9 | npm run tailwind 10 | npm run tailwind:watch # watcher 11 | ``` 12 | 13 | ## Issues 14 | 15 | Raise an issue and mention `@whamtet`. 16 | -------------------------------------------------------------------------------- /dominoui/assets/src/core.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.middleware.core 2 | (:require 3 | [<>.env :as env] 4 | [ring.middleware.defaults :as defaults])) 5 | 6 | (defn wrap-base 7 | [{:keys [site-defaults-config] :as opts}] 8 | (fn [handler] 9 | (-> ((:middleware env/defaults) handler opts) 10 | (defaults/wrap-defaults site-defaults-config)))) 11 | -------------------------------------------------------------------------------- /devcontainer/assets/compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | devcontainer: 4 | build: 5 | dockerfile: Dockerfile.dev 6 | volumes: 7 | - ../..:/workspaces:cached 8 | # Uncomment to share maven cache, doesn't work in Github Codespaces. 9 | # - ~/.m2:/root/.m2 # maven cache 10 | command: sleep infinity 11 | 12 | # Add your other services here. -------------------------------------------------------------------------------- /dominoui-postgres/assets/src/user.clj: -------------------------------------------------------------------------------- 1 | (ns a.b.web.controllers.user) 2 | 3 | (def USER-ID 1) 4 | 5 | (defn get-user [{:keys [query-fn]}] 6 | (query-fn :user-by-id {:user-id USER-ID})) 7 | 8 | (defn set-weight [query-fn weight] 9 | (query-fn :set-weight {:user-id USER-ID :weight weight})) 10 | 11 | (defn set-height [query-fn height] 12 | (query-fn :set-height {:user-id USER-ID :height height})) 13 | -------------------------------------------------------------------------------- /auth/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets [["assets/src/auth.clj" "src/clj/<>/web/middleware/auth.clj"]] 5 | :injections [{:type :edn 6 | :path "deps.edn" 7 | :target [:deps] 8 | :action :merge 9 | :value {buddy/buddy-auth {:mvn/version "3.0.323"}}}]}}} 10 | -------------------------------------------------------------------------------- /datomic/assets/src/datomic.clj: -------------------------------------------------------------------------------- 1 | (ns <>.datomic 2 | (:require [clojure.tools.logging :as log] 3 | [datomic.api :as d] 4 | [integrant.core :as ig])) 5 | 6 | (defmethod ig/init-key :db.datomic/conn 7 | [_ {:keys [db-uri]}] 8 | (when (d/create-database db-uri) 9 | (log/info (str "Database " db-uri " created (didn't exist)"))) 10 | (d/connect db-uri)) 11 | 12 | (defmethod ig/halt-key! :db.datomic/conn 13 | [_ conn] 14 | (d/release conn)) 15 | -------------------------------------------------------------------------------- /cljs/assets/src/core.cljs: -------------------------------------------------------------------------------- 1 | (ns <>.core 2 | (:require 3 | [reagent.core :as r] 4 | [reagent.dom :as d])) 5 | 6 | ;; ------------------------- 7 | ;; Views 8 | 9 | (defn home-page [] 10 | [:div [:h2 "Welcome to Reagent!"]]) 11 | 12 | ;; ------------------------- 13 | ;; Initialize app 14 | 15 | (defn ^:dev/after-load mount-root [] 16 | (d/render [home-page] (.getElementById js/document "app"))) 17 | 18 | (defn ^:export ^:dev/once init! [] 19 | (mount-root)) 20 | -------------------------------------------------------------------------------- /codox/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets [] 5 | :injections [{:type :edn 6 | :path "deps.edn" 7 | :target [:aliases] 8 | :action :merge 9 | :value {:codox {:extra-deps {codox/codox {:mvn/version "0.10.8"}} 10 | :exec-fn codox.main/generate-docs 11 | :exec-args {:source-paths ["src/clj" "resources"]}}}}]}}} 12 | -------------------------------------------------------------------------------- /htmx/assets/src/htmx.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.htmx 2 | (:require 3 | [ring.util.http-response :as http-response] 4 | [hiccup.core :as h] 5 | [hiccup.page :as p])) 6 | 7 | (defmacro page [opts & content] 8 | `(-> (p/html5 ~opts ~@content) 9 | http-response/ok 10 | (http-response/content-type "text/html"))) 11 | 12 | (defmacro fragment [opts & content] 13 | `(-> (str (h/html ~opts ~@content)) 14 | http-response/ok 15 | (http-response/content-type "text/html"))) 16 | -------------------------------------------------------------------------------- /cljs/assets/src/core.uix.cljs: -------------------------------------------------------------------------------- 1 | (ns <>.core 2 | (:require 3 | [uix.core :refer [defui $]] 4 | [uix.dom])) 5 | 6 | ;; ------------------------- 7 | ;; Views 8 | 9 | (defui home-page [] 10 | ($ :div 11 | ($ :h2 "Welcome to UIx!"))) 12 | 13 | ;; ------------------------- 14 | ;; Initialize app 15 | 16 | (defn ^:dev/after-load mount-root [] 17 | (uix.dom/render-root ($ home-page) (uix.dom/create-root (js/document.getElementById "app")))) 18 | 19 | (defn ^:export ^:dev/once init! [] 20 | (mount-root)) 21 | -------------------------------------------------------------------------------- /dominoui/assets/src/ws.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.ws 2 | (:require 3 | [simpleui.render :as render] 4 | [ring.adapter.undertow.websocket :as ws])) 5 | 6 | (def channels (atom #{})) 7 | 8 | ;; TODO: group channels by user etc. 9 | (defn add-channel [channel] 10 | (swap! channels conj channel)) 11 | 12 | (defn remove-channel [channel] 13 | (swap! channels disj channel)) 14 | 15 | (defn broadcast [snippet] 16 | (let [snippet (render/html snippet)] 17 | (doseq [channel @channels] 18 | (ws/send snippet channel)))) 19 | -------------------------------------------------------------------------------- /devcontainer/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? false 3 | :actions 4 | {:assets 5 | [["assets/docker/devcontainer.json" ".devcontainer/devcontainer.json"] 6 | ["assets/Dockerfile.dev" ".devcontainer/Dockerfile.dev"]]}} 7 | :compose 8 | {:require-restart? false 9 | :actions 10 | {:assets 11 | [["assets/compose/devcontainer.json" ".devcontainer/devcontainer.json"] 12 | ["assets/compose/docker-compose.yml" ".devcontainer/docker-compose.yml"] 13 | ["assets/Dockerfile.dev" ".devcontainer/Dockerfile.dev"]]}}} -------------------------------------------------------------------------------- /dominoui/assets/src/disp/home.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.views.disp.home) 2 | 3 | (defn input [label name value] 4 | [:div 5 | [:label label] 6 | [:input 7 | {:type "text" :name name :value value 8 | :hx-post "bmi-form" :hx-trigger "keyup changed delay:0.3s"}]]) 9 | 10 | (defn bmi-label [bmi] 11 | [:label#bmi (format "BMI: %.1f" bmi)]) 12 | 13 | (defn form [height weight bmi] 14 | [:div 15 | [:h2 "BMI Calculator"] 16 | (input "height (KG) " "height" height) 17 | (input "weight (M) " "weight" weight) 18 | (bmi-label bmi)]) 19 | -------------------------------------------------------------------------------- /html/assets/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Something Bad Happened 5 | 6 | 7 | 8 | 9 |
10 |

Error: {{status}}

11 |
12 | {% if title %} 13 |

{{title}}

14 | {% endif %} 15 | {% if message %} 16 |

{{message}}

17 | {% endif %} 18 |
19 | 20 | -------------------------------------------------------------------------------- /dominoui-postgres/assets/sql/queries.sql: -------------------------------------------------------------------------------- 1 | -- Place your queries here. Docs available https://www.hugsql.org/ 2 | 3 | -- :name user-by-id :query :one 4 | select * from user_info where user_id = :user-id 5 | 6 | -- :name set-weight :execute 7 | insert into user_info (user_id, weight) 8 | values (:user-id, :weight) 9 | on conflict (user_id) do update 10 | set weight = EXCLUDED.weight; 11 | 12 | -- :name set-height :execute 13 | insert into user_info (user_id, height) 14 | values (:user-id, :height) 15 | on conflict (user_id) do update 16 | set height = EXCLUDED.height; 17 | -------------------------------------------------------------------------------- /html/assets/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Welcome to <<name>> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 |
17 |
18 |

Hello World!

19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /cljs/assets/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | {:nrepl {:port 7002} 2 | :source-paths ["src/cljs"] 3 | :dependencies [[binaryage/devtools "1.0.3"] 4 | [nrepl "0.8.3"] 5 | [cider/cider-nrepl "0.30.0"] 6 | [cljs-ajax "0.8.4"]] 7 | :builds {:app {:target :browser 8 | :output-dir "target/classes/cljsbuild/public/js" 9 | :asset-path "/js" 10 | :modules {:app {:entries [<>.core] 11 | :init-fn <>.core/init!}} 12 | :devtools {:after-load <>.core/mount-root}}}} 13 | -------------------------------------------------------------------------------- /simpleui/assets/src/hello.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.views.hello 2 | (:require 3 | [simpleui.core :as simpleui :refer [defcomponent]] 4 | [<>.web.htmx :refer [page-htmx]])) 5 | 6 | (defcomponent ^:endpoint hello [req my-name] 7 | [:div#hello "Hello " my-name]) 8 | 9 | (defn ui-routes [base-path] 10 | (simpleui/make-routes 11 | base-path 12 | (fn [req] 13 | (page-htmx 14 | [:label {:style "margin-right: 10px"} 15 | "What is your name?"] 16 | [:input {:type "text" 17 | :name "my-name" 18 | :hx-patch "hello" 19 | :hx-target "#hello" 20 | :hx-swap "outerHTML"}] 21 | (hello req ""))))) 22 | -------------------------------------------------------------------------------- /devcontainer/assets/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM clojure:temurin-20-tools-deps-jammy 2 | # Details as of 2023-09-19: 3 | # Ubuntu: Ubuntu 22.04 LTS (Jammy Jellyfish) 4 | # JDK: eclipse-temurin 20 5 | # Clojure: tools-deps, 1.11.1.1347 6 | 7 | # Extra tools 8 | RUN apt-get update && apt-get install -y gpg curl 9 | 10 | # Install new and clj-new (prefer new, but clj-new is needed for some templates) 11 | 12 | RUN clojure -Ttools install-latest :lib io.github.seancorfield/deps-new :as new 13 | RUN clojure -Ttools install-latest :lib com.github.seancorfield/clj-new :as clj-new 14 | 15 | # Add Babashka 16 | 17 | RUN curl -sLO https://raw.githubusercontent.com/babashka/babashka/master/install \ 18 | && chmod +x install \ 19 | && ./install --static -------------------------------------------------------------------------------- /auth/assets/src/auth.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.middleware.auth 2 | (:require 3 | [buddy.auth.backends.session :as session] 4 | [buddy.auth :as auth] 5 | [buddy.auth.accessrules :as accessrules] 6 | [buddy.auth.middleware :as auth-middleware])) 7 | 8 | (defn on-error [request _response] 9 | {:status 403 10 | :headers {} 11 | :body (str "Access to " (:uri request) " is not authorized")}) 12 | 13 | (defn wrap-restricted [handler] 14 | (accessrules/restrict handler {:handler auth/authenticated? 15 | :on-error on-error})) 16 | 17 | (defn wrap-auth [handler] 18 | (let [backend (session/session-backend)] 19 | (-> handler 20 | (auth-middleware/wrap-authentication backend) 21 | (auth-middleware/wrap-authorization backend)))) 22 | -------------------------------------------------------------------------------- /htmx/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets 5 | [["assets/src/ui.clj" "src/clj/<>/web/routes/ui.clj"] 6 | ["assets/src/htmx.clj" "src/clj/<>/web/htmx.clj"]] 7 | :injections 8 | [{:type :edn 9 | :path "resources/system.edn" 10 | :target [] 11 | :action :merge 12 | :value {:reitit.routes/ui 13 | {:base-path "" 14 | :env #ig/ref :system/env}}} 15 | {:type :edn 16 | :path "deps.edn" 17 | :target [:deps] 18 | :action :merge 19 | :value {hiccup/hiccup {:mvn/version "2.0.0"}}} 20 | {:type :clj 21 | :path "src/clj/<>/core.clj" 22 | :action :append-requires 23 | :value ["[<>.web.routes.ui]"]}]}}} 24 | -------------------------------------------------------------------------------- /simpleui/assets/src/htmx.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.htmx 2 | (:require 3 | [simpleui.render :as render] 4 | [ring.util.http-response :as http-response] 5 | [hiccup.core :as h] 6 | [hiccup.page :as p])) 7 | 8 | (defn page [opts & content] 9 | (-> (p/html5 opts content) 10 | http-response/ok 11 | (http-response/content-type "text/html"))) 12 | 13 | (defn ui [opts & content] 14 | (-> (h/html opts content) 15 | http-response/ok 16 | (http-response/content-type "text/html"))) 17 | 18 | (defn page-htmx [& body] 19 | (page 20 | [:head 21 | [:meta {:charset "UTF-8"}] 22 | [:title "Htmx + Kit"] 23 | [:script {:src "https://unpkg.com/htmx.org@1.2.0/dist/htmx.min.js" :defer true}]] 24 | [:body (render/walk-attrs body)])) 25 | -------------------------------------------------------------------------------- /devcontainer/assets/docker/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kit devcontainer", 3 | "build": { 4 | "dockerfile": "Dockerfile.dev" 5 | }, 6 | "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", 7 | // More info: https://containers.dev/features. 8 | "features": {}, 9 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 10 | // "forwardPorts": [ ], 11 | // Run commands on cluster first start. Useful for download dependencies, etc. 12 | // "postCreateCommand": "", 13 | // Copy host env vars into the devcontainer. You can also refer to some_file.env files in your docker or docker-compose setup. 14 | "remoteEnv": {}, 15 | "customizations": { 16 | "vscode": { 17 | "extensions": [ 18 | "betterthantomorrow.calva" 19 | ] 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /devcontainer/assets/compose/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kit devcontainer", 3 | "dockerComposeFile": "docker-compose.yml", 4 | "service": "devcontainer", 5 | "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", 6 | // More info: https://containers.dev/features. 7 | "features": {}, 8 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 9 | // "forwardPorts": [ ], 10 | // Run commands on cluster first start. Useful for download dependencies, etc. 11 | // "postCreateCommand": "", 12 | // Copy host env vars into the devcontainer. You can also refer to some_file.env files in your docker or docker-compose setup. 13 | "remoteEnv": {}, 14 | "customizations": { 15 | "vscode": { 16 | "extensions": [ 17 | "betterthantomorrow.calva" 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /nrepl/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets [] 5 | :injections [{:type :edn 6 | :path "deps.edn" 7 | :target [:deps] 8 | :action :merge 9 | :value {io.github.kit-clj/kit-nrepl {:mvn/version "1.0.4"}}} 10 | {:type :edn 11 | :path "resources/system.edn" 12 | :target [] 13 | :action :merge 14 | :value {:nrepl/server {:port #long #or [#env NREPL_PORT 7001] 15 | :bind #or [#env NREPL_HOST "127.0.0.1"]}}} 16 | {:type :clj 17 | :path "src/clj/<>/core.clj" 18 | :action :append-requires 19 | :value ["[kit.edge.utils.nrepl]"]}]}}} 20 | 21 | -------------------------------------------------------------------------------- /dominoui/assets/src/htmx.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.htmx 2 | (:require 3 | [simpleui.render :as render] 4 | [ring.util.http-response :as http-response] 5 | [hiccup.core :as h] 6 | [hiccup.page :as p])) 7 | 8 | (defn page [opts & content] 9 | (-> (p/html5 opts content) 10 | http-response/ok 11 | (http-response/content-type "text/html"))) 12 | 13 | (defn ui [opts & content] 14 | (-> (h/html opts content) 15 | http-response/ok 16 | (http-response/content-type "text/html"))) 17 | 18 | (defn page-htmx [& body] 19 | (page 20 | [:head 21 | [:meta {:charset "UTF-8"}] 22 | [:title "DominoUI + Kit"]] 23 | [:body {:hx-ext "ws" :ws-connect "/ws"} 24 | (render/walk-attrs body) 25 | [:script {:src "https://unpkg.com/htmx.org@1.9.9"}] 26 | [:script {:src "https://unpkg.com/htmx.org@1.9.9/dist/ext/ws.js"}]])) 27 | -------------------------------------------------------------------------------- /datomic/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets [["assets/src/datomic.clj" "src/clj/<>/datomic.clj"]] 5 | :injections 6 | [{:action :merge 7 | :path "resources/system.edn" 8 | :target [] 9 | :type :edn 10 | :value 11 | {:db.datomic/conn 12 | #profile 13 | {:dev {:db-uri #or [#env DATOMIC_DB_URI "datomic:dev://localhost:4334/<>"]} 14 | :test {:db-uri #or [#env DATOMIC_DB_URI_TEST "datomic:dev://localhost:4334/<>_test"]} 15 | :prod {:db-uri #env DATOMIC_DB_URI}}}} 16 | 17 | {:action :merge 18 | :path "deps.edn" 19 | :target [:deps] 20 | :type :edn 21 | :value {com.datomic/peer {:mvn/version "1.0.7075"}}} 22 | 23 | {:action :append-requires 24 | :path "src/clj/<>/core.clj" 25 | :type :clj 26 | :value ["[<>.datomic]"]}]}}} 27 | -------------------------------------------------------------------------------- /dominoui-postgres/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :requires [[:kit/sql {:feature-flag :postgres}] :kit/dominoui] 4 | :actions 5 | {:assets [["assets/migrations/20240104143537-user-info.up.sql" "resources/migrations/20240104143537-user-info.up.sql"] 6 | ["assets/migrations/20240104143537-user-info.down.sql" "resources/migrations/20240104143537-user-info.down.sql"] 7 | ["assets/sql/queries.sql" "resources/sql/queries.sql" :force] 8 | ["assets/src/user.clj" "src/clj/<>/web/controllers/user.clj"] 9 | ["assets/src/domino.clj" "src/clj/<>/web/domino.clj" :force] 10 | ["assets/src/home.clj" "src/clj/<>/web/views/home.clj" :force]] 11 | :injections 12 | [{:type :edn 13 | :path "resources/system.edn" 14 | :target [:reitit.routes/ui] 15 | :action :merge 16 | :value {:query-fn #ig/ref :db.sql/query-fn}}]}}} 17 | -------------------------------------------------------------------------------- /dominoui/assets/src/home.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.views.home 2 | (:require 3 | [<>.web.domino :as domino] 4 | [<>.web.htmx :refer [page-htmx]] 5 | [<>.web.views.disp.home :as disp.home] 6 | [simpleui.core :as simpleui :refer [defcomponent]])) 7 | 8 | (defcomponent ^:endpoint bmi-form [{:keys [session] :as req} ^:double height ^:double weight] 9 | (cond 10 | height (domino/transact session :height height) 11 | weight (domino/transact session :weight weight) 12 | :else (disp.home/form 13 | (domino/select session :height) 14 | (domino/select session :weight) 15 | (domino/select session :bmi)))) 16 | 17 | (defn ui-routes [_] 18 | (simpleui/make-routes 19 | "" 20 | (fn [req] 21 | (let [session (or (not-empty (:session req)) domino/initial-session) 22 | req (assoc req :session session)] 23 | (-> req bmi-form page-htmx (assoc :session session)))))) 24 | 25 | -------------------------------------------------------------------------------- /hato/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets [] 5 | :injections [{:type :edn 6 | :path "deps.edn" 7 | :target [:deps] 8 | :action :merge 9 | :value {io.github.kit-clj/kit-hato {:mvn/version "1.0.3"}}} 10 | {:type :clj 11 | :path "src/clj/<>/core.clj" 12 | :action :append-requires 13 | :value ["[kit.edge.http.hato]"]} 14 | {:type :edn 15 | :path "resources/system.edn" 16 | :target [] 17 | :action :merge 18 | :value {:http.client/hato {}}} 19 | {:type :edn 20 | :path "resources/system.edn" 21 | :target [:reitit.routes/api] 22 | :action :merge 23 | :value {:http/client #ig/ref :http.client/hato}}]}}} 24 | -------------------------------------------------------------------------------- /simpleui/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets 5 | [["assets/src/ui.clj" "src/clj/<>/web/routes/ui.clj"] 6 | ["assets/src/htmx.clj" "src/clj/<>/web/htmx.clj"] 7 | ["assets/src/hello.clj" "src/clj/<>/web/views/hello.clj"]] 8 | :injections 9 | [{:type :edn 10 | :path "resources/system.edn" 11 | :target [] 12 | :action :merge 13 | :value {:reitit.routes/ui 14 | {:base-path "" 15 | :env #ig/ref :system/env}}} 16 | {:type :edn 17 | :path "deps.edn" 18 | :target [:deps] 19 | :action :merge 20 | :value {simpleui/simpleui {:git/url "https://github.com/whamtet/simpleui" 21 | :git/sha "a107d7a8a69755dae6cd9954992614a86504c257"}}} 22 | {:type :clj 23 | :path "src/clj/<>/core.clj" 24 | :action :append-requires 25 | :value ["[<>.web.routes.ui]"]}]}}} 26 | -------------------------------------------------------------------------------- /simpleui/assets/src/ui.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.routes.ui 2 | (:require 3 | [<>.web.middleware.exception :as exception] 4 | [<>.web.middleware.formats :as formats] 5 | [<>.web.views.hello :as hello] 6 | [integrant.core :as ig] 7 | [reitit.ring.middleware.muuntaja :as muuntaja] 8 | [reitit.ring.middleware.parameters :as parameters])) 9 | 10 | (defn route-data [opts] 11 | (merge 12 | opts 13 | {:muuntaja formats/instance 14 | :middleware 15 | [;; Default middleware for ui 16 | ;; query-params & form-params 17 | parameters/parameters-middleware 18 | ;; encoding response body 19 | muuntaja/format-response-middleware 20 | ;; exception handling 21 | exception/wrap-exception]})) 22 | 23 | (derive :reitit.routes/ui :reitit/routes) 24 | 25 | (defmethod ig/init-key :reitit.routes/ui 26 | [_ {:keys [base-path] 27 | :or {base-path ""} 28 | :as opts}] 29 | [base-path (route-data opts) (hello/ui-routes base-path)]) 30 | -------------------------------------------------------------------------------- /dominoui-postgres/assets/src/home.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.views.home 2 | (:require 3 | [<>.web.domino :as domino] 4 | [<>.web.htmx :refer [page-htmx]] 5 | [<>.web.views.disp.home :as disp.home] 6 | [simpleui.core :as simpleui :refer [defcomponent]])) 7 | 8 | (defcomponent ^:endpoint bmi-form [{:keys [session] :as req} ^:double height ^:double weight] 9 | (cond 10 | height 11 | (domino/transact session :height height) 12 | weight 13 | (domino/transact session :weight weight) 14 | :else (disp.home/form 15 | (domino/select session :height) 16 | (domino/select session :weight) 17 | (domino/select session :bmi)))) 18 | 19 | (defn ui-routes [{:keys [query-fn]}] 20 | (simpleui/make-routes 21 | "" 22 | [query-fn] 23 | (fn [req] 24 | (let [req (assoc req :query-fn query-fn) 25 | session (or (not-empty (:session req)) (domino/initial-session req)) 26 | req (assoc req :session session)] 27 | (-> req bmi-form page-htmx (assoc :session session)))))) 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Kit framework team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dominoui/assets/src/ui.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.routes.ui 2 | (:require 3 | [<>.web.middleware.exception :as exception] 4 | [<>.web.middleware.formats :as formats] 5 | [<>.web.views.home :as home] 6 | [<>.web.ws :as ws] 7 | [integrant.core :as ig] 8 | [reitit.ring.middleware.muuntaja :as muuntaja] 9 | [reitit.ring.middleware.parameters :as parameters])) 10 | 11 | (defn route-data [opts] 12 | (merge 13 | opts 14 | {:muuntaja formats/instance 15 | :middleware 16 | [;; Default middleware for ui 17 | ;; query-params & form-params 18 | parameters/parameters-middleware 19 | ;; encoding response body 20 | muuntaja/format-response-middleware 21 | ;; exception handling 22 | exception/wrap-exception]})) 23 | 24 | (derive :reitit.routes/ui :reitit/routes) 25 | 26 | (defmethod ig/init-key :reitit.routes/ui 27 | [_ {:keys [base-path] 28 | :or {base-path ""} 29 | :as opts}] 30 | [base-path (route-data opts) 31 | (conj (home/ui-routes opts) 32 | ["/ws" 33 | (fn [_req] 34 | {:undertow/websocket 35 | {:on-open (fn [{:keys [channel]}] (ws/add-channel channel)) 36 | :on-close-message (fn [{:keys [channel]}] (ws/remove-channel channel))}})])]) 37 | -------------------------------------------------------------------------------- /dominoui/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets 5 | [["assets/src/core.clj" "src/clj/<>/web/middleware/core.clj" :force] 6 | ["assets/src/ui.clj" "src/clj/<>/web/routes/ui.clj"] 7 | ["assets/src/htmx.clj" "src/clj/<>/web/htmx.clj"] 8 | ["assets/src/domino.clj" "src/clj/<>/web/domino.clj"] 9 | ["assets/src/ws.clj" "src/clj/<>/web/ws.clj"] 10 | ["assets/src/home.clj" "src/clj/<>/web/views/home.clj"] 11 | ["assets/src/disp/home.clj" "src/clj/<>/web/views/disp/home.clj"]] 12 | :injections 13 | [{:type :edn 14 | :path "resources/system.edn" 15 | :target [] 16 | :action :merge 17 | :value {:reitit.routes/ui 18 | {:base-path "" 19 | :env #ig/ref :system/env}}} 20 | {:type :edn 21 | :path "deps.edn" 22 | :target [:deps] 23 | :action :merge 24 | :value {simpleui/simpleui {:git/url "https://github.com/whamtet/simpleui" 25 | :git/sha "a107d7a8a69755dae6cd9954992614a86504c257"} 26 | domino/core {:mvn/version "0.4.0-alpha.3"}}} 27 | {:type :clj 28 | :path "src/clj/<>/core.clj" 29 | :action :append-requires 30 | :value ["[<>.web.routes.ui]"]}]}}} 31 | -------------------------------------------------------------------------------- /dominoui/assets/src/domino.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.domino 2 | (:require 3 | [domino.core :as domino] 4 | [simpleui.response :as response] 5 | [<>.web.ws :as ws] 6 | [<>.web.views.disp.home :as disp.home])) 7 | 8 | ;; adapted from domino README.md 9 | 10 | (def schema 11 | {:model [[:demographics 12 | [:height {:id :height}] 13 | [:weight {:id :weight}]] 14 | [:vitals 15 | [:bmi {:id :bmi}]]] 16 | :events [{:inputs [:height :weight] 17 | :outputs [:bmi] 18 | :handler (fn [{{:keys [height weight]} :inputs 19 | {:keys [bmi]} :outputs}] 20 | {:bmi (if (and height weight) 21 | (/ weight (* height height)) 22 | bmi)})}] 23 | :effects [{:inputs [:bmi] 24 | :handler (fn [{{:keys [bmi]} :inputs}] 25 | (ws/broadcast 26 | (disp.home/bmi-label bmi)))}]}) 27 | 28 | (def initial-session (domino/initialize schema {:demographics {:height 1.6 :weight 60.0}})) 29 | 30 | (defn transact [session id v] 31 | (assoc response/no-content 32 | :session (domino/transact session [[[id] v]]))) 33 | 34 | (defn select [session id] 35 | (domino/select session id)) 36 | -------------------------------------------------------------------------------- /html/assets/src/pages.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.routes.pages 2 | (:require 3 | [<>.web.middleware.exception :as exception] 4 | [<>.web.pages.layout :as layout] 5 | [integrant.core :as ig] 6 | [reitit.ring.middleware.muuntaja :as muuntaja] 7 | [reitit.ring.middleware.parameters :as parameters] 8 | [ring.middleware.anti-forgery :refer [wrap-anti-forgery]])) 9 | 10 | (defn wrap-page-defaults [] 11 | (let [error-page (layout/error-page 12 | {:status 403 13 | :title "Invalid anti-forgery token"})] 14 | #(wrap-anti-forgery % {:error-response error-page}))) 15 | 16 | (defn home [request] 17 | (layout/render request "home.html")) 18 | 19 | ;; Routes 20 | (defn page-routes [_opts] 21 | [["/" {:get home}]]) 22 | 23 | (def route-data 24 | {:middleware 25 | [;; Default middleware for pages 26 | (wrap-page-defaults) 27 | ;; query-params & form-params 28 | parameters/parameters-middleware 29 | ;; encoding response body 30 | muuntaja/format-response-middleware 31 | ;; exception handling 32 | exception/wrap-exception]}) 33 | 34 | (derive :reitit.routes/pages :reitit/routes) 35 | 36 | (defmethod ig/init-key :reitit.routes/pages 37 | [_ {:keys [base-path] 38 | :or {base-path ""} 39 | :as opts}] 40 | (layout/init-selmer! opts) 41 | (fn [] [base-path route-data (page-routes opts)])) 42 | 43 | -------------------------------------------------------------------------------- /metrics/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets [] 5 | :injections [{:type :edn 6 | :path "deps.edn" 7 | :target [:deps] 8 | :action :merge 9 | :value {io.github.kit-clj/kit-metrics {:mvn/version "1.0.2"}}} 10 | {:type :edn 11 | :path "resources/system.edn" 12 | :target [] 13 | :action :merge 14 | :value {:metrics/prometheus {}}} 15 | {:type :edn 16 | :path "resources/system.edn" 17 | :target [:handler/ring] 18 | :action :merge 19 | :value {:metrics #ig/ref :metrics/prometheus}} 20 | {:type :edn 21 | :path "resources/system.edn" 22 | :target [:reitit.routes/api] 23 | :action :merge 24 | :value {:metrics #ig/ref :metrics/prometheus}} 25 | {:type :clj 26 | :path "src/clj/<>/core.clj" 27 | :action :append-requires 28 | :value ["[kit.edge.utils.metrics]"]} 29 | {:type :clj 30 | :path "src/clj/<>/web/middleware/core.clj" 31 | :action :append-requires 32 | :value ["[iapetos.collector.ring :as prometheus-ring]"]} 33 | ]}}} -------------------------------------------------------------------------------- /html/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :actions 4 | {:assets [["assets/home.html" "resources/html/home.html"] 5 | ["assets/error.html" "resources/html/error.html"] 6 | ["assets/css/screen.css" "resources/public/css/screen.css"] 7 | ["assets/img/kit.png" "resources/public/img/kit.png"] 8 | ["assets/src/pages.clj" "src/clj/<>/web/routes/pages.clj"] 9 | ["assets/src/layout.clj" "src/clj/<>/web/pages/layout.clj"]] 10 | :injections [{:type :edn 11 | :path "resources/system.edn" 12 | :target [] 13 | :action :merge 14 | :value {:reitit.routes/pages 15 | {:base-path "" 16 | :env #ig/ref :system/env}}} 17 | {:type :edn 18 | :path "deps.edn" 19 | :target [:deps] 20 | :action :merge 21 | :value {selmer/selmer {:mvn/version "1.12.50"} 22 | luminus/ring-ttl-session {:mvn/version "0.3.3"}}} 23 | {:type :clj 24 | :path "src/clj/<>/core.clj" 25 | :action :append-requires 26 | :value ["[<>.web.routes.pages]"]}]}}} 27 | -------------------------------------------------------------------------------- /html/assets/src/layout.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.pages.layout 2 | (:require 3 | [clojure.java.io] 4 | [selmer.parser :as parser] 5 | [ring.util.http-response :refer [content-type ok]] 6 | [ring.util.anti-forgery :refer [anti-forgery-field]] 7 | [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] 8 | [ring.util.response])) 9 | 10 | (def selmer-opts {:custom-resource-path (clojure.java.io/resource "html")}) 11 | 12 | (defn init-selmer! 13 | [{:keys [env]}] 14 | ;; disable HTML template caching for live reloading during development 15 | (when (= :dev env) (parser/cache-off!)) 16 | (parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field)))) 17 | 18 | (defn render 19 | [request template & [params]] 20 | (-> (parser/render-file template 21 | (assoc params :page template :csrf-token *anti-forgery-token*) 22 | selmer-opts) 23 | (ok) 24 | (content-type "text/html; charset=utf-8"))) 25 | 26 | (defn error-page 27 | "error-details should be a map containing the following keys: 28 | :status - error status 29 | :title - error title (optional) 30 | :message - detailed error message (optional) 31 | returns a response map with the error page as the body 32 | and the status specified by the status key" 33 | [error-details] 34 | {:status (:status error-details) 35 | :headers {"Content-Type" "text/html; charset=utf-8"} 36 | :body (parser/render-file "error.html" error-details selmer-opts)}) -------------------------------------------------------------------------------- /sente/assets/ws.cljs: -------------------------------------------------------------------------------- 1 | (ns <>.ws 2 | (:require [taoensso.sente :as sente])) 3 | 4 | (let [connection (sente/make-channel-socket! "/ws" js/csrfToken {:type :auto})] 5 | (def ch-chsk (:ch-recv connection)) ; ChannelSocket's receive channel 6 | (def send-message! (:send-fn connection))) 7 | 8 | (defn state-handler [{:keys [?data]}] 9 | (.log js/console (str "state changed: " ?data))) 10 | 11 | (defn handshake-handler [{:keys [?data]}] 12 | (.log js/console (str "connection established: " ?data))) 13 | 14 | (defn default-event-handler [ev-msg] 15 | (.log js/console (str "Unhandled event: " (:event ev-msg)))) 16 | 17 | (defn event-msg-handler [& [{:keys [message state handshake] 18 | :or {state state-handler 19 | handshake handshake-handler}}]] 20 | (fn [ev-msg] 21 | (case (:id ev-msg) 22 | :chsk/handshake (handshake ev-msg) 23 | :chsk/state (state ev-msg) 24 | :chsk/recv (message ev-msg) 25 | (default-event-handler ev-msg)))) 26 | 27 | (def router (atom nil)) 28 | 29 | (defn stop-router! [] 30 | (when-let [stop-f @router] (stop-f))) 31 | 32 | (defn start-router! [message-handler] 33 | (stop-router!) 34 | (reset! router (sente/start-chsk-router! 35 | ch-chsk 36 | (event-msg-handler 37 | {:message message-handler 38 | :state state-handler 39 | :handshake handshake-handler})))) 40 | -------------------------------------------------------------------------------- /htmx/assets/src/ui.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.routes.ui 2 | (:require 3 | [<>.web.middleware.exception :as exception] 4 | [<>.web.middleware.formats :as formats] 5 | [<>.web.routes.utils :as utils] 6 | [<>.web.htmx :refer [page fragment] :as htmx] 7 | [integrant.core :as ig] 8 | [reitit.ring.middleware.muuntaja :as muuntaja] 9 | [reitit.ring.middleware.parameters :as parameters])) 10 | 11 | (defn home [request] 12 | (page {:lang "en"} 13 | [:head 14 | [:meta {:charset "UTF-8"}] 15 | [:title "Htmx + Kit"] 16 | [:script {:src "https://unpkg.com/htmx.org@2.0.8/dist/htmx.min.js" :defer true}]] 17 | [:body 18 | [:h1 "Welcome to Htmx + Kit module"] 19 | [:button {:hx-post "/clicked" :hx-swap "outerHTML"} "Click me!"]])) 20 | 21 | (defn clicked [request] 22 | (fragment 23 | [:div "Congratulations! You just clicked the button!"])) 24 | 25 | ;; Routes 26 | (defn ui-routes [_opts] 27 | [["/" {:get home}] 28 | ["/clicked" {:post clicked}]]) 29 | 30 | (def route-data 31 | {:muuntaja formats/instance 32 | :middleware 33 | [;; Default middleware for ui 34 | ;; query-params & form-params 35 | parameters/parameters-middleware 36 | ;; encoding response body 37 | muuntaja/format-response-middleware 38 | ;; exception handling 39 | exception/wrap-exception]}) 40 | 41 | (derive :reitit.routes/ui :reitit/routes) 42 | 43 | (defmethod ig/init-key :reitit.routes/ui 44 | [_ {:keys [base-path] 45 | :or {base-path ""} 46 | :as opts}] 47 | (fn [] [base-path route-data (ui-routes opts)])) 48 | -------------------------------------------------------------------------------- /cljs/assets/build.clj: -------------------------------------------------------------------------------- 1 | (ns build 2 | (:require [clojure.tools.build.api :as b] 3 | [clojure.string :as string] 4 | [clojure.java.shell :refer [sh]] 5 | [deps-deploy.deps-deploy :as deploy])) 6 | 7 | (def lib 'com.example/test) 8 | (def main-cls (string/join "." (filter some? [(namespace lib) (name lib) "core"]))) 9 | (def version (format "0.0.1-SNAPSHOT")) 10 | (def target-dir "target") 11 | (def class-dir (str target-dir "/" "classes")) 12 | (def uber-file (format "%s/%s-standalone.jar" target-dir (name lib))) 13 | (def basis (b/create-basis {:project "deps.edn"})) 14 | 15 | (defn clean 16 | "Delete the build target directory" 17 | [_] 18 | (println (str "Cleaning " target-dir)) 19 | (b/delete {:path target-dir})) 20 | 21 | (defn prep [_] 22 | (b/write-pom {:class-dir class-dir 23 | :lib lib 24 | :version version 25 | :basis basis 26 | :src-dirs ["src/clj"]}) 27 | (b/copy-dir {:src-dirs ["src" "resources" "env/prod"] 28 | :target-dir class-dir})) 29 | 30 | (defn build-cljs [_] 31 | (println "npx shadow-cljs release app...") 32 | (let [{:keys [exit] :as s} (sh "npx" "shadow-cljs" "release" "app")] 33 | (when-not (zero? exit) 34 | (throw (ex-info "could not compile cljs" s))))) 35 | 36 | (defn uber [_] 37 | (b/compile-clj {:basis basis 38 | :src-dirs ["src/clj" "env/prod/clj"] 39 | :class-dir class-dir}) 40 | (build-cljs nil) 41 | (println "Making uberjar...") 42 | (b/uber {:class-dir class-dir 43 | :uber-file uber-file 44 | :main main-cls 45 | :basis basis})) 46 | 47 | (defn all [_] 48 | (do (clean nil) (prep nil) (uber nil))) 49 | -------------------------------------------------------------------------------- /modules.edn: -------------------------------------------------------------------------------- 1 | {:name "kit-modules" 2 | :modules 3 | {:kit/auth 4 | {:path "auth" 5 | :doc "adds support for auth middleware using Buddy"} 6 | :kit/devcontainer 7 | {:path "devcontainer" 8 | :doc "adds support for devcontainer. Available profiles [ :default :compose ]. Default profile uses a single docker file."} 9 | :kit/html 10 | {:path "html" 11 | :doc "adds support for HTML templating using Selmer"} 12 | :kit/htmx 13 | {:path "htmx" 14 | :doc "adds support for HTMX using hiccup"} 15 | :kit/simpleui 16 | {:path "simpleui" 17 | :doc "adds support for HTMX using SimpleUI (previously called ctmx)"} 18 | :kit/dominoui 19 | {:path "dominoui" 20 | :doc "adds support for HTMX using SimpleUI and Domino"} 21 | :kit/dominoui-postgres 22 | {:path "dominoui-postgres" 23 | :doc "adds support for HTMX using SimpleUI and Domino. Includes sample postgres"} 24 | :kit/metrics 25 | {:path "metrics" 26 | :doc "adds support for metrics using prometheus through iapetos"} 27 | :kit/sente 28 | {:path "sente" 29 | :doc "adds support for Sente websockets to cljs"} 30 | :kit/sql 31 | {:path "sql" 32 | :doc "adds support for SQL. Available profiles [ :postgres :sqlite ]. Default profile :sqlite"} 33 | :kit/cljs 34 | {:path "cljs" 35 | :doc "adds support for cljs using shadow-cljs"} 36 | :kit/nrepl 37 | {:path "nrepl" 38 | :doc "adds support for nREPL"} 39 | :kit/tailwind 40 | {:path "tailwind" 41 | :doc "adds support for tailwindcss"} 42 | :kit/hato 43 | {:path "hato" 44 | :doc "adds support for kit-Hato HTTP client"} 45 | :kit/codox 46 | {:path "codox" 47 | :doc "adds support for codox"} 48 | :kit/datomic 49 | {:path "datomic" 50 | :doc "adds support for Datomic"}}} 51 | -------------------------------------------------------------------------------- /dominoui-postgres/assets/src/domino.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.domino 2 | (:require 3 | [domino.core :as domino] 4 | [simpleui.response :as response] 5 | [<>.web.controllers.user :as user] 6 | [<>.web.ws :as ws] 7 | [<>.web.views.disp.home :as disp.home])) 8 | 9 | ;; adapted from domino README.md 10 | 11 | (defn- schema [{:keys [query-fn]}] 12 | {:model [[:demographics 13 | [:height {:id :height}] 14 | [:weight {:id :weight}]] 15 | [:vitals 16 | [:bmi {:id :bmi}]]] 17 | :events [{:inputs [:height :weight] 18 | :outputs [:bmi] 19 | :handler (fn [{{:keys [height weight]} :inputs 20 | {:keys [bmi]} :outputs}] 21 | {:bmi (if (and height weight) 22 | (/ weight (* height height)) 23 | bmi)})}] 24 | :effects [{:inputs [:bmi] 25 | :handler (fn [{{:keys [bmi]} :inputs}] 26 | (ws/broadcast 27 | (disp.home/bmi-label bmi)))} 28 | {:inputs [:weight] 29 | :handler (fn [{{:keys [weight]} :inputs}] 30 | (user/set-weight query-fn weight))} 31 | {:inputs [:height] 32 | :handler (fn [{{:keys [height]} :inputs}] 33 | (user/set-height query-fn height))}]}) 34 | 35 | (defn initial-session [req] 36 | (let [{:keys [height weight]} (user/get-user req)] 37 | (domino/initialize (schema req) {:demographics {:height (or height 1.6) 38 | :weight (or weight 60.0)}}))) 39 | 40 | (defn transact [session id v] 41 | (assoc response/no-content 42 | :session (domino/transact session [[[id] v]]))) 43 | 44 | (defn select [session id] 45 | (domino/select session id)) 46 | -------------------------------------------------------------------------------- /sente/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:require-restart? true 3 | :requires [:kit/cljs] 4 | :actions 5 | {:assets [["assets/ws.clj" "src/clj/<>/web/routes/ws.clj"] 6 | ["assets/ws.cljs" "src/cljs/<>/ws.cljs"]] 7 | :injections [{:type :edn 8 | :path "deps.edn" 9 | :target [:deps] 10 | :action :merge 11 | :value {com.taoensso/sente {:mvn/version "1.17.0"}}} 12 | {:type :edn 13 | :path "shadow-cljs.edn" 14 | :target [:dependencies] 15 | :action :append 16 | :value [com.taoensso/sente "1.17.0"]} 17 | {:type :html 18 | :path "resources/html/home.html" 19 | :action :append 20 | :target [:head] 21 | :value [:script {:type "text/javascript"} "var csrfToken = '{{csrf-token}}';"]} 22 | {:type :edn 23 | :path "resources/system.edn" 24 | :target [] 25 | :action :merge 26 | :value {:sente/connection {} 27 | 28 | :sente/router 29 | {:connection #ig/ref :sente/connection} 30 | 31 | :reitit.routes/ws 32 | {:base-path "/ws" 33 | :env #ig/ref :system/env 34 | :connection #ig/ref :sente/connection}}} 35 | {:type :clj 36 | :path "src/clj/<>/core.clj" 37 | :action :append-requires 38 | :value ["[<>.web.routes.ws]"]} 39 | ;; {:type :clj 40 | ;; :path "src/cljs/<>/core.cljs" 41 | ;; :action :append-requires 42 | ;; :value ["[<>.ws :as ws]"]} 43 | ]}}} 44 | -------------------------------------------------------------------------------- /sente/assets/ws.clj: -------------------------------------------------------------------------------- 1 | (ns <>.web.routes.ws 2 | (:require 3 | [reitit.ring.middleware.parameters :as parameters] 4 | [taoensso.sente :as sente] 5 | [taoensso.sente.server-adapters.undertow :refer [get-sch-adapter]] 6 | [integrant.core :as ig])) 7 | 8 | (defn client-id [ring-req] 9 | (get-in ring-req [:params :client-id])) 10 | 11 | (defmulti on-message :id) 12 | 13 | (defmethod on-message :default 14 | [{:keys [id client-id ?data] :as message}] 15 | (println "on-message: id: " id " cilient-id: " client-id " ?data: " ?data)) 16 | 17 | (defmethod on-message :guestbook/echo 18 | [{:keys [id client-id ?data send-fn] :as message}] 19 | (let [response "Hello from the server"] 20 | (send-fn client-id [id response]))) 21 | 22 | (defmethod on-message :guestbook/broadcast 23 | [{:keys [id client-id ?data send-fn connected-uids] :as message}] 24 | (let [response (str "Hello to everyone from the client " client-id)] 25 | (doseq [uid (:any @connected-uids)] 26 | (send-fn uid [id response])))) 27 | 28 | (defmethod ig/init-key :sente/connection 29 | [_ opts] 30 | (sente/make-channel-socket! 31 | (get-sch-adapter) 32 | {:packer :edn 33 | ;; :csrf-token-fn nil 34 | :user-id-fn client-id})) 35 | 36 | 37 | (defn handle-message! [msg] 38 | ;; TODO - error handling 39 | (on-message msg)) 40 | 41 | (defmethod ig/init-key :sente/router 42 | [_ {:keys [connection] :as opts}] 43 | (sente/start-chsk-router! (:ch-recv connection) #'handle-message!)) 44 | 45 | (defmethod ig/halt-key! :sente/router 46 | [_ stop-fn] 47 | (when stop-fn (stop-fn))) 48 | 49 | (defn route-data 50 | [opts] 51 | (merge 52 | opts 53 | {:middleware 54 | [ring.middleware.keyword-params/wrap-keyword-params 55 | ring.middleware.params/wrap-params]})) 56 | 57 | (defn ws-routes [{:keys [connection] :as opts}] 58 | [["" {:get (:ajax-get-or-ws-handshake-fn connection) 59 | :post (:ajax-post-fn connection)}]]) 60 | 61 | (derive :reitit.routes/ws :reitit/routes) 62 | 63 | (defmethod ig/init-key :reitit.routes/ws 64 | [_ {:keys [base-path] 65 | :or {base-path ""} 66 | :as opts}] 67 | [base-path (route-data opts) (ws-routes opts)]) 68 | -------------------------------------------------------------------------------- /cljs/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:feature-requires [:base :reagent]} 3 | 4 | :base 5 | {:require-restart? true 6 | :actions 7 | {:assets [["assets/shadow-cljs.edn" "shadow-cljs.edn"]] 8 | :injections [{:type :html 9 | :path "resources/html/home.html" 10 | :action :append 11 | :target [:body] 12 | :value [:div {:id "app"}]} 13 | {:type :html 14 | :path "resources/html/home.html" 15 | :action :append 16 | :target [:body] 17 | :value [:script {:src "/js/app.js"}]} 18 | {:type :edn 19 | :path "deps.edn" 20 | :target [:paths] 21 | :action :append 22 | :value "src/cljs"} 23 | {:type :edn 24 | :path "deps.edn" 25 | :target [:aliases :dev :extra-paths] 26 | :action :append 27 | :value "target/classes/cljsbuild"} 28 | {:type :edn 29 | :path "deps.edn" 30 | :target [:aliases :build :deps] 31 | :action :merge 32 | :value {babashka/fs {:mvn/version "0.1.11"} 33 | babashka/process {:mvn/version "0.3.11"}}} 34 | {:type :clj 35 | :path "build.clj" 36 | :action :append-requires 37 | :value ["[babashka.fs :refer [copy-tree]]" 38 | "[babashka.process :refer [shell]]"]} 39 | {:type :clj 40 | :path "build.clj" 41 | :action :append-build-task 42 | :value (defn build-cljs [] 43 | (println "npx shadow-cljs release app...") 44 | (let [{:keys [exit] 45 | :as s} (shell "npx shadow-cljs release app")] 46 | (when-not (zero? exit) 47 | (throw (ex-info "could not compile cljs" s))) 48 | (copy-tree "target/classes/cljsbuild/public" "target/classes/public")))} 49 | {:type :clj 50 | :path "build.clj" 51 | :action :append-build-task-call 52 | :value (build-cljs)}]}} 53 | :reagent 54 | {:requires [:kit/html] 55 | :feature-requires [:base] 56 | 57 | :actions 58 | {:assets [["assets/src/core.cljs" "src/cljs/<>/core.cljs"] 59 | ["assets/package.json" "package.json"]] 60 | :injections [{:type :edn 61 | :path "shadow-cljs.edn" 62 | :target [:dependencies] 63 | :action :append 64 | :value [reagent "1.1.0"]}]}} 65 | 66 | :uix 67 | {:requires [:kit/html] 68 | :feature-requires [:base] 69 | :actions 70 | {:assets [["assets/src/core.uix.cljs" "src/cljs/<>/core.cljs"] 71 | ["assets/package.uix.json" "package.json"]] 72 | :injections [{:type :edn 73 | :path "shadow-cljs.edn" 74 | :target [:dependencies] 75 | :action :append 76 | :value [com.pitch/uix.core "1.1.0"]} 77 | {:type :edn 78 | :path "shadow-cljs.edn" 79 | :target [:dependencies] 80 | :action :append 81 | :value [com.pitch/uix.dom "1.1.0"]}]}}} 82 | -------------------------------------------------------------------------------- /sql/config.edn: -------------------------------------------------------------------------------- 1 | {:default 2 | {:feature-requires [:base :sqlite]} 3 | 4 | :base 5 | {:require-restart? true 6 | :actions 7 | {:assets [["assets/queries.sql" "resources/sql/queries.sql"] 8 | "resources/migrations"] 9 | :injections [{:type :edn 10 | :path "resources/system.edn" 11 | :target [] 12 | :action :merge 13 | :value {:db.sql/query-fn {:conn #ig/ref :db.sql/connection 14 | :options {} 15 | :filename "sql/queries.sql"} 16 | :db.sql/migrations {:store :database 17 | :db {:datasource #ig/ref :db.sql/connection} 18 | :migrate-on-init? true}}} 19 | {:type :edn 20 | :path "resources/system.edn" 21 | :target [:reitit.routes/api] 22 | :action :merge 23 | :value {:query-fn #ig/ref :db.sql/query-fn}} 24 | {:type :edn 25 | :path "deps.edn" 26 | :target [:deps] 27 | :action :merge 28 | :value {io.github.kit-clj/kit-sql-conman {:mvn/version "1.10.4"} 29 | io.github.kit-clj/kit-sql-migratus {:mvn/version "1.0.4"}}} 30 | {:type :clj 31 | :path "src/clj/<>/core.clj" 32 | :action :append-requires 33 | :value ["[kit.edge.db.sql.conman]" 34 | "[kit.edge.db.sql.migratus]"]}]} 35 | } 36 | 37 | :sqlite 38 | {:feature-requires [:base] 39 | :actions 40 | {:injections [{:type :edn 41 | :path "resources/system.edn" 42 | :target [] 43 | :action :merge 44 | :value {:db.sql/connection #profile 45 | {:dev {:jdbc-url "jdbc:sqlite:<>_dev.db"} 46 | :test {:jdbc-url "jdbc:sqlite:<>_test.db"} 47 | :prod {:jdbc-url #env JDBC_URL}}}} 48 | {:type :edn 49 | :path "deps.edn" 50 | :target [:deps] 51 | :action :merge 52 | :value {org.xerial/sqlite-jdbc {:mvn/version "3.46.0.0"}}}]}} 53 | 54 | :postgres 55 | {:feature-requires [:base] 56 | :actions 57 | {:injections [{:type :clj 58 | :path "src/clj/<>/core.clj" 59 | :action :append-requires 60 | :value ["[kit.edge.db.postgres]"]} 61 | {:type :edn 62 | :path "resources/system.edn" 63 | :target [] 64 | :action :merge 65 | :value {:db.sql/connection #profile 66 | {:dev {:jdbc-url "jdbc:postgresql://localhost:5432/<>_dev?user=<>&password=<>"} 67 | :test {:jdbc-url "jdbc:postgresql://localhost:5432/<>_test?user=<>&password=<>"} 68 | :prod {:jdbc-url #env JDBC_URL}}}} 69 | {:type :edn 70 | :path "deps.edn" 71 | :target [:deps] 72 | :action :merge 73 | :value {org.postgresql/postgresql {:mvn/version "42.3.4"} 74 | io.github.kit-clj/kit-postgres {:mvn/version "1.0.7"}}}]}}} 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Kit Modules 2 | 3 | Kit modules are templates that can be applied to an existing project using the [kit-generator](https://github.com/kit-clj/kit/tree/master/libs/kit-generator). In that, they are different from profiles, which you can apply only when creating a new project. 4 | 5 | ### Using Modules 6 | 7 | Kit embraces the REPL and the generator library is aliased in the `user` namespace as `kit`. Let's see how we can us it to install HTML module in the project. First, we need to sync our module repositories. This is done by running the following command in the REPL: 8 | 9 | ```clojure 10 | user=> (kit/sync-modules) 11 | :done 12 | user=> 13 | ``` 14 | 15 | Once the modules are synchronized, we can list the available modules by running `kit/list-modules`: 16 | 17 | ```clojure 18 | user=> (kit/list-modules) 19 | :kit/sql - adds support for SQL. Available profiles [ :postgres :sqlite ]. Default profile :sqlite 20 | :kit/html - adds support for HTML templating using Selmer 21 | :kit/ctmx - adds support for HTMX using CTMX 22 | :kit/sente - adds support for Sente websockets to cljs 23 | :kit/cljs - adds support for cljs using shadow-cljs 24 | :kit/metrics - adds support for metrics using prometheus through iapetos 25 | :kit/auth - adds support for auth middleware using Buddy 26 | :kit/nrepl - adds support for nREPL 27 | :kit/htmx - adds support for HTMX using hiccup 28 | :kit/hato - adds support for kit-Hato HTTP client 29 | :done 30 | user=> 31 | ``` 32 | 33 | We can see that the three modules specified in the official modules repository are now available for use. Let's install the HTML module by running `kit/install-module` function and passing it the keyword specifying the module name: 34 | 35 | ```clojure 36 | user=> (kit/install-module :kit/html) 37 | updating file: resources/system.edn 38 | updating file: deps.edn 39 | updating file: src/clj/kit/guestbook/core.clj 40 | applying 41 | action: :append-requires 42 | value: ["[kit.guestbook.web.routes.pages]"] 43 | :kit/html installed successfully! 44 | restart required! 45 | nil 46 | user=> 47 | ``` 48 | 49 | Let's restart the REPL and run `(go)` command again to start the application. We should now be able to navigate to `http://localhost:3000` and see the default HTML page provided by the module. 50 | 51 | Generator aims to be idempotent, and will err on the side of safety in case of conflicts. For example, if we attempt to install `:kit/html` module a second time then we'll see he following output: 52 | 53 | ```clojure 54 | user=> (kit/install-module :kit/html) 55 | :kit/html requires following modules: nil 56 | module :kit/html is already installed! 57 | nil 58 | user=> 59 | ``` 60 | 61 | Generator lets us know that the module already exists and there is nothing to be done. 62 | 63 | ### Creating Custom Modules 64 | 65 | Modules are managed using git repositories. You can find the official modules [here](https://github.com/kit-clj/modules). Let's take a brief look at what a module repository looks like. 66 | 67 | A module repository must contain a `modules.edn` file describing the modules that are provided. For example, here are the official modules provided by Kit: 68 | 69 | ```clojure 70 | {:name "kit-modules" 71 | :modules 72 | {:kit/html 73 | {:path "html" 74 | :doc "adds support for HTML templating using Selmer"} 75 | :kit/metrics 76 | {:path "metrics" 77 | :doc "adds support for metrics using prometheus through iapetos"} 78 | :kit/sql 79 | {:path "sql" 80 | :doc "adds support for SQL. Available profiles [ :postgres :sqlite ]. Default profile :sqlite"} 81 | :kit/cljs 82 | {:path "cljs" 83 | :doc "adds support for cljs using shadow-cljs"} 84 | :kit/nrepl 85 | {:path "nrepl" 86 | :doc "adds support for nREPL"}}} 87 | ``` 88 | 89 | As you can see above, the official repository contains five modules. Let's take a look at the [`:kit/html`](https://github.com/kit-clj/modules/tree/master/html) module to see how it works. This module contains a `config.edn` file and a folder called `assets`. It has the following configuration: 90 | 91 | ```clojure 92 | {:default 93 | {:require-restart? true 94 | :actions 95 | {:assets [["assets/home.html" "resources/html/home.html"] 96 | ["assets/error.html" "resources/html/error.html"] 97 | ["assets/css/screen.css" "resources/public/css/screen.css"] 98 | ["assets/img/kit.png" "resources/public/img/kit.png"] 99 | ["assets/src/pages.clj" "src/clj/<>/web/routes/pages.clj"] 100 | ["assets/src/layout.clj" "src/clj/<>/web/pages/layout.clj"]] 101 | :injections [{:type :edn 102 | :path "resources/system.edn" 103 | :target [] 104 | :action :merge 105 | :value {:reitit.routes/pages 106 | {:base-path "" 107 | :env #ig/ref :system/env}}} 108 | {:type :edn 109 | :path "deps.edn" 110 | :target [:deps] 111 | :action :merge 112 | :value {selmer/selmer {:mvn/version "1.12.49"} 113 | luminus/ring-ttl-session {:mvn/version "0.3.3"}}} 114 | {:type :clj 115 | :path "src/clj/<>/core.clj" 116 | :action :append-requires 117 | :value ["[<>.web.routes.pages]"]}]}}} 118 | ``` 119 | 120 | We can see that the module has a `:default` profile. Kit module profiles allow us to provide variations of a module with different configurations. For example, a database module could have different profiles for different types of databases. In case of HTML, we only need a single profile. 121 | 122 | The`:require-restart?` key specifies that the runtime needs to be restarted for changes to take effect. This is necessary for modules that add Maven dependencies necessitating JVM restarts to be loaded. 123 | 124 | Next, the module specifies the actions that will be performed. The first action, called `:assets`, specifies new assets that will be added to the project. These are template files that will be read from the `assets` folder and injected in the project. 125 | 126 | The other configuration action is called `:injections` and specifies code that will be injected into existing files within the project. In order to provide support for rendering HTML templates, the module must update Integrant system configuration by adding a reference for new routes to `system.edn`, add new dependencies to `deps.edn`, and finally require the namespace that contains the routes for the pages in the core namespace of the project. The `:action` values in injections depend on the types of assets being manipulated. 127 | 128 | `:edn` injections 129 | 130 | * `:append` - appends the value at the specified path, the value at the path is assumed to be a collection 131 | 132 | ```clojure 133 | {:type :edn 134 | :path "deps.edn" 135 | :target [:paths] 136 | :action :append 137 | :value "target/classes/cljsbuild"} 138 | ``` 139 | 140 | * `:merge` - merges value with the value found at the path, the value at the path is assumed to be a map 141 | 142 | ```clojure 143 | {:type :edn 144 | :path "deps.edn" 145 | :target [:deps] ; use [] to merge with the top level map 146 | :action :merge 147 | :value {selmer/selmer {:mvn/version "1.12.49"} 148 | luminus/ring-ttl-session {:mvn/version "0.3.3"}}} 149 | ``` 150 | 151 | `:clj` injections 152 | 153 | * `:append-requires` - appends a require in the specified namespace 154 | 155 | ```clojure 156 | {:type :clj 157 | :path "src/clj/<>/core.clj" 158 | :action :append-requires 159 | :value ["[<>.web.routes.pages]"]} 160 | ``` 161 | 162 | * `:append-build-task` - appends a build task in `build.clj` 163 | 164 | ```clojure 165 | {:type :clj 166 | :path "build.clj" 167 | :action :append-build-task 168 | :value (defn build-cljs [] 169 | (println "npx shadow-cljs release app...") 170 | (let [{:keys [exit] 171 | :as s} (sh "npx" "shadow-cljs" "release" "app")] 172 | (when-not (zero? exit) 173 | (throw (ex-info "could not compile cljs" s)))))} 174 | ``` 175 | 176 | * `:append-build-task-call` - appends a function call to the `uber` function in `build.clj` 177 | 178 | ```clojure 179 | {:type :clj 180 | :path "build.clj" 181 | :action :append-build-task-call 182 | :value (build-cljs)} 183 | ``` 184 | 185 | HTML injections 186 | 187 | * `:append` - appends a Hiccup form to the target identified by enlive selectors in the specified HTML resource 188 | 189 | ```clojure 190 | {:type :html 191 | :path "resources/html/home.html" 192 | :action :append 193 | :target [:body] 194 | :value [:div {:id "app"}]} 195 | ``` 196 | --------------------------------------------------------------------------------