swinging-needle-meter/resources/public/js/compiled/out/devtools/util.cljs
2020-10-20 14:44:11 +01:00

189 lines
8.7 KiB
Clojure

(ns devtools.util
(:require-macros [devtools.util :refer [oget ocall oset]])
(:require [goog.userAgent :as ua]
[clojure.data :as data]
[cljs.pprint :refer [pprint]]
[devtools.version :refer [get-current-version]]
[devtools.defaults :as defaults]
[devtools.prefs :as prefs]))
(def ^:dynamic *custom-formatters-active* false)
(def ^:dynamic *console-open* false)
(def ^:dynamic *custom-formatters-warning-reported* false)
(defn ^:dynamic make-version-info []
(let [version (get-current-version)]
(str "v" version)))
(defn ^:dynamic make-lib-info []
(str "CLJS DevTools " (get-current-version)))
(defn ^:dynamic unknown-feature-msg [feature known-features lib-info]
(str "No such feature " feature " is currently available in " lib-info ". "
"The list of supported features is " (pr-str known-features) "."))
(defn ^:dynamic feature-not-available-msg [feature]
(str "Feature " feature " cannot be installed. "
"Unsupported browser " (ua/getUserAgentString) "."))
(defn ^:dynamic custom-formatters-not-active-msg []
(str "CLJS DevTools: some custom formatters were not rendered.\n"
"https://github.com/binaryage/cljs-devtools/blob/master/docs/faq.md#why-some-custom-formatters-were-not-rendered"))
(defn get-lib-info []
(make-lib-info))
(def formatter-key "devtoolsFormatters")
(defn get-formatters-safe []
(let [formatters (aget js/window formatter-key)]
(if (array? formatters) ; TODO: maybe issue a warning if formatters are anything else than array or nil
formatters
#js [])))
(defn set-formatters-safe! [new-formatters]
{:pre [(or (nil? new-formatters) (array? new-formatters))]}
(aset js/window formatter-key (if (empty? new-formatters) nil new-formatters)))
(defn print-config-overrides-if-requested! [msg]
(when (prefs/pref :print-config-overrides)
(let [diff (second (data/diff defaults/prefs (prefs/get-prefs)))]
(if-not (empty? diff)
(.info js/console msg (with-out-str (pprint diff)))))))
; -- custom formatters detection --------------------------------------------------------------------------------------------
(deftype CustomFormattersDetector [])
; https://github.com/binaryage/cljs-devtools/issues/16
(defn make-detector []
(let [detector (CustomFormattersDetector.)]
(aset detector "header" (fn [_object _config]
(set! *custom-formatters-active* true)
nil))
(aset detector "hasBody" (constantly false))
(aset detector "body" (constantly nil))
detector))
(defn install-detector! [detector]
(let [formatters (get-formatters-safe)]
(.push formatters detector)
(set-formatters-safe! formatters)))
(defn uninstall-detector! [detector]
; play it safe here, this method is called asynchronously
; in theory someone else could have installed additional custom formatters
; we have to be careful removing only ours formatters
(let [current-formatters (aget js/window formatter-key)]
(if (array? current-formatters)
(let [new-formatters (.filter current-formatters #(not (= detector %)))]
(set-formatters-safe! new-formatters)))))
(defn check-custom-formatters-active! []
(if (and *console-open* (not *custom-formatters-active*))
(when-not *custom-formatters-warning-reported*
(set! *custom-formatters-warning-reported* true)
(.warn js/console (custom-formatters-not-active-msg)))))
(defn uninstall-detector-and-check-custom-formatters-active! [detector]
(uninstall-detector! detector)
(check-custom-formatters-active!))
; a variation of http://stackoverflow.com/a/30638226/84283
(defn make-detection-printer []
(let [f (fn [])]
(oset f ["toString"] (fn []
(set! *console-open* true)
(js/setTimeout check-custom-formatters-active! 0) ; console is being opened, schedule another check
""))
f))
(defn wrap-with-custom-formatter-detection! [f]
(if-not (prefs/pref :dont-detect-custom-formatters)
(let [detector (make-detector)]
; this is a tricky business here
; we cannot ask DevTools if custom formatters are available and/or enabled
; we abuse the fact that we are printing info banner upon cljs-devtools installation anyways
; we install a special CustomFormattersDetector formatter which just records calls to it
; but does not format anything, it skips the opportunity to format the output so it has no visual effect
; this way we are able to detect if custom formatters are active and record it in *custom-formatters-active*
; but this technique does not work when printing happens when DevTools console is closed
; we have to add another system for detection of when console opens and re-detect custom formatters with opened console
(install-detector! detector)
(f "%c%s" "color:transparent" (make-detection-printer))
; note that custom formatters are applied asynchronously
; we have to uninstall our detector a bit later
(js/setTimeout (partial uninstall-detector-and-check-custom-formatters-active! detector) 0))
(f)))
; -- banner -----------------------------------------------------------------------------------------------------------------
(defn feature-for-display [installed-features feature]
(let [color (if (some #{feature} installed-features) "color:#0000ff" "color:#ccc")]
["%c%s" [color (str feature)]]))
(defn feature-list-display [installed-features feature-groups]
(let [labels (map (partial feature-for-display installed-features) (:all feature-groups))
* (fn [accum val]
[(str (first accum) " " (first val))
(concat (second accum) (second val))])]
(reduce * (first labels) (rest labels))))
(defn display-banner! [installed-features feature-groups fmt & params]
(let [[fmt-str fmt-params] (feature-list-display installed-features feature-groups)]
(wrap-with-custom-formatter-detection! (fn [add-fmt & add-args]
(let [items (concat [(str fmt " " fmt-str add-fmt)] params fmt-params add-args)]
(.apply (.-info js/console) js/console (into-array items)))))))
(defn display-banner-if-needed! [features-to-install feature-groups]
(if-not (prefs/pref :dont-display-banner)
(do
(let [banner (str "Installing %c%s%c and enabling features")
lib-info-style "color:black;font-weight:bold;"
reset-style "color:black"]
(display-banner! features-to-install feature-groups banner lib-info-style (get-lib-info) reset-style)))
; detection cannot be performed if we are not allowed to print something to console => assume active
(set! *custom-formatters-active* true)))
; -- unknown features -------------------------------------------------------------------------------------------------------
(defn report-unknown-features! [features known-features]
(let [lib-info (get-lib-info)]
(doseq [feature features]
(if-not (some #{feature} known-features)
(.warn js/console (unknown-feature-msg feature known-features lib-info))))))
(defn is-known-feature? [known-features feature]
(boolean (some #{feature} known-features)))
(defn convert-legacy-feature [feature]
(case feature
:custom-formatters :formatters
:sanity-hints :hints
feature))
(defn convert-legacy-features [features]
(map convert-legacy-feature features))
(defn sanititze-features! [features feature-groups]
(let [known-features (:all feature-groups)
features (convert-legacy-features features)] ; new feature names were introduced in v0.8
(report-unknown-features! features known-features)
(filter (partial is-known-feature? known-features) features)))
(defn resolve-features! [features-desc feature-groups]
(let [features (cond
(and (keyword? features-desc) (features-desc feature-groups)) (features-desc feature-groups)
(nil? features-desc) (:default feature-groups)
(seqable? features-desc) features-desc
:else [features-desc])]
(sanititze-features! features feature-groups)))
; -- installer --------------------------------------------------------------------------------------------------------------
(defn install-feature! [feature features-to-install available-fn install-fn]
(if (some #{feature} features-to-install)
(if (or (prefs/pref :bypass-availability-checks) (available-fn feature))
(install-fn)
(.warn js/console (feature-not-available-msg feature)))))