Skip to content

Commit

Permalink
Optional Tailwind CSS Compilation for Clerk's static build (#246)
Browse files Browse the repository at this point in the history
Add flag to for compiling viewer stylesheets with tailwind into an optimized CSS file added to the static build.
  • Loading branch information
zampino committed Oct 27, 2022
1 parent 15bd34b commit 4c17b0f
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 22 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ jobs:
- name: 🔧 Setup Google Cloud SDK
uses: google-github-actions/setup-gcloud@v0.3.0

- name: 🔧 NPM install
run: npm install

- name: 📓 Build Clerk Book
run: clojure -J-Dclojure.main.report=stderr -X:demo:nextjournal/clerk :git/sha '"${{ github.sha }}"' :git/url '"https://github.com/nextjournal/clerk"' :paths '["book.clj"]'

Expand Down
4 changes: 3 additions & 1 deletion deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
org.babashka/cli {:mvn/version "0.5.40"}}
:extra-paths ["notebooks"]
:exec-fn nextjournal.clerk/build!
:exec-args {:paths-fn nextjournal.clerk.builder/clerk-docs}
:exec-args {:paths-fn nextjournal.clerk.builder/clerk-docs
:compile-css true}
:main-opts ["-m" "babashka.cli.exec"]
:jvm-opts ["-Dclojure.main.report=stdout"]
:nextjournal.clerk/aliases [:demo]}
:sci {:extra-deps {applied-science/js-interop {:mvn/version "0.3.3"}
org.babashka/sci {:mvn/version "0.5.34"}
Expand Down
5 changes: 1 addition & 4 deletions notebooks/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@ and render back to hiccup with customisable elements.
(def renderers
(assoc md.transform/default-hiccup-renderers
:doc (partial md.transform/into-markup [:div.viewer-markdown])
:ruler (fn [_ _]
[:hr.mt-1.mb-10
{:style {:border "10px solid magenta"
:border-radius "10px"}}])))
:ruler (constantly [:hr.mt-1.mb-10.border-0.w-full.h-5.bg-fuchsia-900.rounded-full])))

(def hiccup
(md.transform/->hiccup renderers sliced))
Expand Down
2 changes: 1 addition & 1 deletion resources/stylesheets/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
darkMode: "class",
content: ["./public/build/index.html", "./public/build/**/*.html", "./build/viewer.js"],
content: ["./tw/viewer.js", "./tw/**/*.edn"],
safelist: ['dark'],
theme: {
extend: {},
Expand Down
2 changes: 1 addition & 1 deletion resources/viewer-js-hash
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3CdrarFnse5u5D3b8hfjwcAa6Npa
3qePCgpcmuVcJNqR8mK6LSfWdRT2
13 changes: 10 additions & 3 deletions src/nextjournal/clerk.clj
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@

(defn ^:private normalize-opts [opts]
(set/rename-keys opts #_(into {} (map (juxt identity #(keyword (str (name %) "?")))) [:bundle :browse :dashboard])
{:bundle :bundle?, :browse :browse?, :dashboard :dashboard?}))
{:bundle :bundle?, :browse :browse?, :dashboard :dashboard? :compile-css :compile-css?}))

