Skip to content

Instantly share code, notes, and snippets.

@bhauman
Last active August 8, 2017 21:19
Show Gist options
  • Select an option

  • Save bhauman/2dca87815dfd92b3ff596bdc1e56c964 to your computer and use it in GitHub Desktop.

Select an option

Save bhauman/2dca87815dfd92b3ff596bdc1e56c964 to your computer and use it in GitHub Desktop.

Revisions

  1. Bruce Hauman revised this gist Jun 29, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion compiler-options-schema.clj
    Original file line number Diff line number Diff line change
    @@ -12,7 +12,7 @@
    (defn update-key-data [k f & args]
    (swap! registry-ref update-in [k] (fn [x] (apply f args))))


    ;; TODO a bunch of work is needed to convert error explain data into a namespaced keyword
    (defn key-doc
    "Given a spec key returns a String of documentation if it has been provided"
    [k]
  2. Bruce Hauman revised this gist Jun 29, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion compiler-options-schema.clj
    Original file line number Diff line number Diff line change
    @@ -12,6 +12,7 @@
    (defn update-key-data [k f & args]
    (swap! registry-ref update-in [k] (fn [x] (apply f args))))


    (defn key-doc
    "Given a spec key returns a String of documentation if it has been provided"
    [k]
    @@ -22,7 +23,6 @@
    [explain-data]
    (->> explain-data
    ::s/problems
    vals
    (keep (comp (juxt identity key-doc) last :via))
    (filter second)
    (into {})))
  3. Bruce Hauman revised this gist Jun 28, 2016. 1 changed file with 11 additions and 4 deletions.
    15 changes: 11 additions & 4 deletions compiler-options-schema.clj
    Original file line number Diff line number Diff line change
    @@ -501,13 +501,15 @@ to work, and only goog-define defines are affected. :closure-defines
    currently does not have any effect with :optimization :whitespace.")

    (def-key ::closure-extra-annotations (s/+ non-blank-string?)
    "Define extra JSDoc annotations that a closure library might use so

    "Define extra JSDoc annotations that a closure library might use so
    that they don't trigger compiler warnings.
    :closure-extra-annotations #{\"api\"}")

    (def-key ::anon-fn-naming-policy #{:off :unmapped :mapped}
    "Strategies for how the Google Closure compiler does naming of

    "Strategies for how the Google Closure compiler does naming of
    anonymous functions that occur as r-values in assignments and variable
    declarations. Defaults to :off.
    @@ -526,7 +528,8 @@ The following values are supported:
    left-hand side of the assignment.")

    (def-key ::optimize-constants boolean?
    "When set to true, constants, such as keywords and symbols, will only

    "When set to true, constants, such as keywords and symbols, will only
    be created once and will be written to a separate file called
    constants_table.js. The compiler will emit a reference to the constant
    as defined in the constants table instead of creating a new object for
    @@ -537,12 +540,14 @@ Defaults to true under :advanced optimizations otherwise to false.
    :optimize-constants true")

    (def-key ::parallel-build boolean?

    "When set to true, compile source in parallel, utilizing multiple cores.
    :parallel-build true")

    (def-key ::watch-fn fn?
    "Is a function that will be called after a successful build.

    "Is a function that will be called after a successful build.
    Only available for cljs.build.api/watch
    @@ -584,6 +589,7 @@ Only available for cljs.build.api/watch
    ::protocol-duped-method
    ::protocol-multiple-impls
    ::invoke-ctor])

    "This flag will turn on/off compiler warnings for references to
    undeclared vars, wrong function call arities, etc. Can be a boolean
    for enabling/disabling common warnings, or a map of specific warning
    @@ -674,6 +680,7 @@ The following warnings are supported:
    ::undefined-variables
    ::unknown-defines
    ::visiblity])

    "Configure warnings generated by the Closure compiler. A map from
    Closure warning to configuration value, only :error, :warning and :off
    are supported.
  4. Bruce Hauman revised this gist Jun 28, 2016. 1 changed file with 46 additions and 17 deletions.
    63 changes: 46 additions & 17 deletions compiler-options-schema.clj
    Original file line number Diff line number Diff line change
    @@ -84,6 +84,7 @@
    ;; Commented out configs are more obvious than no-op ones.

    (def-key ::output-to non-blank-string?

    "After your ClojureScript has been compiled to JavaScript, this
    specifies the name of the JavaScript output file. The contents of
    this file will differ based on the :optimizations setting.
    @@ -98,6 +99,7 @@ output file will contain all the compiled code.
    :output-to \"resources/public/js/main.js\"")

    (def-key ::output-dir non-blank-string?

    "Sets the output directory for output files generated during
    compilation.
    @@ -106,7 +108,8 @@ Defaults to \"out\".
    :output-dir \"resources/public/js/out\"")

    (def-key ::optimizations #{:none :whitespace :simple :advanced}
    "The optimization level. May be :none, :whitespace, :simple, or

    "The optimization level. May be :none, :whitespace, :simple, or
    :advanced. Only :none and :simple are supported for bootstrapped
    ClojureScript.
    @@ -127,7 +130,8 @@ Defaults to :none. Note: lein cljsbuild 1.0.5 will supply :whitespace.
    :optimizations :none")

    (def-key ::main ::string-or-symbol
    "Specifies an entry point namespace. When combined with optimization

    "Specifies an entry point namespace. When combined with optimization
    level :none, :main will cause the compiler to emit a single JavaScript
    file that will import goog/base.js, the JavaScript file for the
    namespace, and emit the required goog.require statement. This permits
    @@ -138,6 +142,7 @@ Also see :asset-path.
    :main \"example.core\"")

    (def-key ::asset-path string?

    "When using :main it is often necessary to control where the entry
    point script attempts to load scripts from due to the configuration of
    the web server. :asset-path is a relative URL path not a file system
    @@ -149,6 +154,7 @@ scripts from \"js/compiled/out\".
    :asset-path \"js/compiled/out\"")

    (def-key ::source-map (s/or :bool boolean? :string non-blank-string?)

    "See https://github.com/clojure/clojurescript/wiki/Source-maps. Under
    optimizations :none the valid values are true and false, with the
    default being true. Under all other optimization settings must specify
    @@ -159,6 +165,7 @@ Under :simple, :whitespace, or :advanced


    (def-key ::preloads (s/+ symbol?)

    "Developing ClojureScript commonly requires development time only
    side effects such as enabling printing, logging, spec instrumentation,
    and connecting REPLs. :preloads permits loading such side effect
    @@ -180,18 +187,21 @@ The :preloads config value must be a sequence of symbols that map to
    existing namespaces discoverable on the classpath.")

    (def-key ::verbose boolean?
    "Emit details and measurements from compiler activity.

    "Emit details and measurements from compiler activity.
    :verbose true")

    (def-key ::pretty-print boolean?

    "Determines whether the JavaScript output will be tabulated in a
    human-readable manner. Defaults to true.
    :pretty-print false")

    (def-key ::target #{:nodejs}
    "If targeting nodejs add this line. Takes no other options at the

    "If targeting nodejs add this line. Takes no other options at the
    moment. The default (no :target specified) implies browsers are being
    targeted. Have a look here for more information on how to run your
    code in nodejs.
    @@ -206,6 +216,7 @@ code in nodejs.
    ::requires
    ::module-type
    ::preprocess])

    "Adds dependencies on foreign libraries. Be sure that the url returns a
    HTTP Code 200
    @@ -250,7 +261,8 @@ keys have these semantics:
    (def-key ::preprocess ::string-or-named)

    (def-key ::externs (s/+ non-blank-string?)
    "Configure externs files for external libraries.

    "Configure externs files for external libraries.
    For this option, and those below, you can find a very good explanation at:
    http://lukevanderhart.com/2011/09/30/using-javascript-and-clojurescript.html
    @@ -266,7 +278,8 @@ Defaults to the empty vector [].
    :req-un [:cljs.options-schema.modules/output-dir
    ::entries]
    :opt-un [::depends-on]))
    "A new option for emitting Google Closure Modules. Closure Modules

    "A new option for emitting Google Closure Modules. Closure Modules
    supports splitting up an optimized build into N different modules. If
    :modules is supplied it replaces the single :output-to. A module needs
    a name, an individual :output-to file path, :entries a set of
    @@ -318,19 +331,22 @@ single source map to name.")
    (def-key ::depends-on (s/+ ::string-or-named))

    (def-key ::source-map-path string?
    "Set the path to source files references in source maps to avoid

    "Set the path to source files references in source maps to avoid
    further web server configuration.
    :source-map-path \"public/js\"")

    (def-key ::source-map-timestamp boolean?
    "Add cache busting timestamps to source map urls. This is helpful for

    "Add cache busting timestamps to source map urls. This is helpful for
    keeping source maps up to date when live reloading code.
    :source-map-timestamp true")

    (def-key ::cache-analysis boolean?
    "Experimental. Cache compiler analysis to disk. This enables faster

    "Experimental. Cache compiler analysis to disk. This enables faster
    cold build and REPL start up times.
    For REPLs, defaults to true. Otherwise, defaults to true if and only
    @@ -339,7 +355,8 @@ if :optimizations is :none.
    :cache-analysis true")

    (def-key ::recompile-dependents boolean?
    "For correctness the ClojureScript compiler now always recompiles

    "For correctness the ClojureScript compiler now always recompiles
    dependent namespaces when a parent namespace changes. This prevents
    corrupted builds and swallowed warnings. However this can impact
    compile times depending on the structure of the application. This
    @@ -348,7 +365,8 @@ option defaults to true.
    :recompile-dependents false")

    (def-key ::static-fns boolean?
    "Employs static dispatch to specific function arities in emitted

    "Employs static dispatch to specific function arities in emitted
    JavaScript, as opposed to making use of the call construct. Defaults
    to false except under advanced optimizations. Useful to have set to
    false at REPL development to facilitate function redefinition, and
    @@ -362,6 +380,7 @@ compiled with :static-fns implicitly set to true.
    ;; (def-key ::warnings (ref-schema 'CompilerWarnings))

    (def-key ::elide-asserts boolean?

    "This flag will cause all (assert x) calls to be removed during
    compilation, including implicit asserts associated with :pre and :post
    conditions. Useful for production. Default is always false even in
    @@ -375,27 +394,31 @@ the elision.
    :elide-asserts true")

    (def-key ::pseudo-names boolean?

    "With :advanced mode optimizations, determines whether readable names
    are emitted. This can be useful when debugging issues in the optimized
    JavaScript and can aid in finding missing externs. Defaults to false.
    :pseudo-names true")

    (def-key ::print-input-delimiter boolean?
    "Determines whether comments will be output in the JavaScript that can

    "Determines whether comments will be output in the JavaScript that can
    be used to determine the original source of the compiled code.
    Defaults to false.
    :print-input-delimiter false")

    (def-key ::output-wrapper boolean?

    "Wrap the JavaScript output in (function(){...};)() to avoid clobbering
    globals. Defaults to false.
    :output-wrapper false")

    (def-key ::libs (s/+ string?)

    "Adds dependencies on external js libraries, i.e. Google
    Closure-compatible javascript files with correct goog.provides() and
    goog.requires() calls. Note that files in these directories will be
    @@ -411,30 +434,34 @@ Defaults to the empty vector []
    \"src/org/example/example.js\"]")

    (def-key ::preamble (s/+ non-blank-string?)
    "Prepends the contents of the given files to each output file. Only

    "Prepends the contents of the given files to each output file. Only
    valid with optimizations other than :none.
    Defaults to the empty vector []
    :preamble [\"license.js\"]")

    (def-key ::hashbang boolean?
    "When using :target :nodejs the compiler will emit a shebang as the

    "When using :target :nodejs the compiler will emit a shebang as the
    first line of the compiled source, making it executable. When your
    intention is to build a node.js module, instead of executable, use
    this option to remove the shebang.
    :hashbang false")

    (def-key ::compiler-stats boolean?
    "Report basic timing measurements on compiler activity.

    "Report basic timing measurements on compiler activity.
    Defaults to false.
    :compiler-stats true")

    (def-key ::language-in #{:ecmascript3 :ecmascript5 :ecmascript5-strict}
    "Configure the input and output languages for the closure library. May

    "Configure the input and output languages for the closure library. May
    be :ecmascript3, ecmascript5, ecmascript5-strict, :ecmascript6-typed,
    :ecmascript6-strict, :ecmascript6 or :no-transpile.
    @@ -443,7 +470,8 @@ Defaults to :ecmascript3
    :language-in :ecmascript3")

    (def-key ::language-out #{:ecmascript3 :ecmascript5 :ecmascript5-strict}
    "Configure the input and output languages for the closure library. May

    "Configure the input and output languages for the closure library. May
    be :ecmascript3, ecmascript5, ecmascript5-strict, :ecmascript6-typed,
    :ecmascript6-strict, :ecmascript6 or :no-transpile.
    @@ -455,6 +483,7 @@ Defaults to :ecmascript3
    (s/map-of
    ::string-or-symbol
    (s/or :number number? :string non-blank-string? :bool boolean?))

    "Set the values of Closure libraries' variables annotated with @define
    or with the cljs.core/goog-define helper macro. A common usage is
    setting goog.DEBUG to false:
  5. Bruce Hauman revised this gist Jun 28, 2016. 1 changed file with 0 additions and 4 deletions.
    4 changes: 0 additions & 4 deletions compiler-options-schema.clj
    Original file line number Diff line number Diff line change
    @@ -512,9 +512,6 @@ Defaults to true under :advanced optimizations otherwise to false.
    :parallel-build true")

    (def-key ::devcards boolean?
    "Whether to include devcard 'defcard' definitions in the output of the compile.")

    (def-key ::watch-fn fn?
    "Is a function that will be called after a successful build.
    @@ -530,7 +527,6 @@ Only available for cljs.build.api/watch
    (def-key ::ups-externs (s/+ non-blank-string?))
    (def-key ::ups-foreign-libs (s/+ ::foreign-libs))
    (def-key ::closure-output-charset non-blank-string?)
    (def-key ::external-config (s/map-of keyword? map?))

    ;; ** ClojureScript Compiler Warnings

  6. Bruce Hauman renamed this gist Jun 28, 2016. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  7. Bruce Hauman revised this gist Jun 28, 2016. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions options_schema.cljs
    Original file line number Diff line number Diff line change
    @@ -5,11 +5,6 @@
    ;; for initial dev
    [clojure.test :refer [deftest is testing]]))

    ;; ** Checking for duplicate keys

    ;; As a spec gets larger its very easy to duplicate a key and break
    ;; everything. It's nice to have a safe guard in place.

    (defn non-blank-string? [x] (and (string? x) (not (string/blank? x))))

    (defonce ^:private registry-ref (atom {}))
    @@ -32,6 +27,11 @@
    (filter second)
    (into {})))

    ;; ** Checking for duplicate keys

    ;; As a spec gets larger its very easy to duplicate a key and break
    ;; everything. It's nice to have a safe guard in place.

    ;; can use this to test for duplicate definitions This works fine with
    ;; a reloading workflow as long as you reload the whole file.
    (let [a (atom {})]
  8. Bruce Hauman revised this gist Jun 28, 2016. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions options_schema.cljs
    Original file line number Diff line number Diff line change
    @@ -60,8 +60,6 @@
    `(update-key-data ~k merge
    {:doc ~(when doc doc)}))))))

    #_(def-key ::aasdff string? "asdfasdfasdfasdf")

    ;; * Specification for ClojureScript
    ;; ** Top level util specs

  9. Bruce Hauman revised this gist Jun 28, 2016. 1 changed file with 19 additions and 19 deletions.
    38 changes: 19 additions & 19 deletions options_schema.cljs
    Original file line number Diff line number Diff line change
    @@ -85,7 +85,7 @@
    ;; remove/comment out no-op configs, to bring awareness to them.
    ;; Commented out configs are more obvious than no-op ones.

    (def-key ::output-to string?
    (def-key ::output-to non-blank-string?
    "After your ClojureScript has been compiled to JavaScript, this
    specifies the name of the JavaScript output file. The contents of
    this file will differ based on the :optimizations setting.
    @@ -99,7 +99,7 @@ output file will contain all the compiled code.
    :output-to \"resources/public/js/main.js\"")

    (def-key ::output-dir string?
    (def-key ::output-dir non-blank-string?
    "Sets the output directory for output files generated during
    compilation.
    @@ -150,7 +150,7 @@ scripts from \"js/compiled/out\".
    :asset-path \"js/compiled/out\"")

    (def-key ::source-map (s/or :bool boolean? :string string?)
    (def-key ::source-map (s/or :bool boolean? :string non-blank-string?)
    "See https://github.com/clojure/clojurescript/wiki/Source-maps. Under
    optimizations :none the valid values are true and false, with the
    default being true. Under all other optimization settings must specify
    @@ -244,14 +244,14 @@ keys have these semantics:
    the supplied value in order to effect the desired code
    transformation.")

    (def-key ::file string?)
    (def-key ::provides (s/+ string?))
    (def-key ::file-min string?)
    (def-key ::requires (s/+ string?))
    (def-key ::file non-blank-string?)
    (def-key ::provides (s/+ non-blank-string?))
    (def-key ::file-min non-blank-string?)
    (def-key ::requires (s/+ non-blank-string?))
    (def-key ::module-type #{:commonjs :amd :es6})
    (def-key ::preprocess ::string-or-named)

    (def-key ::externs (s/+ string?)
    (def-key ::externs (s/+ non-blank-string?)
    "Configure externs files for external libraries.
    For this option, and those below, you can find a very good explanation at:
    @@ -315,8 +315,8 @@ single source map to name.")

    ;; ** TODO name collision don't want docs to collide
    ;; this is the only name collision in this
    (def-key :cljs.options-schema.modules/output-dir string?)
    (def-key ::entries (s/+ string?))
    (def-key :cljs.options-schema.modules/output-dir non-blank-string?)
    (def-key ::entries (s/+ non-blank-string?))
    (def-key ::depends-on (s/+ ::string-or-named))

    (def-key ::source-map-path string?
    @@ -412,7 +412,7 @@ Defaults to the empty vector []
    \"src/js\"
    \"src/org/example/example.js\"]")

    (def-key ::preamble (s/+ string?)
    (def-key ::preamble (s/+ non-blank-string?)
    "Prepends the contents of the given files to each output file. Only
    valid with optimizations other than :none.
    @@ -456,7 +456,7 @@ Defaults to :ecmascript3
    (def-key ::closure-defines
    (s/map-of
    ::string-or-symbol
    (s/or :number number? :string string? :bool boolean?))
    (s/or :number number? :string non-blank-string? :bool boolean?))
    "Set the values of Closure libraries' variables annotated with @define
    or with the cljs.core/goog-define helper macro. A common usage is
    setting goog.DEBUG to false:
    @@ -473,7 +473,7 @@ For :optimization :none, a :main option must be specified for defines
    to work, and only goog-define defines are affected. :closure-defines
    currently does not have any effect with :optimization :whitespace.")

    (def-key ::closure-extra-annotations (s/+ string?)
    (def-key ::closure-extra-annotations (s/+ non-blank-string?)
    "Define extra JSDoc annotations that a closure library might use so
    that they don't trigger compiler warnings.
    @@ -528,10 +528,10 @@ Only available for cljs.build.api/watch
    (def-key ::emit-constants boolean?)
    (def-key ::warning-handlers (s/+ ::s/any)) ;; symbol, string, or fn?
    (def-key ::source-map-inline boolean?)
    (def-key ::ups-libs (s/+ string?))
    (def-key ::ups-externs (s/+ string?))
    (def-key ::ups-libs (s/+ non-blank-string?))
    (def-key ::ups-externs (s/+ non-blank-string?))
    (def-key ::ups-foreign-libs (s/+ ::foreign-libs))
    (def-key ::closure-output-charset string?)
    (def-key ::closure-output-charset non-blank-string?)
    (def-key ::external-config (s/map-of keyword? map?))

    ;; ** ClojureScript Compiler Warnings
    @@ -727,7 +727,7 @@ See the Closure Compiler Warning wiki for detailed descriptions.")
    ;; that will print out a warning and will always pass.

    (defn attach-warning-impl [message-or-fn parent-spec]
    {:pre [(or (fn? message-or-fn) (string? message-or-fn))]}
    {:pre [(or (fn? message-or-fn) (non-blank-string? message-or-fn))]}
    (reify
    clojure.lang.IFn
    (invoke [this x] (parent-spec x))
    @@ -767,7 +767,7 @@ See the Closure Compiler Warning wiki for detailed descriptions.")
    ;; generated by clojure.spec/explain

    (defn attach-reason-impl [reason parent-spec]
    {:pre [(string? reason)]}
    {:pre [(non-blank-string? reason)]}
    (reify
    clojure.lang.IFn
    (invoke [this x] (parent-spec x))
    @@ -950,4 +950,4 @@ See the Closure Compiler Warning wiki for detailed descriptions.")

    (clojure.test/run-tests 'strictly-specking.cljs-options-schema)

    )
    )
  10. Bruce Hauman revised this gist Jun 28, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion options_schema.cljs
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    (ns strictly-specking.cljs-options-schemar
    (ns cljs.compiler-options-schema
    (:require
    [clojure.spec :as s]
    [clojure.string :as string]
  11. Bruce Hauman revised this gist Jun 28, 2016. 1 changed file with 937 additions and 187 deletions.
    1,124 changes: 937 additions & 187 deletions options_schema.cljs
    Original file line number Diff line number Diff line change
    @@ -1,203 +1,953 @@
    (ns cljs.option-schema
    (ns strictly-specking.cljs-options-schemar
    (:require
    [clojure.spec :as s]))
    [clojure.spec :as s]
    [clojure.string :as string]
    ;; for initial dev
    [clojure.test :refer [deftest is testing]]))

    ;; ** Checking for duplicate keys

    ;; As a spec gets larger its very easy to duplicate a key and break
    ;; everything. It's nice to have a safe guard in place.

    (defn non-blank-string? [x] (and (string? x) (not (string/blank? x))))

    (defonce ^:private registry-ref (atom {}))

    (defn update-key-data [k f & args]
    (swap! registry-ref update-in [k] (fn [x] (apply f args))))

    (defn key-doc
    "Given a spec key returns a String of documentation if it has been provided"
    [k]
    (get-in @registry-ref [k :doc]))

    (defn key-docs-for-explain-data
    "Given explain data from a failed spec provides a Map of key -> doc"
    [explain-data]
    (->> explain-data
    ::s/problems
    vals
    (keep (comp (juxt identity key-doc) last :via))
    (filter second)
    (into {})))

    ;; can use this to test for duplicate definitions This works fine with
    ;; a reloading workflow as long as you reload the whole file.
    (let [a (atom {})]
    (defn- duplicate-key-check* [k]
    (when (get @a k)
    (throw (ex-info "Error duplicate key spec" {:k k})))
    (swap! a assoc k true)
    k))

    ;; parse out args
    (def def-key-arg-spec (s/cat :k (s/and keyword? namespace)
    :spec ::s/any
    :doc (s/? non-blank-string?)))

    (defmacro def-key
    "Defines a spec via clojure.spec/def, checks for duplicates and adds optional documentation."
    [& args]
    (let [{:keys [k spec doc default] :as res} (s/conform def-key-arg-spec args)]
    (if-not (and k spec)
    (throw (ex-info
    (with-out-str (s/explain def-key-arg-spec args))
    (or (s/explain-data def-key-arg-spec args) {})))
    `(do
    (s/def (duplicate-key-check* ~k) ~spec)
    ~(when doc
    `(update-key-data ~k merge
    {:doc ~(when doc doc)}))))))

    #_(def-key ::aasdff string? "asdfasdfasdfasdf")

    ;; * Specification for ClojureScript
    ;; ** Top level util specs

    (s/def ::string-or-symbol (s/or :string string? :symbol symbol?))
    (s/def ::string-or-named (s/or :string string? :symbol symbol? :keyword keyword?))
    ;; String should really be a non-blank string

    ;; ** ClojureScript Compiler Warnings
    (def-key ::string-or-symbol (s/or :string non-blank-string? :symbol symbol?))
    (def-key ::string-or-named (s/or :string non-blank-string? :symbol symbol? :keyword keyword?))

    (s/def :cljs-compiler-warning/undeclared-ns-form boolean?)
    (s/def :cljs-compiler-warning/protocol-deprecated boolean?)
    (s/def :cljs-compiler-warning/undeclared-protocol-symbol boolean?)
    (s/def :cljs-compiler-warning/fn-var boolean?)
    (s/def :cljs-compiler-warning/invalid-arithmetic boolean?)
    (s/def :cljs-compiler-warning/preamble-missing boolean?)
    (s/def :cljs-compiler-warning/undeclared-var boolean?)
    (s/def :cljs-compiler-warning/protocol-invalid-method boolean?)
    (s/def :cljs-compiler-warning/variadic-max-arity boolean?)
    (s/def :cljs-compiler-warning/multiple-variadic-overloads boolean?)
    (s/def :cljs-compiler-warning/fn-deprecated boolean?)
    (s/def :cljs-compiler-warning/redef boolean?)
    (s/def :cljs-compiler-warning/fn-arity boolean?)
    (s/def :cljs-compiler-warning/invalid-protocol-symbol boolean?)
    (s/def :cljs-compiler-warning/dynamic boolean?)
    (s/def :cljs-compiler-warning/undeclared-ns boolean?)
    (s/def :cljs-compiler-warning/overload-arity boolean?)
    (s/def :cljs-compiler-warning/extending-base-js-type boolean?)
    (s/def :cljs-compiler-warning/single-segment-namespace boolean?)
    (s/def :cljs-compiler-warning/protocol-duped-method boolean?)
    (s/def :cljs-compiler-warning/protocol-multiple-impls boolean?)
    (s/def :cljs-compiler-warning/invoke-ctor boolean?)
    ;; ** CLJS Compiler Options
    ;; *** Commonly used Compiler Options
    ;; **** TODO Look at the use of clojure.spec/+

    ;; ** Closure Compiler Warnings
    ;; The clojure.spec/+ is used in favor of clojure.spec/* below. The
    ;; assumption is that we want to look at the indivudiual use cases and
    ;; decide if an empty expression actually makes sense.

    (s/def :closure-warning/value #{:error :warning :off})

    (s/def :closure-warning/access-controls :closure-warning/value)
    (s/def :closure-warning/ambiguous-function-decl :closure-warning/value)
    (s/def :closure-warning/debugger-statement-present :closure-warning/value)
    (s/def :closure-warning/check-regexp :closure-warning/value)
    (s/def :closure-warning/check-types :closure-warning/value)
    (s/def :closure-warning/check-useless-code :closure-warning/value)
    (s/def :closure-warning/check-variables :closure-warning/value)
    (s/def :closure-warning/const :closure-warning/value)
    (s/def :closure-warning/constant-property :closure-warning/value)
    (s/def :closure-warning/deprecated :closure-warning/value)
    (s/def :closure-warning/duplicate-message :closure-warning/value)
    (s/def :closure-warning/es5-strict :closure-warning/value)
    (s/def :closure-warning/externs-validation :closure-warning/value)
    (s/def :closure-warning/fileoverview-jsdoc :closure-warning/value)
    (s/def :closure-warning/global-this :closure-warning/value)
    (s/def :closure-warning/internet-explorer-checks :closure-warning/value)
    (s/def :closure-warning/invalid-casts :closure-warning/value)
    (s/def :closure-warning/missing-properties :closure-warning/value)
    (s/def :closure-warning/non-standard-jsdoc :closure-warning/value)
    (s/def :closure-warning/strict-module-dep-check :closure-warning/value)
    (s/def :closure-warning/tweaks :closure-warning/value)
    (s/def :closure-warning/undefined-names :closure-warning/value)
    (s/def :closure-warning/undefined-variables :closure-warning/value)
    (s/def :closure-warning/unknown-defines :closure-warning/value)
    (s/def :closure-warning/visiblity :closure-warning/value)

    ;; ** CLJS Foreign Libs

    (s/def :cljs-foreign-libs/file string?)
    (s/def :cljs-foreign-libs/provides (s/+ string?))
    (s/def :cljs-foreign-libs/file-min string?)
    (s/def :cljs-foreign-libs/requires (s/+ string?))
    (s/def :cljs-foreign-libs/module-type #{:commonjs :amd :es6})
    (s/def :cljs-foreign-libs/preprocess ::string-or-named)

    ;; ** CLJS Compiler Modules

    (s/def :cljs-compiler-modules/output-to string?)
    (s/def :cljs-compiler-modules/entries (s/+ string?))
    (s/def :cljs-compiler-modules/depends-on (s/+ ::string-or-named))
    ;; This conflates the use case of options compliance and creating a
    ;; stricter set of options to inform users of possible missuse.

    ;; ** CLJS Compiler Options
    ;; I think it's better to have a tighter expression and force folks to
    ;; remove/comment out no-op configs, to bring awareness to them.
    ;; Commented out configs are more obvious than no-op ones.

    (def-key ::output-to string?
    "After your ClojureScript has been compiled to JavaScript, this
    specifies the name of the JavaScript output file. The contents of
    this file will differ based on the :optimizations setting.
    If :optimizations is set to :none then this file will merely contain
    the code needed to load Google Closure the and rest of the compiled
    namespaces (which are separate files).
    If :optimizations is set to :simple, :whitespace, or :advanced this
    output file will contain all the compiled code.
    :output-to \"resources/public/js/main.js\"")

    (def-key ::output-dir string?
    "Sets the output directory for output files generated during
    compilation.
    Defaults to \"out\".
    :output-dir \"resources/public/js/out\"")

    (def-key ::optimizations #{:none :whitespace :simple :advanced}
    "The optimization level. May be :none, :whitespace, :simple, or
    :advanced. Only :none and :simple are supported for bootstrapped
    ClojureScript.
    :none is the recommended setting for development
    :advanced is the recommended setting for production, unless
    something prevents it (incompatible external library, bug,
    etc.).
    For a detailed explanation of the different optimization modes see
    https://developers.google.com/closure/compiler/docs/compilation_levels
    When the :main option is not used, :none requires manual code loading
    and hence a separate HTML from the other options.
    Defaults to :none. Note: lein cljsbuild 1.0.5 will supply :whitespace.
    :optimizations :none")

    (def-key ::main ::string-or-symbol
    "Specifies an entry point namespace. When combined with optimization
    level :none, :main will cause the compiler to emit a single JavaScript
    file that will import goog/base.js, the JavaScript file for the
    namespace, and emit the required goog.require statement. This permits
    leaving HTML markup identical between dev and production.
    Also see :asset-path.
    :main \"example.core\"")

    (def-key ::asset-path string?
    "When using :main it is often necessary to control where the entry
    point script attempts to load scripts from due to the configuration of
    the web server. :asset-path is a relative URL path not a file system
    path. For example, if your output directory is :ouput-dir
    \"resources/public/js/compiled/out\" but your webserver is serving files
    from \"resources/public\" then you want the entry point script to load
    scripts from \"js/compiled/out\".
    :asset-path \"js/compiled/out\"")

    (def-key ::source-map (s/or :bool boolean? :string string?)
    "See https://github.com/clojure/clojurescript/wiki/Source-maps. Under
    optimizations :none the valid values are true and false, with the
    default being true. Under all other optimization settings must specify
    a path to where the source map will be written.
    Under :simple, :whitespace, or :advanced
    :source-map \"path/to/source/map.js.map\"")


    (def-key ::preloads (s/+ symbol?)
    "Developing ClojureScript commonly requires development time only
    side effects such as enabling printing, logging, spec instrumentation,
    and connecting REPLs. :preloads permits loading such side effect
    boilerplate right after cljs.core. For example you can make a
    development namespace for enabling printing in browsers:
    (ns foo.dev)
    (enable-console-print!)
    Now you can configure your development build to load this side effect
    prior to your main namespace with the following compiler options:
    {:preloads [foo.dev]
    :main \"foo.core\"
    :output-dir \"out\"}
    The :preloads config value must be a sequence of symbols that map to
    existing namespaces discoverable on the classpath.")

    (def-key ::verbose boolean?
    "Emit details and measurements from compiler activity.
    (s/def :cljs-compiler/main ::string-or-symbol)
    (s/def :cljs-compiler/preloads (s/+ symbol?))
    (s/def :cljs-compiler/asset-path string?)
    (s/def :cljs-compiler-options/output-to string?)

    (s/def :cljs-compiler/output-dir string?)
    (s/def :cljs-compiler/optimizations #{:none :whitespace :simple :advanced})

    (s/def :cljs-compiler/source-map (s/or :bool boolean? :string string?))
    (s/def :cljs-compiler/verbose boolean?)
    (s/def :cljs-compiler/pretty-print boolean?)
    (s/def :cljs-compiler/target #{:nodejs})


    (s/def :cljs-compiler/foreign-libs (s/keys
    :req-un [:cljs-foreign-libs/file
    :cljs-foreign-libs/provides]
    :opt-un [:cljs-foreign-libs/file-min
    :cljs-foreign-libs/requires
    :cljs-foreign-libs/module-type
    :cljs-foreign-libs/preprocess]))

    (s/def :cljs-compiler/externs (s/+ string?))

    (s/def :cljs-compiler/modules (s/map-of
    keyword?
    (s/keys
    :req-un [:cljs-compiler-modules/output-to
    :cljs-compiler-modules/entries]
    :opt-un [:cljs-compiler-modules/depends-on])))

    (s/def :cljs-compiler/source-map-path string?)
    (s/def :cljs-compiler/source-map-timestamp boolean?)
    (s/def :cljs-compiler/cache-analysis boolean?)
    (s/def :cljs-compiler/recompile-dependents boolean?)
    (s/def :cljs-compiler/static-fns boolean?)

    (s/def :cljs-compiler/elide-asserts boolean?)
    (s/def :cljs-compiler/pseudo-names boolean?)
    (s/def :cljs-compiler/print-input-delimiter boolean?)
    (s/def :cljs-compiler/output-wrapper boolean?)
    (s/def :cljs-compiler/libs (s/+ string?))
    (s/def :cljs-compiler/preamble (s/+ string?))
    (s/def :cljs-compiler/hashbang boolean?)
    (s/def :cljs-compiler/compiler-stats boolean?)
    (s/def :cljs-compiler/language-in #{:ecmascript3 :ecmascript5 :ecmascript5-strict})
    (s/def :cljs-compiler/language-out #{:ecmascript3 :ecmascript5 :ecmascript5-strict})

    (s/def :cljs-compiler/closure-defines (s/map-of
    ::string-or-symbol
    (s/or :number number?
    :string string?
    :bool boolean?)))
    (s/def :cljs-compiler/closure-extra-annotations (s/+ string?))
    (s/def :cljs-compiler/anon-fn-naming-policy #{:off :unmapped :mapped})
    (s/def :cljs-compiler/optimize-constants boolean?)
    (s/def :cljs-compiler/parallel-build boolean?)
    (s/def :cljs-compiler/dump-core boolean?)
    (s/def :cljs-compiler/emit-constants boolean?)
    (s/def :cljs-compiler/warning-handlers (s/+ ::s/any)) ;; symbol, string, or fn?
    (s/def :cljs-compiler/source-map-inline boolean?)
    (s/def :cljs-compiler/ups-libs (s/+ string?))
    (s/def :cljs-compiler/ups-externs (s/+ string?))
    (s/def :cljs-compiler/ups-foreign-libs (s/+ :cljs-compiler/foreign-libs))
    (s/def :cljs-compiler/closure-output-charset string?)

    (s/def :cljs-compiler/warnings
    :verbose true")

    (def-key ::pretty-print boolean?
    "Determines whether the JavaScript output will be tabulated in a
    human-readable manner. Defaults to true.
    :pretty-print false")

    (def-key ::target #{:nodejs}
    "If targeting nodejs add this line. Takes no other options at the
    moment. The default (no :target specified) implies browsers are being
    targeted. Have a look here for more information on how to run your
    code in nodejs.
    :target :nodejs")

    (def-key ::foreign-libs
    (s/keys
    :req-un [::file
    ::provides]
    :opt-un [::file-min
    ::requires
    ::module-type
    ::preprocess])
    "Adds dependencies on foreign libraries. Be sure that the url returns a
    HTTP Code 200
    Defaults to the empty vector []
    :foreign-libs [{ :file \"http://example.com/remote.js\"
    :provides [\"my.example\"]}]
    Each element in the :foreign-libs vector should be a map, where the
    keys have these semantics:
    :file Indicates the URL to the library
    :file-min (Optional) Indicates the URL to the minified variant of
    the library.
    :provides A synthetic namespace that is associated with the library.
    This is typically a vector with a single string, but it
    has the capability of specifying multiple namespaces
    (typically used only by Google Closure libraries).
    :requires (Optional) A vector explicitly identifying dependencies
    (:provides values from other foreign libs); used to form a
    topological sort honoring dependencies.
    :module-type (Optional) indicates that the foreign lib uses a given
    module system. Can be one of :commonjs, :amd, :es6.
    Note that if supplied, :requires is not used (as it is
    implicitly determined).
    :preprocess (Optional) Used to preprocess / transform code in other
    dialects (JSX, etc.). A defmethod for
    cljs.clojure/js-transforms must be provided that matches
    the supplied value in order to effect the desired code
    transformation.")

    (def-key ::file string?)
    (def-key ::provides (s/+ string?))
    (def-key ::file-min string?)
    (def-key ::requires (s/+ string?))
    (def-key ::module-type #{:commonjs :amd :es6})
    (def-key ::preprocess ::string-or-named)

    (def-key ::externs (s/+ string?)
    "Configure externs files for external libraries.
    For this option, and those below, you can find a very good explanation at:
    http://lukevanderhart.com/2011/09/30/using-javascript-and-clojurescript.html
    Defaults to the empty vector [].
    :externs [\"jquery-externs.js\"]")

    (def-key ::modules
    (s/map-of
    keyword?
    (s/keys
    :req-un [:cljs.options-schema.modules/output-dir
    ::entries]
    :opt-un [::depends-on]))
    "A new option for emitting Google Closure Modules. Closure Modules
    supports splitting up an optimized build into N different modules. If
    :modules is supplied it replaces the single :output-to. A module needs
    a name, an individual :output-to file path, :entries a set of
    namespaces, and :depends-on a set of modules on which the module
    depends. Modules are only supported with :simple and :advanced
    optimizations. An example follows:
    {:optimizations :advanced
    :source-map true
    :output-dir \"resources/public/js\"
    :modules {
    :common
    {:output-to \"resources/public/js/common.js\"
    :entries #{\"com.foo.common\"}}
    :landing
    {:output-to \"resources/public/js/landing.js\"
    :entries #{\"com.foo.landing\"}
    :depends-on #{:common}}
    :editor
    {:output-to \"resources/public/js/editor.js\"
    :entries #{\"com.foo.editor\"}
    :depends-on #{:common}}}}
    Any namespaces not in an :entries set will be moved into the default
    module :cljs-base. However thanks to cross module code motion, Google
    Closure can move functions and methods into the modules where they are
    actually used. This process is somewhat conservative so if you know
    that you want to keep some code together do this via :entries.
    The :cljs-base module defaults to being written out to :output-dir
    with the name \"cljs_base.js\". This may be overridden by specifying a
    :cljs-base module describing only :output-to.
    Take careful note that a namespace may only appear once across all
    module :entries.
    :modules fully supports :foreign-libs. :foreign-libs are always put
    into dependency order before any Google Closure compiled source.
    Source maps are fully supported, an individual one will be created for
    each module. Just supply :source-map true (see example) as there is no
    single source map to name.")

    ;; ** TODO name collision don't want docs to collide
    ;; this is the only name collision in this
    (def-key :cljs.options-schema.modules/output-dir string?)
    (def-key ::entries (s/+ string?))
    (def-key ::depends-on (s/+ ::string-or-named))

    (def-key ::source-map-path string?
    "Set the path to source files references in source maps to avoid
    further web server configuration.
    :source-map-path \"public/js\"")

    (def-key ::source-map-timestamp boolean?
    "Add cache busting timestamps to source map urls. This is helpful for
    keeping source maps up to date when live reloading code.
    :source-map-timestamp true")

    (def-key ::cache-analysis boolean?
    "Experimental. Cache compiler analysis to disk. This enables faster
    cold build and REPL start up times.
    For REPLs, defaults to true. Otherwise, defaults to true if and only
    if :optimizations is :none.
    :cache-analysis true")

    (def-key ::recompile-dependents boolean?
    "For correctness the ClojureScript compiler now always recompiles
    dependent namespaces when a parent namespace changes. This prevents
    corrupted builds and swallowed warnings. However this can impact
    compile times depending on the structure of the application. This
    option defaults to true.
    :recompile-dependents false")

    (def-key ::static-fns boolean?
    "Employs static dispatch to specific function arities in emitted
    JavaScript, as opposed to making use of the call construct. Defaults
    to false except under advanced optimizations. Useful to have set to
    false at REPL development to facilitate function redefinition, and
    useful to set to true for release for performance.
    This setting does not apply to the standard library, which is always
    compiled with :static-fns implicitly set to true.
    :static-fns true")

    ;; (def-key ::warnings (ref-schema 'CompilerWarnings))

    (def-key ::elide-asserts boolean?
    "This flag will cause all (assert x) calls to be removed during
    compilation, including implicit asserts associated with :pre and :post
    conditions. Useful for production. Default is always false even in
    advanced compilation. Does NOT specify goog.asserts.ENABLE_ASSERTS,
    which is different and used by the Closure library.
    Note that it is currently not possible to dynamically set *assert* to
    false at runtime; this compiler flag must explicitly be used to effect
    the elision.
    :elide-asserts true")

    (def-key ::pseudo-names boolean?
    "With :advanced mode optimizations, determines whether readable names
    are emitted. This can be useful when debugging issues in the optimized
    JavaScript and can aid in finding missing externs. Defaults to false.
    :pseudo-names true")

    (def-key ::print-input-delimiter boolean?
    "Determines whether comments will be output in the JavaScript that can
    be used to determine the original source of the compiled code.
    Defaults to false.
    :print-input-delimiter false")

    (def-key ::output-wrapper boolean?
    "Wrap the JavaScript output in (function(){...};)() to avoid clobbering
    globals. Defaults to false.
    :output-wrapper false")

    (def-key ::libs (s/+ string?)
    "Adds dependencies on external js libraries, i.e. Google
    Closure-compatible javascript files with correct goog.provides() and
    goog.requires() calls. Note that files in these directories will be
    watched and a rebuild will occur if they are modified.
    Paths or filenames can be given. Relative paths are relative to the
    current working directory (usually project root).
    Defaults to the empty vector []
    :libs [\"closure/library/third_party/closure\"
    \"src/js\"
    \"src/org/example/example.js\"]")

    (def-key ::preamble (s/+ string?)
    "Prepends the contents of the given files to each output file. Only
    valid with optimizations other than :none.
    Defaults to the empty vector []
    :preamble [\"license.js\"]")

    (def-key ::hashbang boolean?
    "When using :target :nodejs the compiler will emit a shebang as the
    first line of the compiled source, making it executable. When your
    intention is to build a node.js module, instead of executable, use
    this option to remove the shebang.
    :hashbang false")

    (def-key ::compiler-stats boolean?
    "Report basic timing measurements on compiler activity.
    Defaults to false.
    :compiler-stats true")

    (def-key ::language-in #{:ecmascript3 :ecmascript5 :ecmascript5-strict}
    "Configure the input and output languages for the closure library. May
    be :ecmascript3, ecmascript5, ecmascript5-strict, :ecmascript6-typed,
    :ecmascript6-strict, :ecmascript6 or :no-transpile.
    Defaults to :ecmascript3
    :language-in :ecmascript3")

    (def-key ::language-out #{:ecmascript3 :ecmascript5 :ecmascript5-strict}
    "Configure the input and output languages for the closure library. May
    be :ecmascript3, ecmascript5, ecmascript5-strict, :ecmascript6-typed,
    :ecmascript6-strict, :ecmascript6 or :no-transpile.
    Defaults to :ecmascript3
    :language-out :ecmascript3")

    (def-key ::closure-defines
    (s/map-of
    ::string-or-symbol
    (s/or :number number? :string string? :bool boolean?))
    "Set the values of Closure libraries' variables annotated with @define
    or with the cljs.core/goog-define helper macro. A common usage is
    setting goog.DEBUG to false:
    :closure-defines {\"goog.DEBUG\" false}
    or
    :closure-defines {'goog.DEBUG false}
    Note when using Lein the quote is unnecessary due to implicit quoting.
    For :optimization :none, a :main option must be specified for defines
    to work, and only goog-define defines are affected. :closure-defines
    currently does not have any effect with :optimization :whitespace.")

    (def-key ::closure-extra-annotations (s/+ string?)
    "Define extra JSDoc annotations that a closure library might use so
    that they don't trigger compiler warnings.
    :closure-extra-annotations #{\"api\"}")

    (def-key ::anon-fn-naming-policy #{:off :unmapped :mapped}
    "Strategies for how the Google Closure compiler does naming of
    anonymous functions that occur as r-values in assignments and variable
    declarations. Defaults to :off.
    :anon-fn-naming-policy :unmapped
    The following values are supported:
    :off Don't give anonymous functions names.
    :unmapped Generates names that are based on the left-hand side of
    the assignment. Runs after variable and property renaming,
    so that the generated names will be short and obfuscated.
    :mapped Generates short unique names and provides a mapping from
    them back to a more meaningful name that's based on the
    left-hand side of the assignment.")

    (def-key ::optimize-constants boolean?
    "When set to true, constants, such as keywords and symbols, will only
    be created once and will be written to a separate file called
    constants_table.js. The compiler will emit a reference to the constant
    as defined in the constants table instead of creating a new object for
    it. This option is mainly intended to be used for a release build
    since it can increase performance due to decreased allocation.
    Defaults to true under :advanced optimizations otherwise to false.
    :optimize-constants true")

    (def-key ::parallel-build boolean?
    "When set to true, compile source in parallel, utilizing multiple cores.
    :parallel-build true")

    (def-key ::devcards boolean?
    "Whether to include devcard 'defcard' definitions in the output of the compile.")

    (def-key ::watch-fn fn?
    "Is a function that will be called after a successful build.
    Only available for cljs.build.api/watch
    :watch-fn (fn [] (println \"Updated build\"))")

    (def-key ::dump-core boolean?)
    (def-key ::emit-constants boolean?)
    (def-key ::warning-handlers (s/+ ::s/any)) ;; symbol, string, or fn?
    (def-key ::source-map-inline boolean?)
    (def-key ::ups-libs (s/+ string?))
    (def-key ::ups-externs (s/+ string?))
    (def-key ::ups-foreign-libs (s/+ ::foreign-libs))
    (def-key ::closure-output-charset string?)
    (def-key ::external-config (s/map-of keyword? map?))

    ;; ** ClojureScript Compiler Warnings

    (def-key ::warnings
    (s/keys
    :opt-un
    [:cljs-compiler-warning/undeclared-ns-form
    :cljs-compiler-warning/protocol-deprecated
    :cljs-compiler-warning/undeclared-protocol-symbol
    :cljs-compiler-warning/fn-var
    :cljs-compiler-warning/invalid-arithmetic
    :cljs-compiler-warning/preamble-missing
    :cljs-compiler-warning/undeclared-var
    :cljs-compiler-warning/protocol-invalid-method
    :cljs-compiler-warning/variadic-max-arity
    :cljs-compiler-warning/multiple-variadic-overloads
    :cljs-compiler-warning/fn-deprecated
    :cljs-compiler-warning/redef
    :cljs-compiler-warning/fn-arity
    :cljs-compiler-warning/invalid-protocol-symbol
    :cljs-compiler-warning/dynamic
    :cljs-compiler-warning/undeclared-ns
    :cljs-compiler-warning/overload-arity
    :cljs-compiler-warning/extending-base-js-type
    :cljs-compiler-warning/single-segment-namespace
    :cljs-compiler-warning/protocol-duped-method
    :cljs-compiler-warning/protocol-multiple-impls
    :cljs-compiler-warning/invoke-ctor]))

    (s/def :cljs-compiler/closure-warnings
    [::undeclared-ns-form
    ::protocol-deprecated
    ::undeclared-protocol-symbol
    ::fn-var
    ::invalid-arithmetic
    ::preamble-missing
    ::undeclared-var
    ::protocol-invalid-method
    ::variadic-max-arity
    ::multiple-variadic-overloads
    ::fn-deprecated
    ::redef
    ::fn-arity
    ::invalid-protocol-symbol
    ::dynamic
    ::undeclared-ns
    ::overload-arity
    ::extending-base-js-type
    ::single-segment-namespace
    ::protocol-duped-method
    ::protocol-multiple-impls
    ::invoke-ctor])
    "This flag will turn on/off compiler warnings for references to
    undeclared vars, wrong function call arities, etc. Can be a boolean
    for enabling/disabling common warnings, or a map of specific warning
    keys with associated booleans. Defaults to true.
    :warnings true
    ;; OR
    :warnings {:fn-deprecated false} ;; suppress this warning
    The following warnings are supported:
    :preamble-missing, missing preamble
    :undeclared-var, undeclared var
    :undeclared-ns, var references non-existent namespace
    :undeclared-ns-form, namespace reference in ns form that does not exist
    :redef, var redefinition
    :dynamic, dynamic binding of non-dynamic var
    :fn-var, var previously bound to fn changed to different type
    :fn-arity, invalid invoke arity
    :fn-deprecated, deprecated function usage
    :protocol-deprecated, deprecated protocol usage
    :undeclared-protocol-symbol, undeclared protocol referred
    :invalid-protocol-symbol, invalid protocol symbol
    :multiple-variadic-overloads, multiple variadic arities
    :variadic-max-arity, arity greater than variadic arity
    :overload-arity, duplicate arities
    :extending-base-js-type, JavaScript base type extension
    :invoke-ctor, type constructor invoked as function
    :invalid-arithmetic, invalid arithmetic
    :protocol-invalid-method, protocol method does not match declaration
    :protocol-duped-method, duplicate protocol method implementation
    :protocol-multiple-impls, protocol implemented multiple times
    :single-segment-namespace, single segment namespace")

    ;; *** TODO differnet ns??
    (def-key ::undeclared-ns-form boolean?)
    (def-key ::protocol-deprecated boolean?)
    (def-key ::undeclared-protocol-symbol boolean?)
    (def-key ::fn-var boolean?)
    (def-key ::invalid-arithmetic boolean?)
    (def-key ::preamble-missing boolean?)
    (def-key ::undeclared-var boolean?)
    (def-key ::protocol-invalid-method boolean?)
    (def-key ::variadic-max-arity boolean?)
    (def-key ::multiple-variadic-overloads boolean?)
    (def-key ::fn-deprecated boolean?)
    (def-key ::redef boolean?)
    (def-key ::fn-arity boolean?)
    (def-key ::invalid-protocol-symbol boolean?)
    (def-key ::dynamic boolean?)
    (def-key ::undeclared-ns boolean?)
    (def-key ::overload-arity boolean?)
    (def-key ::extending-base-js-type boolean?)
    (def-key ::single-segment-namespace boolean?)
    (def-key ::protocol-duped-method boolean?)
    (def-key ::protocol-multiple-impls boolean?)
    (def-key ::invoke-ctor boolean?)

    ;; ** Closure Compiler Warnings

    (def-key ::closure-warnings
    (s/keys
    :opt-un
    [:closure-warning/access-controls
    :closure-warning/ambiguous-function-decl
    :closure-warning/debugger-statement-present
    :closure-warning/check-regexp
    :closure-warning/check-types
    :closure-warning/check-useless-code
    :closure-warning/check-variables
    :closure-warning/const
    :closure-warning/constant-property
    :closure-warning/deprecated
    :closure-warning/duplicate-message
    :closure-warning/es5-strict
    :closure-warning/externs-validation
    :closure-warning/fileoverview-jsdoc
    :closure-warning/global-this
    :closure-warning/internet-explorer-checks
    :closure-warning/invalid-casts
    :closure-warning/missing-properties
    :closure-warning/non-standard-jsdoc
    :closure-warning/strict-module-dep-check
    :closure-warning/tweaks
    :closure-warning/undefined-names
    :closure-warning/undefined-variables
    :closure-warning/unknown-defines
    :closure-warning/visiblity]))
    [::access-controls
    ::ambiguous-function-decl
    ::debugger-statement-present
    ::check-regexp
    ::check-types
    ::check-useless-code
    ::check-variables
    ::const
    ::constant-property
    ::deprecated
    ::duplicate-message
    ::es5-strict
    ::externs-validation
    ::fileoverview-jsdoc
    ::global-this
    ::internet-explorer-checks
    ::invalid-casts
    ::missing-properties
    ::non-standard-jsdoc
    ::strict-module-dep-check
    ::tweaks
    ::undefined-names
    ::undefined-variables
    ::unknown-defines
    ::visiblity])
    "Configure warnings generated by the Closure compiler. A map from
    Closure warning to configuration value, only :error, :warning and :off
    are supported.
    :closure-warnings {:externs-validation :off}
    The following Closure warning options are exposed to ClojureScript:
    :access-controls
    :ambiguous-function-decl
    :debugger-statement-present
    :check-regexp
    :check-types
    :check-useless-code
    :check-variables
    :const
    :constant-property
    :deprecated
    :duplicate-message
    :es5-strict
    :externs-validation
    :fileoverview-jsdoc
    :global-this
    :internet-explorer-checks
    :invalid-casts
    :missing-properties
    :non-standard-jsdoc
    :strict-module-dep-check
    :tweaks
    :undefined-names
    :undefined-variables
    :unknown-defines
    :visiblity
    See the Closure Compiler Warning wiki for detailed descriptions.")

    (def-key ::warning-value #{:error :warning :off})

    ;; *** TODO differnet ns??
    (def-key ::access-controls ::warning-value)
    (def-key ::ambiguous-function-decl ::warning-value)
    (def-key ::debugger-statement-present ::warning-value)
    (def-key ::check-regexp ::warning-value)
    (def-key ::check-types ::warning-value)
    (def-key ::check-useless-code ::warning-value)
    (def-key ::check-variables ::warning-value)
    (def-key ::const ::warning-value)
    (def-key ::constant-property ::warning-value)
    (def-key ::deprecated ::warning-value)
    (def-key ::duplicate-message ::warning-value)
    (def-key ::es5-strict ::warning-value)
    (def-key ::externs-validation ::warning-value)
    (def-key ::fileoverview-jsdoc ::warning-value)
    (def-key ::global-this ::warning-value)
    (def-key ::internet-explorer-checks ::warning-value)
    (def-key ::invalid-casts ::warning-value)
    (def-key ::missing-properties ::warning-value)
    (def-key ::non-standard-jsdoc ::warning-value)
    (def-key ::strict-module-dep-check ::warning-value)
    (def-key ::tweaks ::warning-value)
    (def-key ::undefined-names ::warning-value)
    (def-key ::undefined-variables ::warning-value)
    (def-key ::unknown-defines ::warning-value)
    (def-key ::visiblity ::warning-value)

    ;; * Global Option Map Constraints

    ;; There are some global option map contrants that we'd probably like
    ;; to generate warnings and errors for.

    ;; ** Attach Warning

    ;; There are situations that we just want to warn about. Here is a spec
    ;; that will print out a warning and will always pass.

    (defn attach-warning-impl [message-or-fn parent-spec]
    {:pre [(or (fn? message-or-fn) (string? message-or-fn))]}
    (reify
    clojure.lang.IFn
    (invoke [this x] (parent-spec x))
    clojure.spec/Spec
    (conform* [_ x]
    (if (s/valid? parent-spec x)
    (s/conform* parent-spec x)
    (do
    (println "CLJS Compiler Options Warning: " (if (string? message-or-fn)
    message-or-fn
    (message-or-fn x)))
    x)))
    (unform* [_ x] x)
    (explain* [_ path via in x] nil)
    ;; These can be improved
    (gen* [_ a b c]
    (s/gen* parent-spec a b c))
    (with-gen* [_ gfn]
    (s/with-gen* parent-spec gfn))
    (describe* [_] (cons 'attach-warning (s/describe* parent-spec)))))

    (defmacro attach-warning [reason spec]
    `(attach-warning-impl ~reason (s/spec ~spec)))

    #_(s/def ::funnerer (s/and
    (attach-warning "If we have a we really need b"
    (fn [a] (if (:a a)
    (:b a) true)))
    map?))

    #_(s/conform ::funnerer {:a 1})

    ;; ** Attach Reason

    ;; attach-reason is a spec that attaches a :reason to a failing specs
    ;; explain-data this :reason is printed out as part of the message
    ;; generated by clojure.spec/explain

    (defn attach-reason-impl [reason parent-spec]
    {:pre [(string? reason)]}
    (reify
    clojure.lang.IFn
    (invoke [this x] (parent-spec x))
    clojure.spec/Spec
    (conform* [_ x] (s/conform* parent-spec x))
    (unform* [_ x] (s/unform* parent-spec x))
    (explain* [_ path via in x]
    (not-empty
    (into {}
    (map (fn [[k v]] [k (assoc v :reason (str (get v :reason) "\n - " reason))])
    (s/explain* parent-spec path via in x)))))
    ;; These can be improved
    (gen* [_ a b c]
    (s/gen* parent-spec a b c))
    (with-gen* [_ gfn]
    (s/with-gen* parent-spec gfn))
    (describe* [_] (s/describe* parent-spec))))

    (defmacro attach-reason [reason spec]
    `(attach-reason-impl ~reason (s/spec ~spec)))

    #_ (s/def ::funner (attach-reason "Whoa something happened!"
    (attach-reason "If we have a we really need b"
    (fn [a] (if (:a a)
    (:b a) true)))))

    #_(s/explain ::funner {:a 1})

    ;; opt none helper
    (defn- opt-none? [opt]
    (or (nil? opt) (= :none opt)))

    ;; ** The Top level Options Map for the cljs/build fn
    (def-key ::compiler-options
    (s/and
    (attach-warning ":asset-path has no effect without a :main"
    (fn [{:keys [asset-path main]}]
    (not (and (some? asset-path)
    (nil? main)))))

    (attach-warning ":pseudo-names has no effect when :optimizations is not :advanced"
    (fn [{:keys [pseudo-names optimizations]}]
    (not (and (some? pseudo-names) (not= optimizations :advanced)))))

    ;; **** TODO add in the cljs.compiler unknown/similar-key warning here

    ;; these next warnings probably be elevated to an errors attach-reason
    (attach-warning ":preamble has no effect when :optimizations is not :none"
    (fn [{:keys [preamble optimizations]}]
    (not (and (some? preamble)
    (not (opt-none? optimizations))))))

    (attach-warning ":hashbang has no effect when :target is not :nodejs"
    (fn [{:keys [hashbang target]}]
    (not (and (some? hashbang) (not= target :nodejs)))))

    (attach-warning ":clojure-defines has no effect when :optimizations is :whitespace"
    (fn [{:keys [closure-defines optimizations]}]
    (not (and (some? closure-defines)
    (= :whitespace optimizations)))))

    (attach-warning "missing an :output-to option - you probably will want this ..."
    (fn [{:keys [output-to]}] (some? output-to)))

    (attach-reason ":closure-defines requires a :main when :optimizations is :none"
    (fn [{:keys [closure-defines optimizations main]}]
    (not (and (some? closure-defines)
    (nil? main)
    (opt-none? optimizations)))))

    (attach-reason ":source-map must be a boolean when :optimizations is :none"
    (fn [{:keys [source-map optimizations]}]
    (not (and
    (some? source-map)
    (not (boolean? source-map))
    (opt-none? optimizations)))))

    (attach-reason ":source-map must be a string? when :optimizations is not :none"
    (fn [{:keys [source-map optimizations]}]
    (not (and
    (some? source-map)
    (not (string? source-map))
    (not (opt-none? optimizations))))))

    (s/keys
    :opt-un
    [::main
    ::preloads
    ::asset-path
    ::output-to
    ::output-dir
    ::optimizations
    ::source-map
    ::verbose
    ::pretty-print
    ::target
    ::foreign-libs
    ::externs
    ::modules
    ::source-map-path
    ::source-map-timestamp
    ::cache-analysis
    ::recompile-dependents
    ::static-fns
    ::elide-asserts
    ::pseudo-names
    ::print-input-delimiter
    ::output-wrapper
    ::libs
    ::preamble
    ::hashbang
    ::compiler-stats
    ::language-in
    ::language-out
    ::closure-defines
    ::closure-extra-annotations
    ::anon-fn-naming-policy
    ::optimize-constants
    ::parallel-build
    ::devcards
    ::dump-core
    ::emit-constants
    ::warning-handlers
    ::source-map-inline
    ::ups-libs
    ::ups-externs
    ::ups-foreign-libs
    ::closure-output-charset
    ::external-config
    ::watch-fn])))

    (comment

    (s/conform ::compiler-options {:asset-path "asdf/asdf"})
    (s/explain ::compiler-options {:source-map "asdf" :optimizations :none})
    (s/explain ::compiler-options {:source-map false :optimizations :simple})

    (key-docs-for-explain-data (s/explain-data ::compiler-options {:output-wrapper 1}))

    (def not-blank? (complement string/blank?))

    (deftest verify-warnings-and-reasons
    ;; no warnings or errors on a perectly valid empty options
    (is (s/valid? ::compiler-options {}))
    (is (string/blank? (with-out-str (s/valid? ::compiler-options {:output-to "main.js"}))))

    (testing "warnings produce warnings and are still valid"
    ;; no warning produced
    ;; :asset-path
    (is (not-blank? (with-out-str (s/valid? ::compiler-options {:asset-path "asdf/asdf"}))))
    (is (s/valid? ::compiler-options {:asset-path "asdf/asdf"}))

    ;; pseudo-names
    (is (not-blank? (with-out-str (s/valid? ::compiler-options {:pseudo-names true}))))
    (is (s/valid? ::compiler-options {:pseudo-names true}))

    ;; preamble
    (is (not-blank? (with-out-str (s/valid? ::compiler-options {:preamble ["asdf"] :optimizations :advanced}))))
    (is (s/valid? ::compiler-options {:preamble ["asdf"] :optimizations :advanced}))

    ;; hash-bang
    (is (not-blank? (with-out-str (s/valid? ::compiler-options {:hashbang true}))))
    (is (s/valid? ::compiler-options {:hashbang true}))

    ;; clojure-defines
    (is (not-blank? (with-out-str (s/valid? ::compiler-options {:closure-defines {'goog.DEBUG false}
    :optimizations :whitespace}))))
    (is (s/valid? ::compiler-options {:closure-defines {'goog.DEBUG false}
    :optimizations :whitespace}))

    )

    (testing "fail on attach-reason predicates"
    (is (not (s/valid? ::compiler-options {:closure-defines {'goog.DEBUG false}})))
    (is (not (s/valid? ::compiler-options {:source-map "asdf"})))
    (is (not (s/valid? ::compiler-options {:source-map false :optimizations :advanced})))
    )

    (is (s/valid? ::compiler-options { :optimizations :advanced})))

    (clojure.test/run-tests 'strictly-specking.cljs-options-schema)

    )
  12. Bruce Hauman revised this gist Jun 27, 2016. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions options_schema.cljs
    Original file line number Diff line number Diff line change
    @@ -145,8 +145,6 @@
    (s/def :cljs-compiler/ups-externs (s/+ string?))
    (s/def :cljs-compiler/ups-foreign-libs (s/+ :cljs-compiler/foreign-libs))
    (s/def :cljs-compiler/closure-output-charset string?)
    (s/def :cljs-compiler/external-config (s/map-of keyword? map?))


    (s/def :cljs-compiler/warnings
    (s/keys
  13. Bruce Hauman revised this gist Jun 27, 2016. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion options_schema.cljs
    Original file line number Diff line number Diff line change
    @@ -137,7 +137,6 @@
    (s/def :cljs-compiler/anon-fn-naming-policy #{:off :unmapped :mapped})
    (s/def :cljs-compiler/optimize-constants boolean?)
    (s/def :cljs-compiler/parallel-build boolean?)
    (s/def :cljs-compiler/devcards boolean?)
    (s/def :cljs-compiler/dump-core boolean?)
    (s/def :cljs-compiler/emit-constants boolean?)
    (s/def :cljs-compiler/warning-handlers (s/+ ::s/any)) ;; symbol, string, or fn?
  14. Bruce Hauman created this gist Jun 27, 2016.
    206 changes: 206 additions & 0 deletions options_schema.cljs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,206 @@
    (ns cljs.option-schema
    (:require
    [clojure.spec :as s]))

    ;; * Specification for ClojureScript
    ;; ** Top level util specs

    (s/def ::string-or-symbol (s/or :string string? :symbol symbol?))
    (s/def ::string-or-named (s/or :string string? :symbol symbol? :keyword keyword?))

    ;; ** ClojureScript Compiler Warnings

    (s/def :cljs-compiler-warning/undeclared-ns-form boolean?)
    (s/def :cljs-compiler-warning/protocol-deprecated boolean?)
    (s/def :cljs-compiler-warning/undeclared-protocol-symbol boolean?)
    (s/def :cljs-compiler-warning/fn-var boolean?)
    (s/def :cljs-compiler-warning/invalid-arithmetic boolean?)
    (s/def :cljs-compiler-warning/preamble-missing boolean?)
    (s/def :cljs-compiler-warning/undeclared-var boolean?)
    (s/def :cljs-compiler-warning/protocol-invalid-method boolean?)
    (s/def :cljs-compiler-warning/variadic-max-arity boolean?)
    (s/def :cljs-compiler-warning/multiple-variadic-overloads boolean?)
    (s/def :cljs-compiler-warning/fn-deprecated boolean?)
    (s/def :cljs-compiler-warning/redef boolean?)
    (s/def :cljs-compiler-warning/fn-arity boolean?)
    (s/def :cljs-compiler-warning/invalid-protocol-symbol boolean?)
    (s/def :cljs-compiler-warning/dynamic boolean?)
    (s/def :cljs-compiler-warning/undeclared-ns boolean?)
    (s/def :cljs-compiler-warning/overload-arity boolean?)
    (s/def :cljs-compiler-warning/extending-base-js-type boolean?)
    (s/def :cljs-compiler-warning/single-segment-namespace boolean?)
    (s/def :cljs-compiler-warning/protocol-duped-method boolean?)
    (s/def :cljs-compiler-warning/protocol-multiple-impls boolean?)
    (s/def :cljs-compiler-warning/invoke-ctor boolean?)

    ;; ** Closure Compiler Warnings

    (s/def :closure-warning/value #{:error :warning :off})

    (s/def :closure-warning/access-controls :closure-warning/value)
    (s/def :closure-warning/ambiguous-function-decl :closure-warning/value)
    (s/def :closure-warning/debugger-statement-present :closure-warning/value)
    (s/def :closure-warning/check-regexp :closure-warning/value)
    (s/def :closure-warning/check-types :closure-warning/value)
    (s/def :closure-warning/check-useless-code :closure-warning/value)
    (s/def :closure-warning/check-variables :closure-warning/value)
    (s/def :closure-warning/const :closure-warning/value)
    (s/def :closure-warning/constant-property :closure-warning/value)
    (s/def :closure-warning/deprecated :closure-warning/value)
    (s/def :closure-warning/duplicate-message :closure-warning/value)
    (s/def :closure-warning/es5-strict :closure-warning/value)
    (s/def :closure-warning/externs-validation :closure-warning/value)
    (s/def :closure-warning/fileoverview-jsdoc :closure-warning/value)
    (s/def :closure-warning/global-this :closure-warning/value)
    (s/def :closure-warning/internet-explorer-checks :closure-warning/value)
    (s/def :closure-warning/invalid-casts :closure-warning/value)
    (s/def :closure-warning/missing-properties :closure-warning/value)
    (s/def :closure-warning/non-standard-jsdoc :closure-warning/value)
    (s/def :closure-warning/strict-module-dep-check :closure-warning/value)
    (s/def :closure-warning/tweaks :closure-warning/value)
    (s/def :closure-warning/undefined-names :closure-warning/value)
    (s/def :closure-warning/undefined-variables :closure-warning/value)
    (s/def :closure-warning/unknown-defines :closure-warning/value)
    (s/def :closure-warning/visiblity :closure-warning/value)

    ;; ** CLJS Foreign Libs

    (s/def :cljs-foreign-libs/file string?)
    (s/def :cljs-foreign-libs/provides (s/+ string?))
    (s/def :cljs-foreign-libs/file-min string?)
    (s/def :cljs-foreign-libs/requires (s/+ string?))
    (s/def :cljs-foreign-libs/module-type #{:commonjs :amd :es6})
    (s/def :cljs-foreign-libs/preprocess ::string-or-named)

    ;; ** CLJS Compiler Modules

    (s/def :cljs-compiler-modules/output-to string?)
    (s/def :cljs-compiler-modules/entries (s/+ string?))
    (s/def :cljs-compiler-modules/depends-on (s/+ ::string-or-named))

    ;; ** CLJS Compiler Options

    (s/def :cljs-compiler/main ::string-or-symbol)
    (s/def :cljs-compiler/preloads (s/+ symbol?))
    (s/def :cljs-compiler/asset-path string?)
    (s/def :cljs-compiler-options/output-to string?)

    (s/def :cljs-compiler/output-dir string?)
    (s/def :cljs-compiler/optimizations #{:none :whitespace :simple :advanced})

    (s/def :cljs-compiler/source-map (s/or :bool boolean? :string string?))
    (s/def :cljs-compiler/verbose boolean?)
    (s/def :cljs-compiler/pretty-print boolean?)
    (s/def :cljs-compiler/target #{:nodejs})


    (s/def :cljs-compiler/foreign-libs (s/keys
    :req-un [:cljs-foreign-libs/file
    :cljs-foreign-libs/provides]
    :opt-un [:cljs-foreign-libs/file-min
    :cljs-foreign-libs/requires
    :cljs-foreign-libs/module-type
    :cljs-foreign-libs/preprocess]))

    (s/def :cljs-compiler/externs (s/+ string?))

    (s/def :cljs-compiler/modules (s/map-of
    keyword?
    (s/keys
    :req-un [:cljs-compiler-modules/output-to
    :cljs-compiler-modules/entries]
    :opt-un [:cljs-compiler-modules/depends-on])))

    (s/def :cljs-compiler/source-map-path string?)
    (s/def :cljs-compiler/source-map-timestamp boolean?)
    (s/def :cljs-compiler/cache-analysis boolean?)
    (s/def :cljs-compiler/recompile-dependents boolean?)
    (s/def :cljs-compiler/static-fns boolean?)

    (s/def :cljs-compiler/elide-asserts boolean?)
    (s/def :cljs-compiler/pseudo-names boolean?)
    (s/def :cljs-compiler/print-input-delimiter boolean?)
    (s/def :cljs-compiler/output-wrapper boolean?)
    (s/def :cljs-compiler/libs (s/+ string?))
    (s/def :cljs-compiler/preamble (s/+ string?))
    (s/def :cljs-compiler/hashbang boolean?)
    (s/def :cljs-compiler/compiler-stats boolean?)
    (s/def :cljs-compiler/language-in #{:ecmascript3 :ecmascript5 :ecmascript5-strict})
    (s/def :cljs-compiler/language-out #{:ecmascript3 :ecmascript5 :ecmascript5-strict})

    (s/def :cljs-compiler/closure-defines (s/map-of
    ::string-or-symbol
    (s/or :number number?
    :string string?
    :bool boolean?)))
    (s/def :cljs-compiler/closure-extra-annotations (s/+ string?))
    (s/def :cljs-compiler/anon-fn-naming-policy #{:off :unmapped :mapped})
    (s/def :cljs-compiler/optimize-constants boolean?)
    (s/def :cljs-compiler/parallel-build boolean?)
    (s/def :cljs-compiler/devcards boolean?)
    (s/def :cljs-compiler/dump-core boolean?)
    (s/def :cljs-compiler/emit-constants boolean?)
    (s/def :cljs-compiler/warning-handlers (s/+ ::s/any)) ;; symbol, string, or fn?
    (s/def :cljs-compiler/source-map-inline boolean?)
    (s/def :cljs-compiler/ups-libs (s/+ string?))
    (s/def :cljs-compiler/ups-externs (s/+ string?))
    (s/def :cljs-compiler/ups-foreign-libs (s/+ :cljs-compiler/foreign-libs))
    (s/def :cljs-compiler/closure-output-charset string?)
    (s/def :cljs-compiler/external-config (s/map-of keyword? map?))


    (s/def :cljs-compiler/warnings
    (s/keys
    :opt-un
    [:cljs-compiler-warning/undeclared-ns-form
    :cljs-compiler-warning/protocol-deprecated
    :cljs-compiler-warning/undeclared-protocol-symbol
    :cljs-compiler-warning/fn-var
    :cljs-compiler-warning/invalid-arithmetic
    :cljs-compiler-warning/preamble-missing
    :cljs-compiler-warning/undeclared-var
    :cljs-compiler-warning/protocol-invalid-method
    :cljs-compiler-warning/variadic-max-arity
    :cljs-compiler-warning/multiple-variadic-overloads
    :cljs-compiler-warning/fn-deprecated
    :cljs-compiler-warning/redef
    :cljs-compiler-warning/fn-arity
    :cljs-compiler-warning/invalid-protocol-symbol
    :cljs-compiler-warning/dynamic
    :cljs-compiler-warning/undeclared-ns
    :cljs-compiler-warning/overload-arity
    :cljs-compiler-warning/extending-base-js-type
    :cljs-compiler-warning/single-segment-namespace
    :cljs-compiler-warning/protocol-duped-method
    :cljs-compiler-warning/protocol-multiple-impls
    :cljs-compiler-warning/invoke-ctor]))

    (s/def :cljs-compiler/closure-warnings
    (s/keys
    :opt-un
    [:closure-warning/access-controls
    :closure-warning/ambiguous-function-decl
    :closure-warning/debugger-statement-present
    :closure-warning/check-regexp
    :closure-warning/check-types
    :closure-warning/check-useless-code
    :closure-warning/check-variables
    :closure-warning/const
    :closure-warning/constant-property
    :closure-warning/deprecated
    :closure-warning/duplicate-message
    :closure-warning/es5-strict
    :closure-warning/externs-validation
    :closure-warning/fileoverview-jsdoc
    :closure-warning/global-this
    :closure-warning/internet-explorer-checks
    :closure-warning/invalid-casts
    :closure-warning/missing-properties
    :closure-warning/non-standard-jsdoc
    :closure-warning/strict-module-dep-check
    :closure-warning/tweaks
    :closure-warning/undefined-names
    :closure-warning/undefined-variables
    :closure-warning/unknown-defines
    :closure-warning/visiblity]))