├── .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 |
--------------------------------------------------------------------------------