├── src ├── duct_hierarchy.edn └── duct │ ├── repl │ └── shadow_cljs.clj │ ├── compiler │ └── shadow_cljs.clj │ ├── module │ └── shadow_cljs.clj │ └── server │ └── shadow_cljs.clj ├── .gitignore ├── project.clj └── README.md /src/duct_hierarchy.edn: -------------------------------------------------------------------------------- 1 | {:duct.module/shadow-cljs [:duct/module] 2 | :duct.server/shadow-cljs [:duct/server] 3 | :duct.compiler/shadow-cljs [:duct/compiler]} 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | .hgignore 12 | .hg/ 13 | -------------------------------------------------------------------------------- /src/duct/repl/shadow_cljs.clj: -------------------------------------------------------------------------------- 1 | (ns duct.repl.shadow-cljs 2 | (:require 3 | [integrant.repl.state :refer [config]] 4 | [shadow.cljs.devtools.api :as api])) 5 | 6 | 7 | (defn cljs-repl 8 | ([] 9 | (cljs-repl (-> config 10 | (get-in [:duct.server/shadow-cljs :builds]) 11 | keys 12 | first))) 13 | ([build-id] 14 | (api/nrepl-select build-id))) 15 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject g7s/module.shadow-cljs "0.1.2" 2 | :description "Duct module for compiling ClojureScript with shadow-cljs" 3 | :url "https://github.com/g7s/module.shadow-cljs" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies 7 | [[org.clojure/clojure "1.10.1" :scope "provided"] 8 | [org.clojure/clojurescript "1.10.597" :scope "provided"] 9 | [thheller/shadow-cljs "2.8.90"] 10 | [duct/core "0.8.0"] 11 | [duct/logger "0.3.0"] 12 | [integrant "0.8.0"] 13 | [integrant/repl "0.3.1"]]) 14 | -------------------------------------------------------------------------------- /src/duct/compiler/shadow_cljs.clj: -------------------------------------------------------------------------------- 1 | (ns duct.compiler.shadow-cljs 2 | "Integrant methods for compiling with shadow-cljs." 3 | (:require 4 | [integrant.core :as ig] 5 | [duct.logger :as logger] 6 | [shadow.cljs.devtools.config :as config] 7 | [shadow.cljs.devtools.api :as shadow])) 8 | 9 | 10 | (defn- release-options-for-build 11 | [build-conf] 12 | (select-keys (:compiler-options build-conf) [:pseudo-names :pretty-print :debug :source-maps])) 13 | 14 | 15 | (defn- release* 16 | [{:keys [build-id] :as build-conf} {:keys [logger] :as release-opts}] 17 | (shadow/release* build-conf release-opts) 18 | (logger/log logger :info ::complete-build {:build-id build-id})) 19 | 20 | 21 | (defn- release-build 22 | [build-conf opts] 23 | (let [ropts (release-options-for-build build-conf)] 24 | (release* build-conf (merge ropts opts)))) 25 | 26 | 27 | (defmethod ig/init-key :duct.compiler/shadow-cljs 28 | [_ {:keys [logger] :as conf}] 29 | (let [conf (config/normalize conf)] 30 | (shadow/with-runtime 31 | (doseq [build-conf (-> conf :builds vals)] 32 | (release-build build-conf {:logger logger})) 33 | :done))) 34 | -------------------------------------------------------------------------------- /src/duct/module/shadow_cljs.clj: -------------------------------------------------------------------------------- 1 | (ns duct.module.shadow-cljs 2 | "Integrant methods for running shadow-cljs." 3 | (:require 4 | [integrant.core :as ig] 5 | [duct.core :as core] 6 | [duct.core.merge :as merge])) 7 | 8 | 9 | (defn- env-duct-key 10 | [env] 11 | (case env 12 | :production :duct.compiler/shadow-cljs 13 | :duct.server/shadow-cljs)) 14 | 15 | 16 | (def ^:private dev-config 17 | {:dependencies ^:distinct [] 18 | :nrepl (merge/displace false) 19 | :source-paths ^:displace ["src"] 20 | :cache-root (merge/displace ".shadow-cljs")}) 21 | 22 | 23 | (def ^:private prod-config 24 | {:dependencies ^:distinct [] 25 | :source-paths ^:displace ["src"] 26 | :cache-root (merge/displace ".shadow-cljs")}) 27 | 28 | 29 | (def ^:private env-configs 30 | {:production prod-config 31 | :development dev-config}) 32 | 33 | 34 | (defmethod ig/init-key :duct.module/shadow-cljs 35 | [_ shadow-conf] 36 | (fn [duct-conf] 37 | (let [env (::core/environment duct-conf :production) 38 | dkey (env-duct-key env)] 39 | (core/merge-configs duct-conf 40 | {dkey shadow-conf} 41 | {dkey (get env-configs env)})))) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Duct module.shadow-cljs 2 | 3 | [![Clojars Project](https://img.shields.io/clojars/v/g7s/module.shadow-cljs.svg)](https://clojars.org/g7s/module.shadow-cljs) 4 | 5 | A [Duct](https://github.com/duct-framework/duct) module for compiling and dynamically reloading ClojureScript files using [shadow-cljs](https://github.com/thheller/shadow-cljs). 6 | 7 | 8 | ## Installation 9 | 10 | To install, add the following to your project `:dependencies`: 11 | 12 | [g7s/module.shadow-cljs "0.1.2"] 13 | 14 | 15 | ## Usage 16 | 17 | This library provides the `:duct.module/shadow-cljs` key, and accepts the 18 | same options as [shadow-cljs](https://shadow-cljs.github.io/docs/UsersGuide.html) 19 | as well as an (optional) `:logger` top-level key that is a `:duct/logger` that will 20 | be used to log information about your builds in development. 21 | 22 | Additionally it defines two other keys namely `:duct.server/shadow-cljs` and `:duct.compiler/shadow-cljs`. 23 | 24 | ### Server 25 | 26 | The server key `:duct.server/shadow-cljs` is used to start a shadow-cljs server for use in the development phase of your project. 27 | If you want to make changes in the main `:duct.module/shadow-cljs` you should use this key in your development profile configuration. 28 | 29 | ### Compiler 30 | 31 | The compiler key inherits from `:duct/compiler` and it is used only when compiling your code for a release. 32 | If you want to make changes in the main `:duct.module/shadow-cljs` you should use this key in your production profile configuration. 33 | 34 | ### Example 35 | 36 | Example module configuration: 37 | 38 | ```edn 39 | :duct.module/shadow-cljs 40 | {:builds 41 | {:app 42 | {:target :browser 43 | :output-dir "target/resources/my/app/web/public/js" 44 | :asset-path "/js" 45 | :devtools {:watch-dir "resources/my/app/web/public" 46 | :watch-options {:verbose true 47 | :autobuild true} 48 | :browser-inject :common} 49 | :modules 50 | {:base {:entries [cljs.core]} 51 | 52 | :common {:entries [my.app.utils] 53 | :depends-on #{:base}} 54 | 55 | :mod {:entries [my.app.mod] 56 | :depends-on #{:common}} 57 | 58 | :mod-worker {:entries [my.app.mod.worker] 59 | :depends-on #{:base} 60 | :web-worker true} 61 | 62 | :pages {:entries [my.app.pages] 63 | :depends-on #{:common}}}}}} 64 | ``` 65 | 66 | Override in development profile: 67 | 68 | ```edn 69 | :duct.server/shadow-cljs 70 | {:logger #ig/ref :duct/logger 71 | :compiler-options {:output-feature-set :es5} 72 | :builds 73 | {:app 74 | {:closure-defines {goog.DEBUG true} 75 | :devtools {:preloads [devtools.preload]}}}} 76 | ``` 77 | 78 | ## License 79 | 80 | Copyright © 2020 Gerasimos 81 | 82 | Distributed under the Eclipse Public License either version 1.0 or (at 83 | your option) any later version. 84 | -------------------------------------------------------------------------------- /src/duct/server/shadow_cljs.clj: -------------------------------------------------------------------------------- 1 | (ns duct.server.shadow-cljs 2 | "Integrant methods for shadow-cljs server." 3 | (:require 4 | [clojure.core.async :as async :refer [ (async/sliding-buffer 1) 24 | (async/chan))] 25 | (async/go 26 | (loop [] 27 | (when-some [x ( (super/start-worker supervisor build-conf opts) 71 | (worker/watch (log-worker-msg logger verbose) true) 72 | (cond-> 73 | (not (false? autobuild)) 74 | (worker/start-autobuild) 75 | 76 | (false? autobuild) 77 | (worker/compile) 78 | 79 | (not (false? sync)) 80 | (worker/sync!))) 81 | (logger/log logger :info ::worker-started {:build-id build-id}))) 82 | 83 | 84 | (defn- watch-build 85 | [build-conf {:keys [logger] :as opts}] 86 | (if (shadow/worker-running? (:build-id build-conf)) 87 | :running 88 | (let [wopts (watch-options-for-build build-conf)] 89 | (if logger 90 | (watch* build-conf (merge wopts opts)) 91 | (shadow/watch* build-conf wopts)) 92 | :watching))) 93 | 94 | 95 | (defmethod ig/init-key :duct.server/shadow-cljs 96 | [_ {:keys [logger] :as conf}] 97 | (let [conf (config/normalize conf)] 98 | (server/start! conf) 99 | (doseq [build-conf (-> conf :builds vals)] 100 | (watch-build build-conf {:logger logger})) 101 | conf)) 102 | 103 | 104 | (defmethod ig/halt-key! :duct.server/shadow-cljs 105 | [_ _] 106 | (server/stop!)) 107 | 108 | 109 | (defmethod ig/suspend-key! :duct.server/shadow-cljs 110 | [_ _]) 111 | 112 | 113 | (defmethod ig/resume-key :duct.server/shadow-cljs 114 | [k new-shadow-conf old-shadow-conf old-impl] 115 | (when-not (= (dissoc old-shadow-conf :logger :duct.core/requires) 116 | (dissoc new-shadow-conf :logger :duct.core/requires)) 117 | (ig/halt-key! k old-impl) 118 | (ig/init-key k new-shadow-conf))) 119 | --------------------------------------------------------------------------------