<commit_before>(ns figwheel-sidecar.components.cljs-autobuild
  (:require
   [figwheel-sidecar.config :refer [prep-build prepped?]]
   [figwheel-sidecar.build-utils :as butils]
   [figwheel-sidecar.watching :as watching]
   [figwheel-sidecar.utils :as utils]

      ;; build hooks
   [figwheel-sidecar.build-middleware.injection :as injection]
   [figwheel-sidecar.build-middleware.notifications :as notifications]
   [figwheel-sidecar.build-middleware.clj-reloading :as clj-reloading]
   [figwheel-sidecar.build-middleware.javascript-reloading :as javascript-reloading]

   [com.stuartsierra.component :as component]
   [cljs.closure]
   [cljs.build.api :as bapi]
   [clojure.java.io :as io]
   [clojure.java.shell :refer [sh]]))

;; TODO can I run this without a figwheel server??
;; that would make this component much more useful

(defn cljs-build [{:keys [build-config]}]
  (bapi/build
   (apply bapi/inputs (:source-paths build-config))
   (:build-options build-config)
   (:compiler-env build-config)))

(let [reset-color "\u001b[0m"
      foreground-green "\u001b[32m"
      elapsed
      (fn [started-at]
        (let [elapsed-us (- (System/currentTimeMillis) started-at)]
          (with-precision 2
            (str (/ (double elapsed-us) 1000) " seconds"))))]
  (defn figwheel-start-and-end-messages [build-fn]
    (fn [{:keys [figwheel-server build-config changed-files] :as build-state}]
      (let [started-at (System/currentTimeMillis)
            {:keys [build-options source-paths]} build-config
            {:keys [output-to]} build-options]
        ;; print start message
        (println (str reset-color "Compiling \""
                      output-to
                      "\" from " (pr-str source-paths) "..."))
        (flush)
                                        ; build
        (build-fn build-state)
                                        ; print end message
        (println (str foreground-green
                      "Successfully compiled \""
                      output-to
                      "\" in " (elapsed started-at) "." reset-color))
        (flush)))))

(def figwheel-build
  (-> cljs-build
      injection/hook
      notifications/hook
      clj-reloading/hook
      javascript-reloading/hook
      figwheel-start-and-end-messages))

(def figwheel-build-without-javascript-reloading
  (-> cljs-build
      injection/hook
      notifications/hook
      clj-reloading/hook
      figwheel-start-and-end-messages))

(def figwheel-build-without-clj-reloading
  (-> cljs-build
      injection/hook
      notifications/hook
      javascript-reloading/hook
      figwheel-start-and-end-messages))

(defn source-paths-that-affect-build [{:keys [build-options source-paths]}]
  (let [{:keys [libs foreign-libs]} build-options]
    (concat
     source-paths
     libs
     (not-empty (mapv :file foreign-libs)))))

;; this is just used for the initial build
(defn catch-print-hook
  "Build middleware hook that catches and prints errors."
  [build-fn]
  (fn [build-state]
    (try
      (build-fn build-state)
      (catch Throwable e
        (prn (ex-data (.getCause e)))
        (notifications/report-exception e (ex-data (.getCause e)))))))

(defn extract-cljs-build-fn
  [{:keys [figwheel-server] :as cljs-autobuild}]
  (or (:cljs-build-fn cljs-autobuild)
      (:cljs-build-fn figwheel-server)
      ;; if no figwheel server
      ;; default build should be standard
      ;; cljs build
      (if figwheel-server
        figwheel-build
        (figwheel-start-and-end-messages cljs-build))))

(defn execute-build
  [{:keys [figwheel-server] :as cljs-autobuild} files]
  (let [log-writer (or (:log-writer cljs-autobuild)
                       (:log-writer figwheel-server)
                       (io/writer "figwheel_server.log" :append true))
        cljs-build-fn (extract-cljs-build-fn cljs-autobuild)]
    (utils/sync-exec
     (fn []
       (binding [*out* log-writer
                 *err* log-writer]
         (cljs-build-fn
          (assoc cljs-autobuild
            :changed-files (map str files))))
       (when-let [notify-command (-> cljs-autobuild :build-config :notify-command)]
         (println (:out (apply sh notify-command))))))))

