core.clj (6295B)
1 (ns core 2 (:require [hiccup2.core :as h] 3 [clojure.java.io :as io] 4 [clojure.string :as cstr] 5 [clojure.java.shell :as shell])) 6 7 (defn prnt [level emoji & words] 8 (println (apply str 9 (apply str (repeat level "\t")) 10 " " 11 emoji 12 "\t" 13 words))) 14 15 (def VERBOSE (boolean (System/getenv "MW_VERBOSE"))) 16 (def WEB_ROOT (or (System/getenv "WEB_ROOT") "")) 17 18 (defn without-index [path-vec] 19 (if (= (last path-vec) "index.html") 20 (butlast path-vec) 21 path-vec)) 22 23 (defn dev? 24 ([] (dev? (or (System/getenv "MW_ENV") ""))) 25 ([mw-env] (= "DEV" (cstr/upper-case mw-env)))) 26 27 (defmacro if-dev [a b] 28 `(if (dev?) 29 ~a 30 ~b)) 31 32 (defn mk-path-fns [domain getspec] 33 (letfn [(p 34 ([] (p "" (dev?))) 35 ([rest-str] 36 (p rest-str (dev?))) 37 ([rest-str is-dev] 38 (str (if is-dev 39 (str "/" domain rest-str) 40 rest-str)))) 41 (network-path 42 ([pg-key-or-spec] 43 (network-path pg-key-or-spec (dev?))) 44 ([pg-key-or-spec is-dev] 45 (->> pg-key-or-spec 46 getspec 47 :path 48 (into (if is-dev ["localhost:8080" domain] [domain])) 49 without-index 50 (cstr/join "/")))) 51 (url 52 ([pg-key-or-spec] (url pg-key-or-spec (dev?))) 53 ([pg-key-or-spec is-dev] 54 (str "https://" (network-path pg-key-or-spec is-dev)))) 55 (path 56 ([pg-key-or-spec] 57 (path pg-key-or-spec (dev?))) 58 ([pg-key-or-spec is-dev] 59 (->> pg-key-or-spec 60 getspec 61 :path 62 without-index 63 (into (if is-dev [domain] [])) 64 (cstr/join "/") 65 (str "/")))) 66 (outfile [pg-key-or-spec] 67 (->> pg-key-or-spec 68 getspec 69 :path 70 (into ["build" domain]) 71 (cstr/join "/")))] 72 (fn [& args] 73 (let [arg1 (first args) 74 fns {:network-path network-path 75 :url url 76 :out outfile 77 :path path} 78 result (cond 79 (some #(= % arg1) (keys fns)) 80 (apply (arg1 fns) (rest args)) 81 82 (string? arg1) 83 (apply p args) 84 85 (empty? args) 86 (p) 87 88 :else 89 (throw (Exception. (str "⁉ invalid path call with args " args))))] 90 (when VERBOSE 91 (println "DEBUG: p" result "<-" args)) 92 93 result)))) 94 95 (defn p [path] (str WEB_ROOT path)) 96 (defn mk-p [domain] (if (= (clojure.string/upper-case (or (System/getenv "MW_ENV") "")) "DEV") 97 (fn [path] (str "/" domain path)) 98 identity)) 99 100 (defn base-head-stuff [url {:keys [title desc img]}] 101 (list [:meta {:charset "UTF-8"}] 102 [:meta {:name "viewport" 103 :content "width=device-width" 104 :initial-screen-scale "1"}] 105 [:title title] 106 [:script {:type "module"} (h/raw "document.documentElement.classList.remove('no-js'); 107 document.documentElement.classList.add('js');")] 108 [:meta {:name "description" :content desc}] 109 [:meta {:property "og:title" :content title}] 110 [:meta {:property "og:image" :content (get img :src)}] 111 [:meta {:property "og:image:alt" :content (get img :desc)}] 112 [:meta {:property "og:locale" :content "en_US"}] 113 [:meta {:property "og:type" :content "website"}] 114 [:meta {:property "og:url" :content url}] 115 [:meta {:name "theme-color" :content "#000033"}] 116 [:link {:rel "canonical" :href url}])) 117 118 ;; todo figure out how to thread project-specific p stuff into this. 119 (defn massi-world-domain-stuff [url p {:or {img {:src "https://massi.world/shared/assets/icon-180-180.png" 120 :desc "an undiscovered planet."}} 121 :as argmap}] 122 (concat 123 (base-head-stuff url argmap) 124 (list 125 [:link {:rel "shortcut icon" :href (p "/favicon.ico")}] 126 [:link {:rel "icon" :type "image/png" :sizes "16x16" :href (p "/shared/assets/icon-16-16.png")}] 127 [:link {:rel "icon" :type "image/png" :sizes "32x32" :href (p "/shared/assets/icon-32-32.png")}] 128 [:link {:rel "icon" :type "image/png" :sizes "48x48" :href (p "/shared/assets/icon-48-48.png")}] 129 [:link {:rel "apple-touch-icon" :href (p "/shared/assets/icon-180-180.png")}]))) 130 131 (def linkback-footer 132 (list [:footer "made by " [:a {:href "https://massi.world"} "massi"]])) 133 134 (defn fill-templ [templstr hole-map] 135 (clojure.string/replace 136 templstr 137 #"@\(([^\s\)]+)\)" 138 (fn [match] 139 (let [hole-filler (get hole-map (keyword (second match)))] 140 (cond 141 (fn? hole-filler) (hole-filler) 142 (or (seq? hole-filler) (vector? hole-filler)) (str (h/html hole-filler)) 143 (nil? hole-filler) (throw (Exception. (str "No filler for hole " (second match)))) 144 true hole-filler))))) 145 146 (defn process-templ [in-file out-file hole-map] 147 (if (or (not (string? in-file)) 148 (= in-file out-file)) 149 (throw (Exception. "unable to process template. either in/out files not specified or they're the same. which would be weird."))) 150 (let [out-str (fill-templ (slurp in-file) hole-map)] 151 (if out-file 152 (spit out-file out-str) 153 out-str))) 154 155 (defn md-hole [hole-map] 156 (fn [text state] 157 [(fill-templ text hole-map) state])) 158 159 (defn md-to-html [in-str] 160 (:out (shell/sh "bin/marked-custom.js" :in in-str))) 161 162 (defn md 163 ([file] (md file nil)) 164 ([file hole-fillers] 165 (let [templ-str (slurp file) 166 md-str (fill-templ templ-str hole-fillers) 167 html-str (md-to-html md-str)] 168 (h/raw html-str)))) 169 170 (defn prettify-html [html-str] 171 (:out (shell/sh 172 "prettier" 173 "--stdin-filepath" 174 "foo.html" ; just need to end in .html so prettier knows what to do. 175 :in html-str)))