Following up my last post on Google Closure, I wanted to give some pointers on including the library in your code. When you include the lib, it’s sometimes unclear when to use :require vs :import.

In David Nolen’s ClojureScript 101 post, he says:

Here we use :import so that we can use short names for the Google Closure constructors.

Note: :import is only for this use case, you never use it with ClojureScript libraries

If you look at the cljs analyzer, you’ll see the following error message in parse-import-spec:

(throw (error env
  (parse-ns-error-msg spec "Only lib.ns.Ctor or [lib.ns Ctor*] spec supported in :import")))

We can see that the :import spec expects some type of constructor (referred to as Ctor in the error message). As an example, we can revisit cljs-http and look at the ns for cljs-http.util:

(ns cljs-http.util
  (:import goog.Uri) ;;<-- Closure Library
  (:require [clojure.string :refer [blank? capitalize join split lower-case]]
            [cognitect.transit :as t]
            [goog.userAgent :as agent] ;;<-- Closure Library
            [no.en.core :refer [base64-encode]]))

The docs for goog.Uri show that Uri is a class, so its constructor is loaded via :import:

;;(:import goog.Uri)

(defn build-url
  "Build the url from the request map."
  [{:keys [scheme server-name server-port uri query-string]}]
  (str (doto (Uri.) ;; <-- Constructor
         (.setScheme (name (or scheme :http)))
         (.setDomain server-name)
         (.setPort server-port)
         (.setPath uri)
         (.setQuery query-string true))))

goog.userAgent is a namespace that provides several functions, so it’s loaded via :require:

;;(:require [goog.userAgent :as agent])

(defn user-agent
  "Returns the user agent."
  [] (agent/getUserAgentString)) ;;<-- simple fn call

One other point of confusion is Enums (e.g. goog.events.EventType). Enums are implemented as js objects; we :import them as constructors:

(ns cljs-made-easy.example.core
  (:require [goog.events :as events]))
  (:import [goog.events EventType]))

(def src ...)
(defn callback [] ...)

(events/listen! src EventType.CLICK callback) 

I’ve seen enums used as EventType.ACTION and EventType/ACTION, and both appear to work. EventType.ACTION is used more frequently in my experience, but there doesn’t seem to be an idiomatic version. David Nolen points out that EventType/ACTION is incorrect because / always means namespace in cljs. So EventType.Action is definitely what you want to use.

So here’s your guideline: use :import for Closure classes and enums, and use :require for everything else.

Find this post useful? Share the love!

Recent Posts