(defrecord CLJSAutobuild [build-config figwheel-server]
  component/Lifecycle
  (start [this]
    (if-not (:file-watcher this)
      (do
        (println "Figwheel: Watching build -" (:id build-config))
        (flush)
        ;; setup
        (injection/delete-connect-scripts! [build-config])
        ;; TODO this should be conditional based on a flag
        #_(clean-cljs-build* (:build-options build-config))
        ;; initial build only needs the injection and the
        ;; start and end messages

        (let [cljs-build-fn (extract-cljs-build-fn this)]
          ;; build once before watching
          ;; tiny experience tweak
          ;; first build shouldn't send notifications
          ((if (= cljs-build-fn figwheel-build)
             (-> cljs-build
                 catch-print-hook
                 injection/hook
                 figwheel-start-and-end-messages)
             cljs-build-fn) this)
          (assoc this
                 ;; for simple introspection
                 :cljs-autobuild true
                 :file-watcher
                 (watching/watch!
                  (:hawk-options figwheel-server)
                  (source-paths-that-affect-build build-config)
                  (partial execute-build this)))))<commit_msg>Passing notify-command a more descriptive message; trigger on initial build<commit_after>(ns figwheel-sidecar.components.cljs-autobuild
  (:require
   [figwheel-sidecar.config :refer [prep-build prepped?]]
   [figwheel-sidecar.build-utils :as butils]
   [figwheel-sidecar.watching :as watching]
   [figwheel-sidecar.utils :as utils]

      ;; build hooks
   [figwheel-sidecar.build-middleware.injection :as injection]
   [figwheel-sidecar.build-middleware.notifications :as notifications]
   [figwheel-sidecar.build-middleware.clj-reloading :as clj-reloading]
   [figwheel-sidecar.build-middleware.javascript-reloading :as javascript-reloading]

   [com.stuartsierra.component :as component]
   [cljs.closure]
   [cljs.build.api :as bapi]
   [clojure.java.io :as io]
   [clojure.java.shell :refer [sh]]))

;; TODO can I run this without a figwheel server??
;; that would make this component much more useful

(defn cljs-build [{:keys [build-config]}]
  (bapi/build
   (apply bapi/inputs (:source-paths build-config))
   (:build-options build-config)
   (:compiler-env build-config)))

(let [reset-color "\u001b[0m"
      foreground-green "\u001b[32m"
      elapsed
      (fn [started-at]
        (let [elapsed-us (- (System/currentTimeMillis) started-at)]
          (with-precision 2
            (str (/ (double elapsed-us) 1000) " seconds"))))]
  (defn figwheel-start-and-end-messages [build-fn]
    (fn [{:keys [figwheel-server build-config changed-files] :as build-state}]
      (let [started-at (System/currentTimeMillis)
            {:keys [build-options source-paths]} build-config
            {:keys [output-to]} build-options]
        ;; print start message
        (println (str reset-color "Compiling \""
                      output-to
                      "\" from " (pr-str source-paths) "..."))
        (flush)
                                        ; build
        (build-fn build-state)
                                        ; print end message
        (println (str foreground-green
                      "Successfully compiled \""
                      output-to
                      "\" in " (elapsed started-at) "." reset-color))
        (flush)))))


(defn handle-notify-command [build-state s]
  (when-let [notify-command (-> build-state :build-config :notify-command)]
    (apply sh (concat notify-command [s]))))

(defn notify-command-hook [build-fn]
  (fn [build-state]
    (try
      (build-fn build-state)
      (handle-notify-command build-state (str "Successfully compiled ClojureScript build: "
                                              (name (-> build-state :build-config :id))))
      (catch Throwable e
        (handle-notify-command build-state (str "Failed compiling ClojureScript build: "
                                                (name (-> build-state :build-config :id))))
        (throw e)))))

(def figwheel-build
  (-> cljs-build
      injection/hook
      notify-command-hook
      notifications/hook
      clj-reloading/hook
      javascript-reloading/hook
      figwheel-start-and-end-messages))

(def figwheel-build-without-javascript-reloading
  (-> cljs-build
      injection/hook
      notify-command-hook      
      notifications/hook
      clj-reloading/hook
      figwheel-start-and-end-messages))

(def figwheel-build-without-clj-reloading
  (-> cljs-build
      injection/hook
      notify-command-hook      
      notifications/hook
      javascript-reloading/hook
      figwheel-start-and-end-messages))

(defn source-paths-that-affect-build [{:keys [build-options source-paths]}]
  (let [{:keys [libs foreign-libs]} build-options]
    (concat
     source-paths
     libs
     (not-empty (mapv :file foreign-libs)))))

;; this is just used for the initial build
(defn catch-print-hook
  "Build middleware hook that catches and prints errors."
  [build-fn]
  (fn [build-state]
    (try
      (build-fn build-state)
      (catch Throwable e
        (prn (ex-data (.getCause e)))
        (notifications/report-exception e (ex-data (.getCause e)))))))

(defn extract-cljs-build-fn
  [{:keys [figwheel-server] :as cljs-autobuild}]
  (or (:cljs-build-fn cljs-autobuild)
      (:cljs-build-fn figwheel-server)
      ;; if no figwheel server
      ;; default build should be standard
      ;; cljs build
      (if figwheel-server
        figwheel-build
        (figwheel-start-and-end-messages cljs-build))))

(defn execute-build
  [{:keys [figwheel-server] :as cljs-autobuild} files]
  (let [log-writer (or (:log-writer cljs-autobuild)
                       (:log-writer figwheel-server)
                       (io/writer "figwheel_server.log" :append true))
        cljs-build-fn (extract-cljs-build-fn cljs-autobuild)]
    (utils/sync-exec
     (fn []
       (binding [*out* log-writer
                 *err* log-writer]
         (cljs-build-fn
          (assoc cljs-autobuild
                 :changed-files (map str files))))))))

(defrecord CLJSAutobuild [build-config figwheel-server]
  component/Lifecycle
  (start [this]
    (if-not (:file-watcher this)
      (do
        (println "Figwheel: Watching build -" (:id build-config))
        (flush)
        ;; setup
        (injection/delete-connect-scripts! [build-config])
        ;; TODO this should be conditional based on a flag
        #_(clean-cljs-build* (:build-options build-config))
        ;; initial build only needs the injection and the
        ;; start and end messages

        (let [cljs-build-fn (extract-cljs-build-fn this)]
          ;; build once before watching
          ;; tiny experience tweak
          ;; first build shouldn't send notifications
          ((if (= cljs-build-fn figwheel-build)
             (-> cljs-build
                 notify-command-hook
                 catch-print-hook
                 injection/hook
                 figwheel-start-and-end-messages)
             cljs-build-fn) this)
          (assoc this
                 ;; for simple introspection
                 :cljs-autobuild true
                 :file-watcher
                 (watching/watch!
                  (:hawk-options figwheel-server)
                  (source-paths-that-affect-build build-config)
                  (partial execute-build this)))))
{'fraction_non_alphanumeric': 0.12409886714727085, 'fraction_numerical': 0.002403020940611054, 'mean_word_length': 2.130843632455669, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6611902', 'n_tokens_mistral': 3604, 'n_tokens_neox': 3452, 'n_words': 799}