如何防止闭包编译器缩小 clojurescript 中的某些方法?

How can I prevent the closure compiler from minifying certain methods in clojurescript?

我正在将 quilljs 与我的 clojurescript 应用程序集成。我将它包含在我的 project.cljs 文件中,如下所示:[cljsjs/quill "1.3.5-0"]

编译器正在缩小某些方法并导致错误:

function xA(a, b) {
        var c = t(a).getSelection(!0)
          , d = c.index
          , e = c.length
          , h = Quill.import("delta");
        c = function(b, c, d, e) {
            return function(b) {
                return t(a).updateContents((new e).rf(c).delete(d).nf({
                    image: b
                }))
            }
        }(c, d, e, h);
        return b.c ? b.c(c) : b.call(null, c)
    }

这是错误:Uncaught TypeError: (intermediate value).rf is not a function

clojurescript 代码如下所示:

(defn file-recieve-handler [this cb]
  (let [range (.getSelection @this true)
        index (.-index range)
        length (.-length range)
        delta (.import js/Quill "delta")]

    (cb (fn [url]
            (.updateContents @this
                             (.insert
                               (.delete
                                 (.retain (new delta) index)
                                 length)
                               #js {:image url}))))))

retain 方法和 insert 方法正在缩小 - 他们不应该这样。 (删除不是出于某种原因,我猜那是因为它是 javascript 中的关键字。)

我找到了 quilljs 的外部文件: https://github.com/cljsjs/packages/blob/master/quill/resources/cljsjs/quill/common/quill.ext.js

我是否需要以某种方式补充外部文件或我可以编写代码的其他方式,以便在为编译器打开高级编译时这两种方法不会被缩小?

对于某些上下文,下面是完整文件。它基于此 https://github.com/benhowell/reagent-quill/blob/master/quill.cljs

(ns quill.core
  (:require
   [reagent.core :as r]))


(defn quill-toolbar [id]
  [:div {:id (str "quill-toolbar-" id)}

   [:span {:class "ql-formats"}
    [:select {:class "ql-header"}
     [:option {:value "1"}]
     [:option {:value "2"}]
     [:option {:value "3"}]
     [:option {:value "4"}]
     [:option {:value "5"}]
     [:option]]]

   [:span {:class "ql-formats"}
    [:select {:class "ql-font"}
     [:option]
     [:option {:value "serif"}]
     [:option {:value "monospace"}]]]

   [:span {:class "ql-formats"}
    [:select {:class "ql-size"}
     [:option {:value "small"}]
     [:option]
     [:option {:value "large"}]
     [:option {:value "huge"}]]]

   [:span {:class "ql-formats"}
    [:button {:class "ql-bold"}]
    [:button {:class "ql-italic"}]
    [:button {:class "ql-underline"}]
    [:button {:class "ql-strike"}]
    [:button {:class "ql-blockquote"}]]

   [:span {:class "ql-formats"}
    [:select {:class "ql-align"}]]

   [:span {:class "ql-formats"}
    [:button {:class "ql-script" :value "sub"}]
    [:button {:class "ql-script" :value "super"}]]

   [:span {:class "ql-formats"}
    [:button {:class "ql-indent" :value "-1"}]
    [:button {:class "ql-indent" :value "+1"}]]

   [:span {:class "ql-formats"}
    [:button {:class "ql-image"}] ]

   [:span {:class "ql-formats"}
    [:select {:class "ql-color"}]
    [:select {:class "ql-background"}]]

   [:span {:class "ql-formats"}
    [:button {:class "ql-clean"}]]])


(defn file-recieve-handler [this cb]
  (let [range (.getSelection @this true)
        index (.-index range)
        length (.-length range)
        delta (.import js/Quill "delta")]

    (cb (fn [url]
            (.updateContents @this
                             (.insert
                               (.delete
                                 (.retain (new delta) index)
                                 length)
                               #js {:image url}))))))


(defn editor [{:keys [id value selection on-change image-handler]}]
  (let [this (r/atom nil)
        get-value #(aget @this "container" "firstChild" "innerHTML")
        string-id (if (keyword? id) (name id) id) ]
    (r/create-class
     {:component-did-mount
      (fn [component]
        (reset! this
                (js/Quill.
                 (aget (.-children (r/dom-node component)) 1)
                 #js {:modules #js {:toolbar (aget (.-children (r/dom-node component)) 0)}
                      :theme "snow"
                      :scrollingContainer (str "quill-wrapper-" string-id)
                      :placeholder "Compose an epic..."}))

        (.on @this "text-change"
             (fn [delta old-delta source]
                 (on-change source (get-value))))

        ; FYI this is another area I had trouble. I got around it using 
        ; get and set in the goog.object
        (let [toolbar (.getModule @this "toolbar")
              handlers (goog.object/get toolbar "handlers")]
          (goog.object/set handlers "image" #(file-recieve-handler this image-handler)))

        (if (= selection nil)
          (.setSelection @this nil)
          (.setSelection @this (first selection) (second selection) "api")))

      :component-will-receive-props
      (fn [component next-props]
        (if
            (or
             (not= (:value (second next-props)) (get-value))
             (not= (:id (r/props component)) (:id (second next-props))))
          (do
            (if (= selection nil)
              (.setSelection @this nil)
              (.setSelection @this (first selection) (second selection) "api"))
            (.pasteHTML @this (:value (second next-props))))))

      :display-name  (str "quill-editor-" string-id)

      :reagent-render
      (fn []
        [:div {:id (str "quill-wrapper-" string-id) :class "quill-wrapper"}
         [quill-toolbar string-id]
         [:div {:id (str "quill-editor-" string-id)
                :class "quill-editor"
                :dangerouslySetInnerHTML {:__html value}}]])})))


(defn display-area [{:keys [id content]}]
  (let [this (r/atom nil)]
    (r/create-class
     {:component-did-mount
      (fn [component]
        (reset! this (js/Quill. (r/dom-node component)
                                #js {:theme "snow"
                                     :modules #js {:toolbar false}
                                     :placeholder ""}))
        (.disable @this))

      :component-will-receive-props
      (fn [component next-props]
        (.pasteHTML @this (:content (second next-props))))

      :display-name  (str "quill-display-area-" id)

      :reagent-render
      (fn []
        [:div {:id (str "quill-display-area-" id)
               :class "quill-display-area"
               :dangerouslySetInnerHTML {:__html content}}])})))

您可以打开外部推理警告,编译器会告诉您可能会重命名的内容。

;; in the actual ns
(set! *warn-on-infer* true)

;; in the build config compiler options
:infer-externs true

https://clojurescript.org/guides/externs#externs-inference

为了帮助调试重命名问题,您可以在编译器选项中打开 :pseudo-names true。这将使找出哪些方法被重命名以及可能需要 ^js typehint 或手动 externs 变得更容易。

我按照@thomas-heller 的建议使用了类型提示并且它起作用了。我把原来的功能分成了两个独立的功能。这里重写:

(defn add-image [^js/Quill quill ^js/Quill.imports.delta delta index length url]
  (.updateContents quill
                   (.insert
                     (.delete
                       (.retain delta index)
                       length)
                     #js {:image url})))

;https://github.com/quilljs/quill/pull/995/files#diff-6dafc0fe6b5e9aed0859eef541e68372
(defn file-recieve-handler [^js/Quill quill cb]
  (let [range (.getSelection quill true)
        index (.-index range)
        length (.-length range)
        delta  (new (.-delta (.-imports js/Quill)))]

    (cb (fn [url]
            (add-image quill delta index length url)))))