├── .gitignore ├── LICENSE ├── README.md ├── bb.edn ├── deps.edn ├── package.json ├── resources ├── public │ └── index.html └── tw │ └── style.css ├── shadow-cljs.edn ├── src ├── cljs_reframe_template │ ├── config.cljs │ ├── core.cljs │ ├── db.cljs │ ├── routes.cljs │ ├── styles.cljs │ ├── use_cases │ │ └── core_cases.cljs │ └── views │ │ ├── compo.cljs │ │ └── home.cljs └── tools │ ├── reframetools.cljs │ └── viewtools.cljs └── tailwind.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | /*.log 2 | /target 3 | /*-init.clj 4 | /resources/public/js/compiled 5 | out 6 | .nrepl-port 7 | .rebel_readline_history 8 | 9 | /resources/public/js 10 | /node_modules 11 | package-lock.json 12 | /.shadow-cljs 13 | /.cpcache 14 | /.clj-kondo 15 | /.lsp 16 | /.calva 17 | /.vscode 18 | /resources/public/app.css -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ulf Ninow 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cljs-reframe-template 2 | 3 | A starter https://github.com/Day8/re-frame[re-frame] application. 4 | 5 | frontend routing with https://github.com/metosin/reitit[reitit] 6 | 7 | dev tooling with shadow-cljs 8 | 9 | styles per inline injection with https://github.com/noprompt/garden[CSS Garden] 10 | 11 | styles via TailwindCSS https://tailwindcss.com/ 12 | 13 | ## Development Mode 14 | 15 | 16 | ```bash 17 | npx shadow-cljs watch app 18 | ``` 19 | 20 | 21 | ## Compile application 22 | 23 | ```bash 24 | npx shadow-cljs compile app 25 | ``` 26 | 27 | ```bash 28 | npx shadow-cljs release app 29 | ``` 30 | 31 | ## CSS via Tailwind 32 | 33 | - CSS artifacts are compiled 34 | - via Tailwind's new JIT Engine 35 | - using https://github.com/teknql/shadow-cljs-tailwind-jit 36 | - so one can use 37 | - build hooks 38 | - inside Shadow-Cljs projects 39 | 40 | ## manually purge CSS 41 | 42 | ```bash 43 | npm run-script tw 44 | ``` 45 | 46 | ## Run application: 47 | 48 | Wait a bit, then browse to http://localhost:8021. 49 | 50 | ## Using babashka 51 | 52 | https://github.com/babashka/babashka[babashka] 53 | 54 | - use of the new babashka tasks feature 55 | - config via bb.edn 56 | 57 | ```bash 58 | bb tasks 59 | ``` 60 | 61 | ```bash 62 | bb watch 63 | bb compile 64 | bb release 65 | bb purgecss 66 | bb buildreport 67 | ``` 68 | 69 | -------------------------------------------------------------------------------- /bb.edn: -------------------------------------------------------------------------------- 1 | { 2 | :min-bb-version "0.3.6" 3 | :tasks 4 | { 5 | :init (do 6 | ;;"npx shadow-cljs " 7 | (defn run-shadow [args] (shell (str "pnpm exec shadow-cljs " args))) 8 | ;;"npm run " 9 | (defn run-srcpt [args] (shell (str "pnpm run " args))) 10 | ;;"npx " 11 | (defn run-dlx [args] (shell (str "pnpm dlx " args)))) 12 | watch {:task (run-shadow "watch app")} 13 | compile {:task (run-shadow "compile app")} 14 | release {:task (run-shadow "release app")} 15 | ;;updates 16 | update-npm {:task (run-dlx "npm-check-updates -u")} 17 | update-deps {:task (shell "clojure -Moutdated --upgrade")} 18 | ;;tailwind 19 | purgecss {:task (run-srcpt "tw")} 20 | ;;shadow report 21 | buildreport {:task (let [date (java.time.LocalDateTime/now) 22 | formatter (java.time.format.DateTimeFormatter/ofPattern "yyyy-MM-dd") 23 | file (str "buildreport_" (.format date formatter) ".html")] 24 | (run-shadow (str "run shadow.cljs.build-report app " file)))} 25 | 26 | ,}} 27 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:aliases {:test {:extra-paths ["test"], 2 | :extra-deps {org.clojure/test.check {:mvn/version "RELEASE"}}}, 3 | :runner {:extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner", 4 | :sha "9d36f36ff541dac680a05010e4348c744333f191"}}, 5 | :main-opts ["-m" "cognitect.test-runner" "-d" "test"]}, 6 | :dev {:extra-deps { 7 | com.teknql/shadow-cljs-tailwind-jit {:mvn/version "1.0.0"} 8 | binaryage/devtools {:mvn/version "1.0.7"}, 9 | day8.re-frame/re-frame-10x {:mvn/version "1.9.9"}} 10 | :extra-paths ["src" "env/dev" "target"]} 11 | :outdated {:extra-deps {com.github.liquidz/antq {:mvn/version "RELEASE"}} 12 | :main-opts ["-m" "antq.core"]}}, 13 | :deps {org.clojure/clojure {:mvn/version "RELEASE"}, 14 | reagent/reagent {:mvn/version "1.2.0"}, 15 | ns-tracker/ns-tracker {:mvn/version "1.0.0"}, 16 | re-pressed/re-pressed {:mvn/version "0.3.2" 17 | :exclusions [re-frame/re-frame 18 | reagent/reagent]}, 19 | ;;garden/garden {:mvn/version "1.3.10"}, 20 | metosin/reitit-schema {:mvn/version "0.7.2"}, 21 | metosin/reitit-frontend {:mvn/version "0.7.2"}, 22 | metosin/reitit {:mvn/version "0.7.2"}, 23 | re-frame/re-frame {:mvn/version "1.4.3"}, 24 | thheller/shadow-cljs {:mvn/version "2.28.16"}}, 25 | :paths ["src" "resources" "test/cljs"]} 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "tw": "npx tailwind -i resources/tw/style.css -o resources/public/app.css", 4 | "watch:css": "npx tailwind -i resources/tw/style.css -o resources/public/app.css -w" 5 | }, 6 | "dependencies": { 7 | "highlight.js": "11.10.0", 8 | "react": "^18.3.1", 9 | "react-dom": "^18.3.1", 10 | "scheduler": "^0.23.2" 11 | }, 12 | "devDependencies": { 13 | "autoprefixer": "^10.4.20", 14 | "shadow-cljs": "^2.28.16", 15 | "tailwindcss": "^3.4.13" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Example Project 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /resources/tw/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | {:deps {:aliases [:dev :test]} 2 | 3 | :nrepl {:port 7002} 4 | :builds {:app {:target :browser 5 | :asset-path "js" 6 | :output-dir "resources/public/js" 7 | :dev {:build-hooks [(teknql.tailwind/start-watch!)]} 8 | :release {:build-hooks [(teknql.tailwind/compile-release!)]} 9 | :tailwind/output "resources/public/app.css" 10 | :tailwind/files {:base-path "." 11 | :tailwind.css "./resources/tw/style.css"} 12 | :compiler-options {:infer-externs :auto} 13 | :modules { 14 | :main {:entries [cljs-reframe-template.core] 15 | :init-fn cljs-reframe-template.core/init}} 16 | 17 | 18 | :devtools { 19 | :http-port 8021 20 | :http-root "resources/public/" 21 | :after-load cljs-reframe-template.core/re-render 22 | :preloads [devtools.preload shadow.remote.runtime.cljs.browser]}}}} 23 | -------------------------------------------------------------------------------- /src/cljs_reframe_template/config.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-reframe-template.config) 2 | 3 | (def debug? 4 | ^boolean goog.DEBUG) 5 | -------------------------------------------------------------------------------- /src/cljs_reframe_template/core.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-hooks cljs-reframe-template.core 2 | (:require [cljs-reframe-template.config :as config] 3 | [cljs-reframe-template.routes :as routes] 4 | [cljs-reframe-template.styles :as styl] 5 | [cljs-reframe-template.use-cases.core-cases :as ccases] 6 | [cljs-reframe-template.views.home :as views] 7 | [goog.dom :as gdom] 8 | [react :as react] 9 | [re-frame.core :as re-frame] 10 | [reagent.dom.client :as rdc])) 11 | 12 | 13 | 14 | (defn dev-setup [] 15 | (when config/debug? 16 | (enable-console-print!) 17 | (println "dev mode"))) 18 | 19 | (defonce root (rdc/create-root (gdom/getElement "app"))) 20 | 21 | (defn mount-root [] 22 | (println "mount") 23 | (re-frame/clear-subscription-cache!) 24 | (styl/inject-trace-styles js/document) 25 | (rdc/render root [:> react/StrictMode {} [#'views/main-panel]])) 26 | 27 | (defn ^:after-load re-render [] 28 | (mount-root)) 29 | 30 | (defn ^:export init [] 31 | (println "init again..") 32 | (re-frame/dispatch-sync [::ccases/initialize-db]) 33 | (dev-setup) 34 | (routes/app-routes) 35 | 36 | (mount-root)) -------------------------------------------------------------------------------- /src/cljs_reframe_template/db.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-reframe-template.db) 2 | 3 | (def default-db 4 | {:name "re-frame"}) 5 | -------------------------------------------------------------------------------- /src/cljs_reframe_template/routes.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-reframe-template.routes 2 | (:require 3 | [re-frame.core :as rf] 4 | [reitit.frontend :as rtf] 5 | [reitit.frontend.easy :as rtfe] 6 | [reitit.coercion.schema :as rsc] 7 | [cljs-reframe-template.views.home :as home] 8 | [cljs-reframe-template.views.compo :as compo] 9 | [tools.reframetools :refer [sdb gdb]])) 10 | 11 | ;;https://clojure.org/guides/weird_characters#__code_code_var_quote 12 | (def routes 13 | (rtf/router 14 | ["/" 15 | ["" 16 | {:name :routes/#frontpage 17 | :view #'home/main}] 18 | ["component" 19 | {:name :routes/#component 20 | :view #'compo/main}]] 21 | 22 | {:data {:coercion rsc/coercion}})) 23 | 24 | (defn on-navigate [new-match] 25 | (when new-match 26 | (rf/dispatch [:routes/navigated new-match]))) 27 | 28 | (defn app-routes [] 29 | (rtfe/start! routes 30 | on-navigate 31 | {:use-fragment true})) 32 | 33 | (rf/reg-sub 34 | :routes/current-route 35 | (gdb [:current-route])) 36 | 37 | ;;; Events 38 | (rf/reg-event-db 39 | :routes/navigated 40 | (sdb [:current-route])) 41 | 42 | (rf/reg-event-fx 43 | :routes/navigate 44 | (fn [_cofx [_ & route]] 45 | {:routes/navigate! route})) 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/cljs_reframe_template/styles.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-reframe-template.styles 2 | (:require 3 | [goog.dom :as gdom] 4 | ;;[garden.core :as g] 5 | )) 6 | 7 | (def styles 8 | []) 9 | 10 | (defn inject-node! [old-node new-node document] 11 | (if old-node 12 | (gdom/replaceNode new-node old-node) 13 | (gdom/appendChild (.-head document) new-node))) 14 | 15 | (defn inject-inline-style [document id style] 16 | (let [old-style (gdom/getElement id) 17 | new-style (gdom/createDom "style" 18 | (clj->js {:type "text/css" 19 | :id id}) 20 | style)] 21 | 22 | (inject-node! old-style new-style document))) 23 | 24 | (defn inject-inline-link [document id link] 25 | (let [old-link (gdom/getElement id) 26 | new-link (gdom/createDom "link" 27 | (clj->js {:id id 28 | :rel :stylesheet 29 | :href link}))] 30 | 31 | (inject-node! old-link new-link document))) 32 | 33 | 34 | (defn inject-trace-styles [document] 35 | ;;(inject-inline-style document "--reframe-template--" (apply g/css styles)) 36 | (inject-inline-link document "--app.css--" "app.css")) 37 | 38 | -------------------------------------------------------------------------------- /src/cljs_reframe_template/use_cases/core_cases.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-reframe-template.use-cases.core-cases 2 | (:require 3 | [re-frame.core :as re-frame] 4 | [cljs-reframe-template.db :as db] 5 | [tools.reframetools :refer [sdb gdb]])) 6 | ;[day8.re-frame.tracing :refer-macros [fn-traced defn-traced]])) 7 | 8 | 9 | 10 | (re-frame/reg-sub ::name (gdb [:name])) 11 | (re-frame/reg-sub ::re-pressed-example (gdb [:re-pressed-example])) 12 | 13 | (re-frame/reg-event-db ::initialize-db (constantly db/default-db)) 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/cljs_reframe_template/views/compo.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-reframe-template.views.compo) 2 | 3 | (defn main [] 4 | [:div 5 | {:class ["h-[220px]" "w-[300px]" :bg-red-50 :m-4 :p-2 "rounded-[5px]"]} 6 | [:h2.text-4xl "Component"] 7 | [:div "red room"]]) 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/cljs_reframe_template/views/home.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-reframe-template.views.home 2 | (:require 3 | [re-frame.core :as re-frame] 4 | [tools.viewtools :as vt] 5 | [cljs.pprint :as pp])) 6 | 7 | 8 | (defn main [] 9 | [:div 10 | {:class ["h-[220px]" "w-[300px]" :bg-blue-50 :m-4 :p-2 "rounded-[5px]"]} 11 | [:h2.text-4xl "home"] 12 | [:p "nothing to see here"]]) 13 | 14 | (def toolbar-items 15 | [ 16 | ["#" :routes/#frontpage] 17 | ["component" :routes/#component]]) 18 | 19 | (defn route-info [route] 20 | [:div.m-4 21 | [:p "Routeinfo"] 22 | [:pre.border-solid.border-2.rounded 23 | (with-out-str (pp/pprint route))]]) 24 | ;; main 25 | 26 | (defn show-panel [route] 27 | (when-let [route-data (:data route)] 28 | (let [view (:view route-data)] 29 | [:<> 30 | [view] 31 | [route-info route]]))) 32 | 33 | (defn main-panel [] 34 | (let [active-route (re-frame/subscribe [:routes/current-route])] 35 | [:div 36 | [vt/navigation toolbar-items] 37 | [show-panel @active-route]])) 38 | -------------------------------------------------------------------------------- /src/tools/reframetools.cljs: -------------------------------------------------------------------------------- 1 | (ns tools.reframetools) 2 | 3 | (defn sdb [path] 4 | (fn [db [_ v]] 5 | (assoc-in db path v))) 6 | 7 | (defn gdb 8 | [path] 9 | (fn [db _] (get-in db path))) -------------------------------------------------------------------------------- /src/tools/viewtools.cljs: -------------------------------------------------------------------------------- 1 | (ns tools.viewtools 2 | (:require [reitit.frontend.easy :as rtfe])) 3 | 4 | 5 | (defn item [e] 6 | (cond 7 | (fn? e) [e] 8 | (vector? e) e 9 | (string? e) [:h2 e])) 10 | 11 | (defn panel [name component] 12 | [:div 13 | 14 | [item name] 15 | [item component]]) 16 | 17 | ;; navigation tools 18 | (defn sep [] 19 | [:span " | "]) 20 | 21 | (defn nav-item [i] 22 | (if (= :sep i) 23 | [sep] 24 | [:a.text-blue-700 25 | {:href (rtfe/href (second i))} (first i)])) 26 | 27 | (defn navigation [routes] 28 | (let [coll (->> routes (interpose :sep) (map-indexed vector))] 29 | [:div 30 | (for [[idx rt] coll] 31 | ^{:key (str idx)} [nav-item rt])])) -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | './resources/**/*.html', 4 | './src/**/*.cljs' 5 | ], 6 | theme: { 7 | extend: {}, 8 | }, 9 | variants: {}, 10 | plugins: [], 11 | } 12 | --------------------------------------------------------------------------------