geocsv-js/docs/js/compiled/out/devtools/util.cljs

249 lines
12 KiB
Clojure

(ns devtools.util
(:require-macros [devtools.oops :refer [oget ocall oset unchecked-aget unchecked-aset]]
[devtools.compiler :refer [check-compiler-options!]])
(:require [goog.userAgent :as ua]
[clojure.data :as data]
[devtools.version :refer [get-current-version]]
[devtools.context :as context]
[cljs.pprint :as cljs-pprint]
[devtools.prefs :as prefs]))
; cljs.pprint does not play well in advanced mode :optimizations, see https://github.com/binaryage/cljs-devtools/issues/37
(check-compiler-options!)
(def lib-info-style "color:black;font-weight:bold;")
(def reset-style "color:black")
(def advanced-build-explanation-url
"https://github.com/binaryage/cljs-devtools/blob/master/docs/faq.md#why-custom-formatters-do-not-work-for-advanced-builds")
(def ^:dynamic *custom-formatters-active* false)
(def ^:dynamic *console-open* false)
(def ^:dynamic *custom-formatters-warning-reported* false)
; -- general helpers --------------------------------------------------------------------------------------------------------
(defn pprint-str [& args]
(with-out-str
(binding [*print-level* 300]
(apply cljs-pprint/pprint args))))
; -- version helpers --------------------------------------------------------------------------------------------------------
(defn ^:dynamic make-version-info []
(str (get-current-version)))
(defn ^:dynamic make-lib-info []
(str "CLJS DevTools " (make-version-info)))
(defn get-lib-info []
(make-lib-info))
; -- node.js support --------------------------------------------------------------------------------------------------------
(defn ^:dynamic get-node-info [root]
(try
(let [process (oget root "process")
version (oget process "version")
platform (oget process "platform")]
(if (and version platform)
{:version version
:platform platform}))
(catch :default _
nil)))
(defn ^:dynamic get-node-description [node-info]
(str (or (:platform node-info) "?") "/" (or (:version node-info) "?")))
(defn ^:dynamic in-node-context? []
(some? (get-node-info (context/get-root))))
; -- javascript context utils -----------------------------------------------------------------------------------------------
(defn ^:dynamic get-js-context-description []
(if-let [node-info (get-node-info (context/get-root))]
(str "node/" (get-node-description node-info))
(let [user-agent (ua/getUserAgentString)]
(if (empty? user-agent)
"<unknown context>"
user-agent))))
; -- message formatters -----------------------------------------------------------------------------------------------------
(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 Javascript context: " (get-js-context-description) "."))
(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"))
; -- devtools formatters access ---------------------------------------------------------------------------------------------
(def formatter-key "devtoolsFormatters")
(defn get-formatters-safe []
(let [formatters (unchecked-aget (context/get-root) 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))]}
(unchecked-aset (context/get-root) 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 @prefs/default-config (prefs/get-prefs)))]
(if-not (empty? diff)
(.info (context/get-console) msg (pprint-str diff))))))
; -- custom formatters detection --------------------------------------------------------------------------------------------
(deftype CustomFormattersDetector [])
; https://github.com/binaryage/cljs-devtools/issues/16
(defn make-detector []
(let [detector (CustomFormattersDetector.)]
(unchecked-aset detector "header" (fn [_object _config]
(set! *custom-formatters-active* true)
nil))
(unchecked-aset detector "hasBody" (constantly false))
(unchecked-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 (unchecked-aget (context/get-root) 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 (context/get-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)
console (context/get-console)]
(.apply (.-info console) 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")]
(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 (context/get-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)))
; -- advanced mode check ----------------------------------------------------------------------------------------------------
(defn under-advanced-build? []
(if-not (prefs/pref :disable-advanced-mode-check)
(nil? (oget (context/get-root) "devtools" "version")))) ; we rely on the fact that under advanced mode the namespace will be renamed
(defn display-advanced-build-warning-if-needed! []
(if-not (prefs/pref :dont-display-advanced-build-warning)
(let [banner (str "%cNOT%c installing %c%s%c under advanced build. See " advanced-build-explanation-url ".")]
(.warn (context/get-console) banner "font-weight:bold" reset-style lib-info-style (get-lib-info) reset-style))))
; -- 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 (context/get-console) (feature-not-available-msg feature)))))