(defn ^:private started-via-bb-cli? [opts]
(contains? (meta opts) :org.babashka/cli))
Expand Down Expand Up @@ -438,8 +438,15 @@
:dashboard {:desc "Flag to serve a dashboard with the build progress."}
:out-path {:desc "Path to an build output folder, defaults to \"public/build\"."}
:git/sha {:desc "Git sha to use for the backlink."}
:git/url {:desc "Git url to use for the backlink."}}
:order [:paths :paths-fn :index :browse :bundle :dashbaord :out-path :git/sha :git/url]}}
:git/url {:desc "Git url to use for the backlink."}
:compile-css {:desc "Flag to compile all viewer stylesheets and add a minimized CSS file to the output folder.
Assumes tailwind installed, as per
```
npm install -D tailwindcss @tailwindcss/typography
```
"}}
:order [:paths :paths-fn :index :browse :bundle :dashbaord :out-path :git/sha :git/url :compile-css]}}
[build-opts]
(if (:help build-opts)
(if-let [format-opts (and (started-via-bb-cli? build-opts) (requiring-resolve 'babashka.cli/format-opts))]
Expand Down
60 changes: 58 additions & 2 deletions src/nextjournal/clerk/builder.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
(:require [babashka.fs :as fs]
[clojure.java.browse :as browse]
[clojure.set :as set]
[clojure.java.io :as io]
[clojure.string :as str]
[clojure.java.shell :refer [sh]]
[nextjournal.clerk.analyzer :as analyzer]
[nextjournal.clerk.builder-ui :as builder-ui]
[nextjournal.clerk.eval :as eval]
[nextjournal.clerk.parser :as parser]
[nextjournal.clerk.view :as view]
[nextjournal.clerk.webserver :as webserver]))
[nextjournal.clerk.webserver :as webserver]
[nextjournal.clerk.config :as config]))

(def clerk-docs
(into ["CHANGELOG.md"
Expand Down Expand Up @@ -78,6 +81,7 @@
(str "Errored in " duration ". ❌\n")
(str "Done in " duration ". ✅\n"))
:building (str "🔨 Building \"" (:file doc) "\"")
:compiling-css "🎨 Compiling CSS… "
:downloading-cache (str "⏬ Downloading distributed cache… ")
:uploading-cache (str "⏫ Uploading distributed cache… ")
:finished (str "📦 Static app bundle created in " duration ". Total build time was " (-> event :total-duration format-duration) ".\n"))))
Expand Down Expand Up @@ -183,8 +187,53 @@
(expand-paths {:paths-fn `my-paths}))
#_(expand-paths {:paths ["notebooks/viewers**"]})

(defn compile-css!
"Compiles a minimal tailwind css stylesheet with only the used styles included, replaces the generated stylesheet link in html pages."
[{:keys [out-path]} docs]
(assert (and (= 0 (:exit (sh "which" "npx"))) (= 0 (:exit (sh "npx" "tailwindcss"))))
"Clerk's CSS optimizaiton failed: node and tailwind need to be installed. Please run `npm install -D tailwindcss @tailwindcss/typography` and retry.")
(let [tw-folder (fs/create-dirs "tw")
tw-input (str tw-folder "/input.css")
tw-config (str tw-folder "/tailwind.config.cjs")
tw-output (str tw-folder "/viewer.css")
tw-viewer (str tw-folder "/viewer.js")]
(spit tw-config (slurp (io/resource "stylesheets/tailwind.config.js")))
;; NOTE: a .cjs extension is safer in case the current npm project is of type module (like Clerk's): in this case all .js files
;; are treated as ES modules and this is not the case of our tw config.
(spit tw-input (slurp (io/resource "stylesheets/viewer.css")))
(spit tw-viewer (slurp (get @config/!resource->url "/js/viewer.js")))
(doseq [{:keys [file viewer]} docs]
(spit (let [path (fs/path tw-folder (str/replace file #"\.(cljc?|md)$" ".edn"))]
(fs/create-dirs (fs/parent path))
(str path))
(pr-str viewer)))
(let [{:as ret :keys [out err exit]}
(sh "npx" "tailwindcss"
"--input" tw-input
"--config" tw-config
;; FIXME: pass inline
;;"--content" (str tw-viewer)
;;"--content" (str tw-folder "/**/*.edn")
"--output" tw-output
"--minify")]
(when-not (= 0 exit)
(throw (ex-info (str "Clerk build! failed\n" out "\n" err) ret))))
(let [content-addressed (fs/file "_data" (str (analyzer/valuehash (slurp tw-output)) ".css"))]
(fs/create-dirs (fs/parent (fs/file out-path content-addressed)))
(when-not (fs/exists? (fs/file out-path content-addressed))
(fs/copy tw-output (fs/file out-path content-addressed)))
(swap! config/!resource->url assoc "/css/viewer.css" (str content-addressed))
;; cleanup
(fs/delete-tree tw-folder))))

#_(fs/delete-tree "public/build")
#_(build-static-app! {:paths ["notebooks/hello.clj" "notebooks/markdown.md" "notebooks/viewers/image.clj"]
:bundle? false
:compile-css? true})

(defn build-static-app! [opts]
(let [{:as opts :keys [paths download-cache-fn upload-cache-fn bundle? report-fn]} (process-build-opts opts)
(let [{:as opts :keys [download-cache-fn upload-cache-fn report-fn compile-css?]}
(process-build-opts opts)
{:keys [expanded-paths error]} (try {:expanded-paths (expand-paths opts)}
(catch Exception e
{:error e}))
Expand Down Expand Up @@ -223,6 +272,10 @@
result)) state (range))
_ (when-let [first-error (some :error state)]
(throw first-error))
_ (when compile-css?
(report-fn {:stage :compiling-css})
(let [{duration :time-ms} (eval/time-ms (compile-css! opts state))]
(report-fn {:stage :done :duration duration})))
{state :result duration :time-ms} (eval/time-ms (write-static-app! opts state))]
(when upload-cache-fn
(report-fn {:stage :uploading-cache})
Expand All @@ -235,3 +288,6 @@
#_(build-static-app! {:paths ["index.clj" "notebooks/rule_30.clj" "notebooks/markdown.md"] :bundle? false :browse? false})
#_(build-static-app! {:paths ["notebooks/viewers/**"]})
#_(build-static-app! {:index "notebooks/rule_30.clj" :git/sha "bd85a3de12d34a0622eb5b94d82c9e73b95412d1" :git/url "https://github.com/nextjournal/clerk"})
#_(swap! config/!resource->url assoc
"/js/viewer.js" (-> config/lookup-url slurp clojure.edn/read-string (get "/js/viewer.js")))
#_(swap! config/!resource->url dissoc "/css/viewer.css")
33 changes: 23 additions & 10 deletions src/nextjournal/clerk/view.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,44 @@
[nextjournal.clerk.viewer :as v]
[hiccup.page :as hiccup]
[clojure.string :as str]
[clojure.java.io :as io]))
[clojure.java.io :as io])
(:import (java.net URI)))

(defn doc->viewer
([doc] (doc->viewer {} doc))
([opts {:as doc :keys [ns file]}]
(binding [*ns* ns]
(-> (merge doc opts) v/notebook v/present))))


#_(doc->viewer (nextjournal.clerk/eval-file "notebooks/hello.clj"))
#_(nextjournal.clerk/show! "notebooks/test.clj")
#_(nextjournal.clerk/show! "notebooks/visibility.clj")

(defn include-viewer-css []
(defn relative? [url]
(and (not (.isAbsolute (URI. url)))
(not (str/starts-with? url "/"))))
#_ (relative? "/hello/css")
#_ (relative? "hello/css")
#_ (relative? "https://cdn.stylesheet.css")

(defn relativize [url current-path]
(str (str/join (repeat (get (frequencies current-path) \/ 0) "../"))
url))

(defn include-viewer-css [{:keys [current-path]}]
(if-let [css-url (@config/!resource->url "/css/viewer.css")]
(hiccup/include-css css-url)
(hiccup/include-css (cond-> css-url
(and current-path (relative? css-url))
(relativize current-path)))
(list (hiccup/include-js "https://cdn.tailwindcss.com?plugins=typography")
[:script (-> (slurp (io/resource "stylesheets/tailwind.config.js"))
(str/replace #"^module.exports" "tailwind.config")
(str/replace #"require\(.*\)" ""))]
(str/replace #"^module.exports" "tailwind.config")
(str/replace #"require\(.*\)" ""))]
[:style {:type "text/tailwindcss"} (slurp (io/resource "stylesheets/viewer.css"))])))

(defn include-css+js []
(defn include-css+js [state]
(list
(include-viewer-css)
(include-viewer-css state)
[:script {:type "module" :src (@config/!resource->url "/js/viewer.js")}]
(hiccup/include-css "https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css")
(hiccup/include-css "https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&family=Fira+Mono:wght@400;700&family=Fira+Sans+Condensed:ital,wght@0,700;1,700&family=Fira+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&family=PT+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap")))
Expand All @@ -38,7 +51,7 @@
[:head
[:meta {:charset "UTF-8"}]
[:meta {:name "viewport" :content "width=device-width, initial-scale=1"}]
(include-css+js)]
(include-css+js state)]
[:body.dark:bg-gray-900
[:div#clerk]
[:script {:type "module"} "let viewer = nextjournal.clerk.sci_env
Expand All @@ -58,7 +71,7 @@ window.ws_send = msg => ws.send(msg)")]]))
[:meta {:charset "UTF-8"}]
[:meta {:name "viewport" :content "width=device-width, initial-scale=1"}]
(when current-path (v/open-graph-metas (-> state :path->doc (get current-path) v/->value :open-graph)))
(include-css+js)]
(include-css+js state)]
[:body
[:div#clerk-static-app]
[:script {:type "module"} "let viewer = nextjournal.clerk.sci_env
Expand Down

0 comments on commit 4c17b0f

Please sign in to comment.