clojurific 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +3 -2
  2. package/cljf/launcher.js +15 -8
  3. package/package.json +2 -2
  4. package/compiler/deps.edn +0 -9
  5. package/compiler/src/main/cljs/cljs/analyzer/passes/and_or.cljc +0 -118
  6. package/compiler/src/main/cljs/cljs/analyzer/passes/lite.cljc +0 -32
  7. package/compiler/src/main/cljs/cljs/analyzer/passes.cljc +0 -32
  8. package/compiler/src/main/cljs/cljs/bootstrap_nodejs.js +0 -154
  9. package/compiler/src/main/cljs/cljs/core/specs/alpha.cljc +0 -272
  10. package/compiler/src/main/cljs/cljs/core/specs/alpha.cljs +0 -10
  11. package/compiler/src/main/cljs/cljs/core.cljs +0 -13194
  12. package/compiler/src/main/cljs/cljs/esm/goog/array.js +0 -61
  13. package/compiler/src/main/cljs/cljs/esm/goog/math.js +0 -1846
  14. package/compiler/src/main/cljs/cljs/esm/goog/object.js +0 -80
  15. package/compiler/src/main/cljs/cljs/esm/goog/string.js +0 -192
  16. package/compiler/src/main/cljs/cljs/esm/goog/userAgent/product.js +0 -10
  17. package/compiler/src/main/cljs/cljs/esm/goog.js +0 -90
  18. package/compiler/src/main/cljs/cljs/esm/karma.cljs +0 -103
  19. package/compiler/src/main/cljs/cljs/esm/lazy.clj +0 -51
  20. package/compiler/src/main/cljs/cljs/esm/lazy.cljs +0 -49
  21. package/compiler/src/main/cljs/cljs/esm/node_test.cljs +0 -21
  22. package/compiler/src/main/cljs/cljs/esm/repl_runtime.cljs +0 -353
  23. package/compiler/src/main/cljs/cljs/externs.js +0 -27
  24. package/compiler/src/main/cljs/cljs/imul.js +0 -11
  25. package/compiler/src/main/cljs/cljs/js.clj +0 -24
  26. package/compiler/src/main/cljs/cljs/js.cljs +0 -1432
  27. package/compiler/src/main/cljs/cljs/loader.cljs +0 -107
  28. package/compiler/src/main/cljs/cljs/main.clj +0 -67
  29. package/compiler/src/main/cljs/cljs/math.cljs +0 -869
  30. package/compiler/src/main/cljs/cljs/module_deps.js +0 -250
  31. package/compiler/src/main/cljs/cljs/nodejs.cljs +0 -27
  32. package/compiler/src/main/cljs/cljs/nodejs_externs.js +0 -3
  33. package/compiler/src/main/cljs/cljs/nodejscli.cljs +0 -22
  34. package/compiler/src/main/cljs/cljs/pprint.cljc +0 -167
  35. package/compiler/src/main/cljs/cljs/pprint.cljs +0 -3325
  36. package/compiler/src/main/cljs/cljs/proxy/impl.cljs +0 -32
  37. package/compiler/src/main/cljs/cljs/proxy.cljs +0 -184
  38. package/compiler/src/main/cljs/cljs/reader.clj +0 -18
  39. package/compiler/src/main/cljs/cljs/reader.cljs +0 -214
  40. package/compiler/src/main/cljs/cljs/repl.cljs +0 -206
  41. package/compiler/src/main/cljs/cljs/source_map/base64.cljs +0 -25
  42. package/compiler/src/main/cljs/cljs/source_map/base64_vlq.cljs +0 -102
  43. package/compiler/src/main/cljs/cljs/source_map.cljs +0 -305
  44. package/compiler/src/main/cljs/cljs/spec/alpha.cljc +0 -564
  45. package/compiler/src/main/cljs/cljs/spec/alpha.cljs +0 -1510
  46. package/compiler/src/main/cljs/cljs/spec/gen/alpha.cljc +0 -68
  47. package/compiler/src/main/cljs/cljs/spec/gen/alpha.cljs +0 -183
  48. package/compiler/src/main/cljs/cljs/spec/test/alpha.cljc +0 -329
  49. package/compiler/src/main/cljs/cljs/spec/test/alpha.cljs +0 -380
  50. package/compiler/src/main/cljs/cljs/stacktrace.cljc +0 -578
  51. package/compiler/src/main/cljs/cljs/test.cljc +0 -458
  52. package/compiler/src/main/cljs/cljs/test.cljs +0 -606
  53. package/compiler/src/main/cljs/clojure/browser/dom.cljs +0 -152
  54. package/compiler/src/main/cljs/clojure/browser/event.cljs +0 -101
  55. package/compiler/src/main/cljs/clojure/browser/net.cljs +0 -181
  56. package/compiler/src/main/cljs/clojure/browser/repl/preload.cljs +0 -13
  57. package/compiler/src/main/cljs/clojure/browser/repl.cljs +0 -283
  58. package/compiler/src/main/cljs/clojure/core/protocols.cljs +0 -29
  59. package/compiler/src/main/cljs/clojure/core/reducers.cljs +0 -312
  60. package/compiler/src/main/cljs/clojure/data.cljs +0 -162
  61. package/compiler/src/main/cljs/clojure/datafy.cljs +0 -63
  62. package/compiler/src/main/cljs/clojure/edn.cljs +0 -55
  63. package/compiler/src/main/cljs/clojure/reflect.cljs +0 -57
  64. package/compiler/src/main/cljs/clojure/set.cljs +0 -165
  65. package/compiler/src/main/cljs/clojure/string.cljs +0 -290
  66. package/compiler/src/main/cljs/clojure/walk.cljs +0 -98
  67. package/compiler/src/main/cljs/clojure/zip.cljs +0 -291
  68. package/compiler/src/main/cljs/process/env.cljs +0 -12
  69. package/compiler/src/main/cljs/shadow/resource.clj +0 -60
  70. package/compiler/src/main/cljs/shadow/resource.cljs +0 -10
  71. package/compiler/src/main/clojure/cljs/analyzer/api.cljc +0 -299
  72. package/compiler/src/main/clojure/cljs/analyzer/impl/namespaces.cljc +0 -68
  73. package/compiler/src/main/clojure/cljs/analyzer/impl.cljc +0 -59
  74. package/compiler/src/main/clojure/cljs/analyzer/macros.clj +0 -57
  75. package/compiler/src/main/clojure/cljs/analyzer/utils.clj +0 -36
  76. package/compiler/src/main/clojure/cljs/analyzer.cljc +0 -5292
  77. package/compiler/src/main/clojure/cljs/build/api.clj +0 -309
  78. package/compiler/src/main/clojure/cljs/cli.clj +0 -812
  79. package/compiler/src/main/clojure/cljs/closure.clj +0 -3440
  80. package/compiler/src/main/clojure/cljs/compiler/api.clj +0 -101
  81. package/compiler/src/main/clojure/cljs/compiler/macros.clj +0 -16
  82. package/compiler/src/main/clojure/cljs/compiler.cljc +0 -2748
  83. package/compiler/src/main/clojure/cljs/core/macros.clj +0 -43
  84. package/compiler/src/main/clojure/cljs/core/server.clj +0 -167
  85. package/compiler/src/main/clojure/cljs/core.cljc +0 -3719
  86. package/compiler/src/main/clojure/cljs/env/macros.clj +0 -37
  87. package/compiler/src/main/clojure/cljs/env.cljc +0 -102
  88. package/compiler/src/main/clojure/cljs/esm/repl/nrepl.clj +0 -151
  89. package/compiler/src/main/clojure/cljs/esm/repl.clj +0 -878
  90. package/compiler/src/main/clojure/cljs/esm.clj +0 -987
  91. package/compiler/src/main/clojure/cljs/externs/default.edn +0 -1549
  92. package/compiler/src/main/clojure/cljs/externs.clj +0 -479
  93. package/compiler/src/main/clojure/cljs/foreign/node.clj +0 -207
  94. package/compiler/src/main/clojure/cljs/instant.clj +0 -54
  95. package/compiler/src/main/clojure/cljs/js_deps.cljc +0 -412
  96. package/compiler/src/main/clojure/cljs/module_graph.cljc +0 -402
  97. package/compiler/src/main/clojure/cljs/repl/bootstrap.clj +0 -53
  98. package/compiler/src/main/clojure/cljs/repl/browser.clj +0 -540
  99. package/compiler/src/main/clojure/cljs/repl/node.clj +0 -253
  100. package/compiler/src/main/clojure/cljs/repl/node_repl.js +0 -114
  101. package/compiler/src/main/clojure/cljs/repl/reflect.clj +0 -85
  102. package/compiler/src/main/clojure/cljs/repl/server.clj +0 -236
  103. package/compiler/src/main/clojure/cljs/repl.cljc +0 -1615
  104. package/compiler/src/main/clojure/cljs/server/browser.clj +0 -54
  105. package/compiler/src/main/clojure/cljs/server/node.clj +0 -54
  106. package/compiler/src/main/clojure/cljs/source_map/base64.clj +0 -28
  107. package/compiler/src/main/clojure/cljs/source_map/base64_vlq.clj +0 -104
  108. package/compiler/src/main/clojure/cljs/source_map.clj +0 -369
  109. package/compiler/src/main/clojure/cljs/support.cljc +0 -18
  110. package/compiler/src/main/clojure/cljs/tagged_literals.cljc +0 -93
  111. package/compiler/src/main/clojure/cljs/util.cljc +0 -420
  112. package/compiler/src/main/clojure/cljs/vendor/bridge.clj +0 -42
  113. package/compiler/src/main/clojure/cljs/vendor/clojure/data/json.clj +0 -809
  114. package/compiler/src/main/clojure/cljs/vendor/clojure/tools/reader/default_data_readers.clj +0 -303
  115. package/compiler/src/main/clojure/cljs/vendor/clojure/tools/reader/edn.clj +0 -440
  116. package/compiler/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/commons.clj +0 -131
  117. package/compiler/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/errors.clj +0 -214
  118. package/compiler/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/inspect.clj +0 -91
  119. package/compiler/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/utils.clj +0 -127
  120. package/compiler/src/main/clojure/cljs/vendor/clojure/tools/reader/reader_types.clj +0 -431
  121. package/compiler/src/main/clojure/cljs/vendor/clojure/tools/reader.clj +0 -1035
  122. package/compiler/src/main/clojure/cljs/vendor/cognitect/transit.clj +0 -479
  123. /package/{compiler/no-clojurescript → no-clojurescript}/deps.edn +0 -0
@@ -1,2748 +0,0 @@
1
- ; Copyright (c) Rich Hickey. All rights reserved.
2
- ; The use and distribution terms for this software are covered by the
3
- ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4
- ; which can be found in the file epl-v10.html at the root of this distribution.
5
- ; By using this software in any fashion, you are agreeing to be bound by
6
- ; the terms of this license.
7
- ; You must not remove this notice, or any other, from this software.
8
-
9
- (ns cljs.compiler
10
- #?(:clj (:refer-clojure :exclude [ensure macroexpand-1 munge])
11
- :cljs (:refer-clojure :exclude [ensure js-reserved macroexpand-1 munge]))
12
- #?(:cljs (:require-macros [cljs.compiler.macros :refer [emit-wrap]]
13
- [cljs.env.macros :refer [ensure]]))
14
- #?(:clj (:require [cljs.analyzer :as ana]
15
- [cljs.env :as env :refer [ensure]]
16
- [cljs.js-deps :as deps]
17
- [cljs.source-map :as sm]
18
- [cljs.tagged-literals :as tags]
19
- [cljs.util :as util]
20
- [cljs.vendor.clojure.data.json :as json]
21
- [clojure.java.io :as io]
22
- [clojure.set :as set]
23
- [clojure.string :as string]
24
- [cljs.vendor.clojure.tools.reader :as reader])
25
- :cljs (:require [cljs.analyzer :as ana]
26
- [cljs.analyzer.impl :as ana.impl]
27
- [cljs.env :as env]
28
- [cljs.source-map :as sm]
29
- [cljs.tools.reader :as reader]
30
- [clojure.set :as set]
31
- [clojure.string :as string]
32
- [goog.string :as gstring]))
33
- #?(:clj (:import [cljs.tagged_literals JSValue]
34
- java.lang.StringBuilder
35
- [java.io File Writer]
36
- [java.time Instant]
37
- [java.util.concurrent Executors ExecutorService TimeUnit]
38
- [java.util.concurrent.atomic AtomicLong])
39
- :cljs (:import [goog.string StringBuffer])))
40
-
41
- #?(:clj (set! *warn-on-reflection* true))
42
-
43
- (def js-reserved ana/js-reserved)
44
-
45
- (def ^:private es5>=
46
- (into #{}
47
- (comp
48
- (mapcat (fn [lang]
49
- [lang (keyword (string/replace (name lang) #"^ecmascript" "es"))])))
50
- [:ecmascript5 :ecmascript5-strict :ecmascript6 :ecmascript6-strict
51
- :ecmascript-2015 :ecmascript6-typed :ecmascript-2016 :ecmascript-2017
52
- :ecmascript-2018 :ecmascript-2019 :ecmascript-2020 :ecmascript-2021
53
- :ecmascript-next]))
54
-
55
- (def ^:dynamic *recompiled* nil)
56
- (def ^:dynamic *inputs* nil)
57
- (def ^:dynamic *source-map-data* nil)
58
- (def ^:dynamic *source-map-data-gen-col* nil)
59
- (def ^:dynamic *lexical-renames* {})
60
-
61
- ;; ES module output (:module-format :esm)
62
- ;;
63
- ;; Every namespace becomes an ES module. Vars of the namespace being compiled
64
- ;; are module-local bindings named my$ns$var, exported as var. Vars of other
65
- ;; namespaces are referenced through namespace imports, i.e. my$ns.var, which
66
- ;; bundlers can statically resolve, tree-shake and rename.
67
-
68
- (def ^:dynamic *esm*
69
- "Per-file ESM emission state, a map of atoms. Bound by emit-source when
70
- compiling with :module-format :esm."
71
- nil)
72
-
73
- (def ^:dynamic *esm-emitting*
74
- "True while emitting (not analyzing) an ESM file. Var munging only switches
75
- to ESM names during emission, analysis time munging is unaffected."
76
- false)
77
-
78
- (def ^:dynamic *esm-in-fn*
79
- "True while emitting code nested in a JS function, where a top-level var
80
- declaration can't be emitted."
81
- false)
82
-
83
- (def ^:dynamic *esm-repl*
84
- "True while emitting a form evaluated at the REPL (cljs.esm.repl), the body
85
- of a function the REPL runtime calls with the namespaces it references.
86
- The namespace being compiled is one of them: its vars are module bindings
87
- of a module already running, reached through the runtime's view of the
88
- namespace, my$ns.var, and def assigns the view."
89
- false)
90
-
91
- (def esm-repl-runtime
92
- "The namespace evaluating REPL forms in a running build, imported first by
93
- the main namespaces under :esm-repl."
94
- 'cljs.esm.repl-runtime)
95
-
96
- (defn esm-mode?
97
- "Whether compiling to ES modules, :module-format :esm."
98
- ([] (esm-mode? (when env/*compiler* (:options @env/*compiler*))))
99
- ([opts] (= :esm (:module-format opts))))
100
-
101
- (def cljs-reserved-file-names #{"deps.cljs"})
102
-
103
- (defn get-first-ns-segment
104
- "Gets the part up to the first `.` of a namespace.
105
- Returns the empty string for nil.
106
- Returns the entire string if no `.` in namespace"
107
- [ns]
108
- (let [ns (str ns)
109
- idx (.indexOf ns ".")]
110
- (if (== -1 idx)
111
- ns
112
- (subs ns 0 idx))))
113
-
114
- (defn ^:dynamic find-ns-starts-with [needle]
115
- (reduce-kv
116
- (fn [xs ns _]
117
- (when (= needle (get-first-ns-segment ns))
118
- (reduced needle)))
119
- nil
120
- (::ana/namespaces @env/*compiler*)))
121
-
122
- ; Helper fn
123
- (defn shadow-depth [s]
124
- (let [{:keys [name info]} s]
125
- (loop [d 0, {:keys [shadow]} info]
126
- (cond
127
- shadow (recur (inc d) shadow)
128
- (find-ns-starts-with (str name)) (inc d)
129
- :else d))))
130
-
131
- (defn hash-scope [s]
132
- (hash-combine #?(:clj (hash (:name s))
133
- :cljs (-hash ^not-native (:name s)))
134
- (shadow-depth s)))
135
-
136
- (declare munge emits)
137
-
138
- (defn esm-ns-alias
139
- "Returns the identifier a namespace is imported as, i.e. my$ns."
140
- [ns]
141
- (if (= "goog" (str ns))
142
- ;; not goog, the Closure Library compatibility layer defines a global goog
143
- "goog$"
144
- (string/join "$" (map #(str (munge % #{})) (string/split (str ns) #"\.")))))
145
-
146
- (defn esm-module-ns?
147
- "Whether references into namespace ns (a string) should go through an ES
148
- module import. Everything else, i.e. Math or unknown namespaces, keeps
149
- being referenced as a global."
150
- [ns]
151
- (let [cenv @env/*compiler*]
152
- (or (= ns (str ana/*cljs-ns*))
153
- (= "goog" ns)
154
- ;; Closure Library namespaces, i.e. goog.string but not goog.DEBUG
155
- (and (string/starts-with? ns "goog.")
156
- (contains? (:js-dependency-index cenv) ns))
157
- (contains? (::ana/namespaces cenv) (symbol ns))
158
- ;; namespaces of the build not analyzed yet, i.e. under :parallel-build
159
- (contains? (:cljs.esm/namespaces cenv) (symbol ns)))))
160
-
161
- (defn esm-var-name
162
- "Munges the name of a var for ESM output, reserved words are munged like
163
- they are for global namespace paths so that munging is idempotent."
164
- [nm]
165
- (str (munge nm js-reserved)))
166
-
167
- (declare esm-goog-shim-exports)
168
-
169
- (defn- munge-esm-var
170
- ([ns nm] (munge-esm-var ns nm js-reserved))
171
- ([ns nm reserved]
172
- (let [local? (and (not *esm-repl*) (= ns (str ana/*cljs-ns*)))
173
- idx (.indexOf ^String nm ".")
174
- member (esm-var-name (if (neg? idx) nm (subs nm 0 idx)))
175
- ;; nm may be a property path, i.e. Foo.prototype.bar, reserved
176
- ;; words are valid property names, i.e. an npm module's default
177
- nm' (if (neg? idx)
178
- member
179
- (str member "." (munge (subs nm (inc idx)) #{})))
180
- ;; Closure Library members are taken from ES module shims if they
181
- ;; provide them, otherwise from the Closure Library itself run by
182
- ;; the compatibility layer, see cljs.esm/install-goog-libs
183
- lib? (and (or (= "goog" ns) (string/starts-with? ns "goog."))
184
- (not (contains? #?(:clj (esm-goog-shim-exports ns ana/*cljs-ns*) :cljs #{}) member)))]
185
- (cond
186
- local? nil
187
- lib? (swap! (:goog-lib-refs *esm*) conj ns)
188
- :else (swap! (:refs *esm*) conj ns))
189
- (str (esm-ns-alias ns) (when lib? "$") (if local? "$" ".") nm'))))
190
-
191
- (defn- esm-split-dotted
192
- "Fully qualified dotted references like cljs.core.PersistentVector.EMPTY
193
- resolve to namespace cljs.core.PersistentVector, returns the longest
194
- namespace prefix and the rest, if any."
195
- [ns nm]
196
- (let [segs (string/split ns #"\.")]
197
- (loop [n (dec (count segs))]
198
- (when (pos? n)
199
- (let [prefix (string/join "." (take n segs))]
200
- (if (esm-module-ns? prefix)
201
- [prefix (string/join "." (concat (drop n segs) [nm]))]
202
- (recur (dec n))))))))
203
-
204
- (defn- esm-var-ref
205
- "While emitting ES modules, returns [ns name] if symbol s references a var
206
- of a namespace compiled to an ES module."
207
- [s]
208
- (when (and *esm-emitting* (symbol? s) (some? (namespace s)))
209
- (if (esm-module-ns? (namespace s))
210
- [(namespace s) (name s)]
211
- (esm-split-dotted (namespace s) (name s)))))
212
-
213
- (defn fn-self-name [{:keys [name info] :as name-var}]
214
- (let [name (string/replace (str name) ".." "_DOT__DOT_")
215
- {:keys [ns fn-scope]} info
216
- scoped-name (apply str
217
- (interpose "_$_"
218
- (concat (map (comp str :name) fn-scope) [name])))]
219
- (symbol
220
- (munge
221
- (str (string/replace (str ns) "." "$") "$" scoped-name)))))
222
-
223
- (defn munge-reserved [reserved]
224
- (fn [s]
225
- (if-not (nil? (get reserved s))
226
- (str s "$")
227
- s)))
228
-
229
- (defn munge
230
- ([s] (munge s js-reserved))
231
- ([s reserved]
232
- (cond
233
- #?(:clj (map? s)
234
- :cljs (ana.impl/cljs-map? s))
235
- (let [name-var s
236
- name (:name name-var)
237
- field (:field name-var)
238
- info (:info name-var)]
239
- (if-not (nil? (:fn-self-name info))
240
- (fn-self-name s)
241
- ;; Unshadowing
242
- (let [depth (shadow-depth s)
243
- code (hash-scope s)
244
- renamed (get *lexical-renames* code)
245
- name (cond
246
- (true? field) (str "self__." name)
247
- (not (nil? renamed)) renamed
248
- :else name)
249
- munged-name (munge name reserved)]
250
- (if (or (true? field) (zero? depth))
251
- munged-name
252
- (symbol (str munged-name "__$" depth))))))
253
- ;; vars of namespaces compiled to ES modules
254
- (some? (esm-var-ref s))
255
- (let [[ns nm] (esm-var-ref s)]
256
- (symbol (munge-esm-var ns nm reserved)))
257
-
258
- ;; String munging
259
- :else
260
- (let [ss (string/replace (str s) ".." "_DOT__DOT_")
261
- ss (string/replace ss
262
- #?(:clj #"\/(.)" :cljs (js/RegExp. "\\/(.)")) ".$1") ; Division is special
263
- rf (munge-reserved reserved)
264
- ss (map rf (string/split ss #"\."))
265
- ss (string/join "." ss)
266
- ms #?(:clj (clojure.lang.Compiler/munge ss)
267
- :cljs (munge-str ss))]
268
- (if (symbol? s)
269
- (symbol ms)
270
- ms)))))
271
-
272
- (defn esm-rewrite-core-refs
273
- "Under ESM, rewrite cljs.core.foo references in emitted JS strings to
274
- ESM references."
275
- [s]
276
- (if (and *esm-emitting* (string/includes? s "cljs.core."))
277
- (string/replace s #"(?<![\w$.])cljs\.core\.([A-Za-z_$][\w$]*)"
278
- (fn [[_ n]]
279
- (munge-esm-var "cljs.core" n)))
280
- s))
281
-
282
- (defn- cc
283
- "Emitted JS that references cljs.core vars as cljs.core.foo goes through
284
- here to support ESM output."
285
- [s]
286
- (esm-rewrite-core-refs s))
287
-
288
- (defn- esm-js-ns-ref
289
- "Under ESM, returns the reference a js/my.ns or js/my.ns.foo global
290
- reference of a namespace of the build is emitted as."
291
- [js-name]
292
- (when (string/includes? js-name ".")
293
- (let [segs (string/split js-name #"\.")]
294
- (loop [n (count segs)]
295
- (when (pos? n)
296
- (let [ns (string/join "." (take n segs))]
297
- (if (esm-module-ns? ns)
298
- (do (if (and (= ns (str ana/*cljs-ns*)) (not *esm-repl*))
299
- ;; the module's own namespace object
300
- (reset! (:self-import *esm*) true)
301
- (swap! (:refs *esm*) conj ns))
302
- (string/join "." (cons (esm-ns-alias ns) (drop n segs))))
303
- (recur (dec n)))))))))
304
-
305
- (defn- esm-def!
306
- "Records a var defined by the namespace being compiled as an export of the
307
- ES module. Returns the module-local name of the var."
308
- [sym]
309
- (let [local (str (munge sym))
310
- export (esm-var-name (name sym))]
311
- (swap! (:exports *esm*) assoc export local)
312
- (swap! (:export-syms *esm*) assoc export sym)
313
- (when (get-in @env/*compiler*
314
- [::ana/namespaces (symbol (namespace sym)) :defs (symbol (name sym)) :dynamic])
315
- (swap! (:dynamic *esm*) conj export))
316
- local))
317
-
318
- (defn- emit-esm-type-binding
319
- "Emits the binding a deftype / defrecord constructor is assigned to."
320
- [t]
321
- (if (and *esm-emitting* (not *esm-repl*))
322
- (let [local (esm-def! t)]
323
- (if *esm-in-fn*
324
- (do (swap! (:hoist *esm*) conj local)
325
- (emits local))
326
- (emits "var " local)))
327
- (emits (munge t))))
328
-
329
- (defn- comma-sep [xs]
330
- (interpose "," xs))
331
-
332
- (defn- escape-char [^Character c]
333
- (let [cp #?(:clj (.hashCode c)
334
- :cljs (gstring/hashCode c))]
335
- (case cp
336
- ; Handle printable escapes before ASCII
337
- 34 "\\\""
338
- 92 "\\\\"
339
- ; Handle non-printable escapes
340
- 8 "\\b"
341
- 12 "\\f"
342
- 10 "\\n"
343
- 13 "\\r"
344
- 9 "\\t"
345
- (if (< 31 cp 127)
346
- c ; Print simple ASCII characters
347
- #?(:clj (format "\\u%04X" cp) ; Any other character is Unicode
348
- :cljs (let [unpadded (.toString cp 16)
349
- pad (subs "0000" (.-length unpadded))]
350
- (str "\\u" pad unpadded)))))))
351
-
352
- (defn- escape-string [^CharSequence s]
353
- (let [sb #?(:clj (StringBuilder. (count s))
354
- :cljs (StringBuffer.))]
355
- (doseq [c s]
356
- (.append sb (escape-char c)))
357
- (.toString sb)))
358
-
359
- (defn- wrap-in-double-quotes [x]
360
- (str \" x \"))
361
-
362
- (defmulti emit* :op)
363
-
364
- (defn emit [ast]
365
- (when *source-map-data*
366
- (let [{:keys [env]} ast]
367
- (when (:line env)
368
- (let [{:keys [line column]} env]
369
- (swap! *source-map-data*
370
- (fn [m]
371
- (let [minfo (cond-> {:gcol #?(:clj (.get ^AtomicLong *source-map-data-gen-col*)
372
- :cljs (:gen-col m))
373
- :gline (:gen-line m)}
374
- (#{:var :local :js-var :binding} (:op ast))
375
- (assoc :name (str (-> ast :info :name))))]
376
- ; Dec the line/column numbers for 0-indexing.
377
- ; tools.reader uses 1-indexed sources, chrome
378
- ; expects 0-indexed source maps.
379
- (update-in m [:source-map (dec line)]
380
- (fnil (fn [line]
381
- (update-in line [(if column (dec column) 0)]
382
- (fnil (fn [column] (conj column minfo)) [])))
383
- (sorted-map))))))))))
384
- (if (and *esm-emitting*
385
- (not *esm-in-fn*)
386
- (or (= :fn (:op ast))
387
- ;; these emit an IIFE in expression context
388
- (and (= :expr (-> ast :env :context))
389
- (contains? #{:case :throw :do :try :let :loop :letfn} (:op ast)))))
390
- (binding [*esm-in-fn* true]
391
- (emit* ast))
392
- (emit* ast)))
393
-
394
- (defn emits
395
- ([])
396
- ([^Object a]
397
- (cond
398
- (nil? a) nil
399
- #?(:clj (map? a) :cljs (ana.impl/cljs-map? a)) (emit a)
400
- #?(:clj (seq? a) :cljs (ana.impl/cljs-seq? a)) (apply emits a)
401
- #?(:clj (fn? a) :cljs (js-fn? a)) (a)
402
- :else (let [^String s (cond-> a (not (string? a)) .toString)]
403
- #?(:clj (when-some [^AtomicLong gen-col *source-map-data-gen-col*]
404
- (.addAndGet gen-col (.length s)))
405
- :cljs (when-some [sm-data *source-map-data*]
406
- (swap! sm-data update :gen-col #(+ % (.-length s)))))
407
- #?(:clj (.write ^Writer *out* s)
408
- :cljs (print s))))
409
- nil)
410
- ([a b]
411
- (emits a) (emits b))
412
- ([a b c]
413
- (emits a) (emits b) (emits c))
414
- ([a b c d]
415
- (emits a) (emits b) (emits c) (emits d))
416
- ([a b c d e]
417
- (emits a) (emits b) (emits c) (emits d) (emits e))
418
- ([a b c d e & xs]
419
- (emits a) (emits b) (emits c) (emits d) (emits e)
420
- (doseq [x xs] (emits x))))
421
-
422
- (defn ^:private _emitln []
423
- (newline)
424
- (when *source-map-data*
425
- #?(:clj (.set ^AtomicLong *source-map-data-gen-col* 0))
426
- (swap! *source-map-data*
427
- (fn [{:keys [gen-line] :as m}]
428
- (assoc m
429
- :gen-line (inc gen-line)
430
- #?@(:cljs [:gen-col 0])))))
431
- nil)
432
-
433
- (defn emitln
434
- ([] (_emitln))
435
- ([a]
436
- (emits a) (_emitln))
437
- ([a b]
438
- (emits a) (emits b) (_emitln))
439
- ([a b c]
440
- (emits a) (emits b) (emits c) (_emitln))
441
- ([a b c d]
442
- (emits a) (emits b) (emits c) (emits d) (_emitln))
443
- ([a b c d e]
444
- (emits a) (emits b) (emits c) (emits d) (emits e) (_emitln))
445
- ([a b c d e & xs]
446
- (emits a) (emits b) (emits c) (emits d) (emits e)
447
- (doseq [x xs] (emits x))
448
- (_emitln)))
449
-
450
- (defn ^String emit-str [expr]
451
- (with-out-str (emit expr)))
452
-
453
- #?(:clj
454
- (defmulti emit-constant* class)
455
- :cljs
456
- (defmulti emit-constant* type))
457
-
458
- (declare emit-map emit-list emit-vector emit-set emit-js-object emit-js-array
459
- emit-with-meta emit-constants-comma-sep emit-constant emit-record-value)
460
-
461
- (defn all-distinct? [xs]
462
- (apply distinct? xs))
463
-
464
- #?(:clj
465
- (defn emit-constant-no-meta [x]
466
- (cond
467
- (seq? x) (emit-list x emit-constants-comma-sep)
468
- (record? x) (let [[ns name] (ana/record-ns+name x)]
469
- (emit-record-value ns name #(emit-constant (into {} x))))
470
- (map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep all-distinct?)
471
- (vector? x) (emit-vector x emit-constants-comma-sep)
472
- (set? x) (emit-set x emit-constants-comma-sep all-distinct?)
473
- :else (emit-constant* x)))
474
- :cljs
475
- (defn emit-constant-no-meta [x]
476
- (cond
477
- (ana.impl/cljs-seq? x) (emit-list x emit-constants-comma-sep)
478
- (record? x) (let [[ns name] (ana/record-ns+name x)]
479
- (emit-record-value ns name #(emit-constant (into {} x))))
480
- (ana.impl/cljs-map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep all-distinct?)
481
- (ana.impl/cljs-vector? x) (emit-vector x emit-constants-comma-sep)
482
- (ana.impl/cljs-set? x) (emit-set x emit-constants-comma-sep all-distinct?)
483
- :else (emit-constant* x))))
484
-
485
- (defn emit-constant [v]
486
- (let [m (ana/elide-irrelevant-meta (meta v))]
487
- (if (some? (seq m))
488
- (emit-with-meta #(emit-constant-no-meta v) #(emit-constant-no-meta m))
489
- (emit-constant-no-meta v))))
490
-
491
- (defmethod emit-constant* :default
492
- [x]
493
- (throw
494
- (ex-info (str "failed compiling constant: " x "; "
495
- (pr-str (type x)) " is not a valid ClojureScript constant.")
496
- {:constant x
497
- :type (type x)
498
- :clojure.error/phase :compilation})))
499
-
500
- (defmethod emit-constant* nil [x] (emits "null"))
501
-
502
- #?(:clj
503
- (defmethod emit-constant* Long [x] (emits "(" x ")")))
504
-
505
- #?(:clj
506
- (defmethod emit-constant* Integer [x] (emits x))) ; reader puts Integers in metadata
507
-
508
- #?(:clj
509
- (defmethod emit-constant* Double [x]
510
- (let [x (double x)]
511
- (cond (Double/isNaN x)
512
- (emits "NaN")
513
-
514
- (Double/isInfinite x)
515
- (emits (if (pos? x) "Infinity" "-Infinity"))
516
-
517
- :else (emits x))))
518
- :cljs
519
- (defmethod emit-constant* js/Number [x]
520
- (cond (js/isNaN x)
521
- (emits "NaN")
522
-
523
- (not (js/isFinite x))
524
- (emits (if (pos? x) "Infinity" "-Infinity"))
525
-
526
- (and (zero? x) (neg? (/ x)))
527
- (emits "(-0)")
528
-
529
- :else (emits "(" x ")"))))
530
-
531
- #?(:clj
532
- (defmethod emit-constant* BigDecimal [x] (emits (.doubleValue ^BigDecimal x))))
533
-
534
- #?(:clj
535
- (defmethod emit-constant* clojure.lang.BigInt [x] (emits (.doubleValue ^clojure.lang.BigInt x))))
536
-
537
- (defmethod emit-constant* #?(:clj String :cljs js/String) [x]
538
- (emits (wrap-in-double-quotes (escape-string x))))
539
-
540
- (defmethod emit-constant* #?(:clj Boolean :cljs js/Boolean) [x] (emits (if x "true" "false")))
541
-
542
- #?(:clj
543
- (defmethod emit-constant* Character [x]
544
- (emits (wrap-in-double-quotes (escape-char x)))))
545
-
546
- (defmethod emit-constant* #?(:clj java.util.regex.Pattern :cljs js/RegExp) [x]
547
- (if (= "" (str x))
548
- (emits "(new RegExp(\"\"))")
549
- (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" (str x))]
550
- #?(:clj (emits \/
551
- (.replaceAll (re-matcher #"/" pattern) "\\\\/")
552
- \/ flags)
553
- :cljs (emits pattern)))))
554
-
555
- (defn emits-keyword [kw]
556
- (let [ns (namespace kw)
557
- name (name kw)]
558
- (emits (cc "new cljs.core.Keyword("))
559
- (emit-constant ns)
560
- (emits ",")
561
- (emit-constant name)
562
- (emits ",")
563
- (emit-constant (if ns
564
- (str ns "/" name)
565
- name))
566
- (emits ",")
567
- (emit-constant (hash kw))
568
- (emits ")")))
569
-
570
- (defn emits-symbol [sym]
571
- (let [ns (namespace sym)
572
- name (name sym)
573
- symstr (if-not (nil? ns)
574
- (str ns "/" name)
575
- name)]
576
- (emits (cc "new cljs.core.Symbol("))
577
- (emit-constant ns)
578
- (emits ",")
579
- (emit-constant name)
580
- (emits ",")
581
- (emit-constant symstr)
582
- (emits ",")
583
- (emit-constant (hash sym))
584
- (emits ",")
585
- (emit-constant nil)
586
- (emits ")")))
587
-
588
- (defn- esm-constants?
589
- "Whether the module being emitted references keyword and symbol constants
590
- of the constants module (:emit-constants). cljs.core, which the constants
591
- module imports, keeps its own."
592
- []
593
- (and *esm-emitting*
594
- (-> @env/*compiler* :options :emit-constants)
595
- (not= 'cljs.core ana/*cljs-ns*)
596
- ;; the constants module has the constants of the compiled namespaces
597
- (not *esm-repl*)))
598
-
599
- (defn- emit-constant-ref
600
- "Emits the reference to constant x of the constants table, true if it has
601
- one."
602
- [x]
603
- (when-let [value (and (-> @env/*compiler* :options :emit-constants)
604
- (-> @env/*compiler* ::ana/constant-table x))]
605
- (if *esm-emitting*
606
- (when (esm-constants?)
607
- (emits (esm-ns-alias ana/constants-ns-sym) "." value)
608
- true)
609
- (do (emits "cljs.core." value)
610
- true))))
611
-
612
- (defmethod emit-constant* #?(:clj clojure.lang.Keyword :cljs Keyword) [x]
613
- (when-not (emit-constant-ref x)
614
- (emits-keyword x)))
615
-
616
- (defmethod emit-constant* #?(:clj clojure.lang.Symbol :cljs Symbol) [x]
617
- (when-not (emit-constant-ref x)
618
- (emits-symbol x)))
619
-
620
- (defn emit-constants-comma-sep [cs]
621
- (fn []
622
- (doall
623
- (map-indexed (fn [i m]
624
- (if (even? i)
625
- (emit-constant m)
626
- (emits m)))
627
- (comma-sep cs)))))
628
-
629
- (def ^:private array-map-threshold 8)
630
-
631
- ;; tagged literal support
632
-
633
- (defn- emit-inst [inst-ms]
634
- (emits "new Date(" inst-ms ")"))
635
-
636
- (defmethod emit-constant* #?(:clj java.util.Date :cljs js/Date) [^java.util.Date date]
637
- (emit-inst (.getTime date)))
638
-
639
- #?(:clj
640
- (defmethod emit-constant* java.time.Instant [^java.time.Instant inst]
641
- (emit-inst (.toEpochMilli inst))))
642
-
643
- (defmethod emit-constant* #?(:clj java.util.UUID :cljs UUID) [^java.util.UUID uuid]
644
- (let [uuid-str (.toString uuid)]
645
- (emits (cc "new cljs.core.UUID(\"") uuid-str "\", " (hash uuid-str) ")")))
646
-
647
- (defmethod emit-constant* #?(:clj JSValue :cljs cljs.tagged-literals/JSValue) [^JSValue v]
648
- (let [items (.-val v)]
649
- (if (map? items)
650
- (emit-js-object items #(fn [] (emit-constant %)))
651
- (emit-js-array items emit-constants-comma-sep))))
652
-
653
- #?(:clj
654
- (defmacro emit-wrap [env & body]
655
- `(let [env# ~env]
656
- (when (= :return (:context env#)) (emits "return "))
657
- ~@body
658
- (when-not (= :expr (:context env#)) (emitln ";")))))
659
-
660
- (defmethod emit* :no-op [m])
661
-
662
- (defn emit-var
663
- [{:keys [info env form] :as ast}]
664
- (if-let [const-expr (:const-expr ast)]
665
- (emit (assoc const-expr :env env))
666
- (let [{:keys [options] :as cenv} @env/*compiler*
667
- var-name (:name info)
668
- info (if (= (namespace var-name) "js")
669
- (let [js-module-name (get-in cenv [:js-module-index (name var-name) :name])]
670
- (or js-module-name (name var-name)))
671
- info)]
672
- ;; We need a way to write bindings out to source maps and javascript
673
- ;; without getting wrapped in an emit-wrap calls, otherwise we get
674
- ;; e.g. (function greet(return x, return y) {}).
675
- (cond
676
- (:binding-form? ast)
677
- ;; Emit the arg map so shadowing is properly handled when munging
678
- ;; (prevents duplicate fn-param-names)
679
- (emits (munge ast))
680
-
681
- ;; ES modules are never loaded by the goog debug loader
682
- (and *esm-emitting* (= 'js/COMPILED var-name))
683
- (emit-wrap env (emits "true"))
684
-
685
- ;; a compile time constant, bundlers eliminate dead code depending on
686
- ;; it, set with :closure-defines {goog.DEBUG false}
687
- (and *esm-emitting* (contains? '#{goog/DEBUG js/goog.DEBUG} var-name))
688
- (when-not (= :statement (:context env))
689
- (emit-wrap env
690
- (emits (pr-str (get-in options [:closure-defines "goog.DEBUG"] true)))))
691
-
692
- ;; a direct eval in a module defeats tree shaking and renaming of all
693
- ;; of the module's bindings
694
- (and *esm-emitting* (= 'js/eval var-name))
695
- (when-not (= :statement (:context env))
696
- (emit-wrap env (emits "(0,eval)")))
697
-
698
- ;; namespaces accessed as JavaScript globals, i.e. js/my.ns.foo
699
- (and *esm-emitting* (= "js" (namespace var-name)) (esm-js-ns-ref (name var-name)))
700
- (when-not (= :statement (:context env))
701
- (emit-wrap env (emits (esm-js-ns-ref (name var-name)))))
702
-
703
- :else
704
- (when-not (= :statement (:context env))
705
- (let [reserved (cond-> js-reserved
706
- (and (es5>= (:language-out options))
707
- ;; we can skip munging things like `my.ns.default`
708
- ;; but not standalone `default` variable names
709
- ;; as they're not valid ES5 - Antonio
710
- (some? (namespace var-name)))
711
- (set/difference ana/es5-allowed))
712
- js-module (get-in cenv [:js-namespaces (or (namespace var-name) (name var-name))])
713
- info (cond-> info
714
- (not= form 'js/-Infinity) (munge reserved))]
715
- (emit-wrap env
716
- (case (:module-type js-module)
717
- ;; Closure exports CJS exports through default property
718
- :commonjs
719
- (if (namespace var-name)
720
- (emits (munge (namespace var-name) reserved) "[\"default\"]." (munge (name var-name) reserved))
721
- (emits (munge (name var-name) reserved) "[\"default\"]"))
722
-
723
- ;; Emit bracket notation for default prop access instead of dot notation
724
- :es6
725
- (if (and (namespace var-name) (= "default" (name var-name)))
726
- (emits (munge (namespace var-name) reserved) "[\"default\"]")
727
- (emits info))
728
-
729
- (emits info)))))))))
730
-
731
- (defmethod emit* :var [expr] (emit-var expr))
732
- (defmethod emit* :binding [expr] (emit-var expr))
733
- (defmethod emit* :js-var [expr] (emit-var expr))
734
- (defmethod emit* :local [expr] (emit-var expr))
735
-
736
- (defmethod emit* :the-var
737
- [{:keys [env var sym meta] :as arg}]
738
- {:pre [(ana/ast? sym) (ana/ast? meta)]}
739
- (let [{:keys [name]} (:info var)]
740
- (emit-wrap env
741
- (emits (cc "new cljs.core.Var(function(){return ") (munge name) ";},"
742
- sym "," meta ")"))))
743
-
744
- (defn emit-with-meta [expr meta]
745
- (emits (cc "cljs.core.with_meta(") expr "," meta ")"))
746
-
747
- (defmethod emit* :with-meta
748
- [{:keys [expr meta env]}]
749
- (emit-wrap env
750
- (emit-with-meta expr meta)))
751
-
752
- (defn distinct-keys? [keys]
753
- (let [keys (map ana/unwrap-quote keys)]
754
- (and (every? #(= (:op %) :const) keys)
755
- (= (count (into #{} keys)) (count keys)))))
756
-
757
- (defn obj-map-key [x]
758
- (if (keyword? x)
759
- (str \" "\\uFDD0" \'
760
- (if (namespace x)
761
- (str (namespace x) "/") "")
762
- (name x)
763
- \")
764
- (str \" x \")))
765
-
766
- (defn emit-obj-map [str-keys vals comma-sep distinct-keys?]
767
- (if (zero? (count str-keys))
768
- (emits (cc "cljs.core.ObjMap.EMPTY"))
769
- (emits (cc "cljs.core.ObjMap.fromObject([") (comma-sep str-keys) "], {"
770
- (comma-sep (map (fn [k v] (str k ":" (emit-str v))) str-keys vals))
771
- "})")))
772
-
773
- (defn emit-lite-map [keys vals comma-sep distinct-keys?]
774
- (if (zero? (count keys))
775
- (emits (cc "cljs.core.HashMapLite.EMPTY"))
776
- (emits (cc "cljs.core.HashMapLite.fromArrays([") (comma-sep keys) "], [" (comma-sep vals) "])")))
777
-
778
- (defn emit-map [keys vals comma-sep distinct-keys?]
779
- (cond
780
- (zero? (count keys))
781
- (emits (cc "cljs.core.PersistentArrayMap.EMPTY"))
782
-
783
- (<= (count keys) array-map-threshold)
784
- (if (distinct-keys? keys)
785
- (emits (cc "new cljs.core.PersistentArrayMap(null, ") (count keys) ", ["
786
- (comma-sep (interleave keys vals))
787
- "], null)")
788
- (emits (cc "cljs.core.PersistentArrayMap.createAsIfByAssoc([")
789
- (comma-sep (interleave keys vals))
790
- "])"))
791
-
792
- :else
793
- (emits (cc "cljs.core.PersistentHashMap.fromArrays([")
794
- (comma-sep keys)
795
- "],["
796
- (comma-sep vals)
797
- "])")))
798
-
799
- (defmethod emit* :map
800
- [{:keys [env form keys vals]}]
801
- (emit-wrap env
802
- (if (ana/lite-mode?)
803
- (let [form-keys (clojure.core/keys form)]
804
- (if (every? #(or (string? %) (keyword? %)) form-keys)
805
- (emit-obj-map (map obj-map-key form-keys) vals comma-sep distinct-keys?)
806
- (emit-lite-map keys vals comma-sep distinct-keys?)))
807
- (emit-map keys vals comma-sep distinct-keys?))))
808
-
809
- (defn emit-list [items comma-sep]
810
- (if (empty? items)
811
- (emits (cc "cljs.core.List.EMPTY"))
812
- (emits (cc "cljs.core.list(") (comma-sep items) ")")))
813
-
814
- (defn emit-vector [items comma-sep]
815
- (if (empty? items)
816
- (emits (cc "cljs.core.PersistentVector.EMPTY"))
817
- (let [cnt (count items)]
818
- (if (< cnt 32)
819
- (emits (cc "new cljs.core.PersistentVector(null, ") cnt
820
- (cc ", 5, cljs.core.PersistentVector.EMPTY_NODE, [") (comma-sep items) "], null)")
821
- (emits (cc "cljs.core.PersistentVector.fromArray([") (comma-sep items) "], true)")))))
822
-
823
- (defn emit-lite-vector [items comma-sep]
824
- (if (empty? items)
825
- (emits (cc "cljs.core.VectorLite.EMPTY"))
826
- (emits (cc "new cljs.core.VectorLite(null, [") (comma-sep items) "], null)")))
827
-
828
- (defmethod emit* :vector
829
- [{:keys [items env]}]
830
- (emit-wrap env
831
- (if (ana/lite-mode?)
832
- (emit-lite-vector items comma-sep)
833
- (emit-vector items comma-sep))))
834
-
835
- (defn distinct-constants? [items]
836
- (let [items (map ana/unwrap-quote items)]
837
- (and (every? #(= (:op %) :const) items)
838
- (= (count (into #{} items)) (count items)))))
839
-
840
- (defn- checked-set-literals?
841
- "Whether set literals of non-constant items throw on duplicates (CLJS-3415),
842
- :checked-set-literals false keeps the earlier behavior: duplicates collapse."
843
- []
844
- (not (false? (get-in @env/*compiler* [:options :checked-set-literals]))))
845
-
846
- (defn emit-set [items comma-sep distinct-constants?]
847
- (cond
848
- (empty? items)
849
- (emits (cc "cljs.core.PersistentHashSet.EMPTY"))
850
-
851
- (distinct-constants? items)
852
- (emits (cc "new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, ") (count items) ", ["
853
- (comma-sep (interleave items (repeat "null"))) "], null), null)")
854
-
855
- (checked-set-literals?)
856
- (emits (cc "cljs.core.PersistentHashSet.createWithCheck([") (comma-sep items) "])")
857
-
858
- :else (emits (cc "cljs.core.PersistentHashSet.createAsIfByAssoc([") (comma-sep items) "])")))
859
-
860
- (defn emit-lite-set [items comma-sep distinct-constants?]
861
- (cond
862
- (empty? items)
863
- (emits (cc "cljs.core.SetLite.EMPTY"))
864
-
865
- (distinct-constants? items)
866
- (emits (cc "cljs.core.set_lite([") (comma-sep items) "])")
867
-
868
- (checked-set-literals?)
869
- (emits (cc "cljs.core.set_lite_check([") (comma-sep items) "])")
870
-
871
- :else (emits (cc "cljs.core.set_lite([") (comma-sep items) "])")))
872
-
873
- (defmethod emit* :set
874
- [{:keys [items env]}]
875
- (emit-wrap env
876
- (if (ana/lite-mode?)
877
- (emit-lite-set items comma-sep distinct-constants?)
878
- (emit-set items comma-sep distinct-constants?))))
879
-
880
- (defn emit-js-object [items emit-js-object-val]
881
- (emits "({")
882
- (when-let [items (seq items)]
883
- (let [[[k v] & r] items]
884
- (emits "\"" (name k) "\": " (emit-js-object-val v))
885
- (doseq [[k v] r]
886
- (emits ", \"" (name k) "\": " (emit-js-object-val v)))))
887
- (emits "})"))
888
-
889
- (defn emit-js-array [items comma-sep]
890
- (emits "[" (comma-sep items) "]"))
891
-
892
- (defmethod emit* :js-object
893
- [{:keys [keys vals env]}]
894
- (emit-wrap env
895
- (emit-js-object (map vector keys vals) identity)))
896
-
897
- (defmethod emit* :js-array
898
- [{:keys [items env]}]
899
- (emit-wrap env
900
- (emit-js-array items comma-sep)))
901
-
902
- (defn emit-record-value
903
- [ns name items]
904
- (emits ns ".map__GT_" name "(" items ")"))
905
-
906
- (defmethod emit* :quote
907
- [{:keys [expr]}]
908
- (emit expr))
909
-
910
- (defmethod emit* :const
911
- [{:keys [form env]}]
912
- (when-not (= :statement (:context env))
913
- (emit-wrap env (emit-constant form))))
914
-
915
- (defn truthy-constant? [expr]
916
- (let [{:keys [op form const-expr]} (ana/unwrap-quote expr)]
917
- (or (and (= op :const)
918
- form
919
- (not (or (and (string? form) (= form ""))
920
- (and (number? form) (zero? form)))))
921
- (and (some? const-expr)
922
- (truthy-constant? const-expr)))))
923
-
924
- (defn falsey-constant? [expr]
925
- (let [{:keys [op form const-expr]} (ana/unwrap-quote expr)]
926
- (or (and (= op :const)
927
- (or (false? form) (nil? form)))
928
- (and (some? const-expr)
929
- (falsey-constant? const-expr)))))
930
-
931
- (defn safe-test? [env e]
932
- (let [tag (ana/infer-tag env e)]
933
- (or ('#{boolean seq} (ana/js-prim-ctor->tag tag tag))
934
- (truthy-constant? e))))
935
-
936
- (defmethod emit* :if
937
- [{:keys [test then else env unchecked]}]
938
- (let [context (:context env)
939
- checked (not (or unchecked (safe-test? env test)))]
940
- (cond
941
- (truthy-constant? test) (emitln then)
942
- (falsey-constant? test) (emitln else)
943
- :else
944
- (if (= :expr context)
945
- (emits "(" (when checked (cc "cljs.core.truth_")) "(" test ")?" then ":" else ")")
946
- (do
947
- (if checked
948
- (emitln (cc "if(cljs.core.truth_(") test ")){")
949
- (emitln "if(" test "){"))
950
- (emitln then "} else {")
951
- (emitln else "}"))))))
952
-
953
- (defn iife-open [{:keys [async]}]
954
- (str (when async "(await ") "(" (when async "async ") "function (){"))
955
-
956
- (defn iife-close [{:keys [async]}]
957
- (str "})()" (when async ")")))
958
-
959
- (defmethod emit* :case
960
- [{v :test :keys [nodes default env]}]
961
- (when (= (:context env) :expr)
962
- (emitln (iife-open env)))
963
- (let [gs (gensym "caseval__")]
964
- (when (= :expr (:context env))
965
- (emitln "var " gs ";"))
966
- (emitln "switch (" v ") {")
967
- (doseq [{ts :tests {:keys [then]} :then} nodes]
968
- (doseq [test (map :test ts)]
969
- (emitln "case " test ":"))
970
- (if (= :expr (:context env))
971
- (emitln gs "=" then)
972
- (emitln then))
973
- (emitln "break;"))
974
- (when default
975
- (emitln "default:")
976
- (if (= :expr (:context env))
977
- (emitln gs "=" default)
978
- (emitln default)))
979
- (emitln "}")
980
- (when (= :expr (:context env))
981
- (emitln "return " gs ";"
982
- (iife-close env)))))
983
-
984
- (defmethod emit* :throw
985
- [{throw :exception :keys [env]}]
986
- (if (= :expr (:context env))
987
- (emits (iife-open env) "throw " throw (iife-close env))
988
- (emitln "throw " throw ";")))
989
-
990
- (def base-types
991
- #{"null" "*" "...*"
992
- "boolean" "Boolean"
993
- "string" "String"
994
- "number" "Number"
995
- "array" "Array"
996
- "object" "Object"
997
- "RegExp"
998
- "Date"})
999
-
1000
- (def mapped-types
1001
- {"nil" "null"})
1002
-
1003
- (defn resolve-type [env ^String t]
1004
- (cond
1005
- (get base-types t) t
1006
-
1007
- (get mapped-types t) (get mapped-types t)
1008
-
1009
- #?(:clj (.startsWith t "!")
1010
- :cljs (gstring/startsWith t "!"))
1011
- (str "!" (resolve-type env (subs t 1)))
1012
-
1013
- #?(:clj (.startsWith t "{")
1014
- :cljs (gstring/startsWith t "{")) t
1015
-
1016
- #?(:clj (.startsWith t "function")
1017
- :cljs (gstring/startsWith t "function"))
1018
- (let [idx (.lastIndexOf t ":")
1019
- [fstr rstr] (if-not (== -1 idx)
1020
- [(subs t 0 idx) (subs t (inc idx) (count t))]
1021
- [t nil])
1022
- ret-t (when rstr (resolve-type env rstr))
1023
- axstr (subs fstr 9 (dec (count fstr)))
1024
- args-ts (when-not (string/blank? axstr)
1025
- (map (comp #(resolve-type env %) string/trim)
1026
- (string/split axstr #",")))]
1027
- (cond-> (str "function(" (string/join "," args-ts) ")")
1028
- ret-t (str ":" ret-t)))
1029
-
1030
- #?(:clj (.endsWith t "=")
1031
- :cljs (gstring/endsWith t "="))
1032
- (str (resolve-type env (subs t 0 (dec (count t)))) "=")
1033
-
1034
- :else
1035
- (munge (str (:name (ana/resolve-var env (symbol t)))))))
1036
-
1037
- (defn resolve-types [env ts]
1038
- (let [ts (-> ts string/trim (subs 1 (dec (count ts))))
1039
- xs (string/split ts #"\|")]
1040
- (str "{" (string/join "|" (map #(resolve-type env %) xs)) "}")))
1041
-
1042
- (defn munge-param-return [env line]
1043
- (cond
1044
- (re-find #"@param" line)
1045
- (let [[p ts n & xs] (map string/trim
1046
- (string/split (string/trim line) #" "))]
1047
- (if (and (= "@param" p)
1048
- ts #?(:clj (.startsWith ^String ts "{")
1049
- :cljs (gstring/startsWith ts "{")))
1050
- (string/join " " (concat [p (resolve-types env ts) (munge n)] xs))
1051
- line))
1052
-
1053
- (re-find #"@return" line)
1054
- (let [[p ts & xs] (map string/trim
1055
- (string/split (string/trim line) #" "))]
1056
- (if (and (= "@return" p)
1057
- ts #?(:clj (.startsWith ^String ts "{")
1058
- :cljs (gstring/startsWith ts "{")))
1059
- (string/join " " (concat [p (resolve-types env ts)] xs))
1060
- line))
1061
-
1062
- :else line))
1063
-
1064
- (defn checking-types? []
1065
- (#{:error :warning}
1066
- (get-in @env/*compiler*
1067
- [:options :closure-warnings :check-types])))
1068
-
1069
- (defn emit-comment
1070
- "Emit a nicely formatted comment string."
1071
- ([doc jsdoc]
1072
- (emit-comment nil doc jsdoc))
1073
- ([env doc jsdoc]
1074
- (let [docs (when doc [doc])
1075
- docs (if jsdoc (concat docs jsdoc) docs)
1076
- docs (remove nil? docs)]
1077
- (letfn [(print-comment-lines [e]
1078
- (let [[x & ys]
1079
- (map #(if (checking-types?) (munge-param-return env %) %)
1080
- (string/split-lines e))]
1081
- (emitln " * " (string/replace x "*/" "* /"))
1082
- (doseq [next-line ys]
1083
- (emitln " * "
1084
- (-> next-line
1085
- (string/replace #"^ " "")
1086
- (string/replace "*/" "* /"))))))]
1087
- (when (seq docs)
1088
- (emitln "/**")
1089
- (doseq [e docs]
1090
- (when e
1091
- (print-comment-lines e)))
1092
- (emitln " */"))))))
1093
-
1094
- (defn valid-define-value? [x]
1095
- (or (string? x)
1096
- (true? x)
1097
- (false? x)
1098
- (number? x)))
1099
-
1100
- (defn get-define [mname jsdoc]
1101
- (let [opts (get @env/*compiler* :options)]
1102
- (and (some #?(:clj #(.startsWith ^String % "@define")
1103
- :cljs #(gstring/startsWith % "@define"))
1104
- jsdoc)
1105
- opts
1106
- (or (= (:optimizations opts) :none)
1107
- (esm-mode? opts))
1108
- (let [define (get-in opts [:closure-defines (str mname)])]
1109
- (when (valid-define-value? define)
1110
- (pr-str define))))))
1111
-
1112
- (defmethod emit* :def
1113
- [{:keys [name var init env doc goog-define jsdoc export test var-ast]}]
1114
- ;; Under ESM a var without init, i.e. (declare ^:dynamic *x*), still needs
1115
- ;; a binding other namespaces can import and set! with binding
1116
- (when (and *esm-emitting* (not *esm-repl*) (nil? init) (not (:def-emits-var env)))
1117
- (swap! (:hoist *esm*) conj (esm-def! name)))
1118
- ;; We only want to emit if an init is supplied, this is to avoid dead code
1119
- ;; elimination issues. The REPL is the exception to this rule.
1120
- (when (or init (:def-emits-var env))
1121
- (let [mname (binding [*esm-emitting* false] (munge name))
1122
- ;; at the REPL var is the namespace view's property, see *esm-repl*
1123
- esm-local (when (and *esm-emitting* (not *esm-repl*)) (esm-def! name))
1124
- esm-var-decl? (and esm-local
1125
- (not *esm-in-fn*)
1126
- (= :statement (:context env))
1127
- (not (:def-emits-var env)))]
1128
- (when (and esm-local (not esm-var-decl?))
1129
- (swap! (:hoist *esm*) conj esm-local))
1130
- (emit-comment env doc (concat
1131
- (when goog-define
1132
- [(str "@define {" goog-define "}")])
1133
- jsdoc (:jsdoc init)))
1134
- (when (= :return (:context env))
1135
- (emitln "return ("))
1136
- (when (:def-emits-var env)
1137
- (emitln (iife-open env)))
1138
- (when esm-var-decl?
1139
- (emits "var "))
1140
- (emits var)
1141
- (when init
1142
- (emits " = "
1143
- (if-let [define (get-define mname jsdoc)]
1144
- define
1145
- (if (and goog-define *esm-emitting* (= :invoke (:op init)))
1146
- ;; no override, the default value of (goog/define name default)
1147
- (second (:args init))
1148
- init))))
1149
- (when (:def-emits-var env)
1150
- (emitln "; return (")
1151
- (emits (merge
1152
- {:op :the-var
1153
- :env (assoc env :context :expr)}
1154
- var-ast))
1155
- (emitln ");"
1156
- (iife-close env)))
1157
- (when (= :return (:context env))
1158
- (emitln ")"))
1159
- ;; NOTE: JavaScriptCore does not like this under advanced compilation
1160
- ;; this change was primarily for REPL interactions - David
1161
- ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined")
1162
- (when-not (= :expr (:context env)) (emitln ";"))
1163
- (when export
1164
- (if *esm-emitting*
1165
- ;; also a global for scripts calling into the build, i.e. my.app.init()
1166
- (emitln (munge 'goog/exportSymbol) "('" (binding [*esm-emitting* false] (munge export)) "', " var ");")
1167
- (emitln "goog.exportSymbol('" (munge export) "', " mname ");")))
1168
- (when (and ana/*load-tests* test)
1169
- (when (= :expr (:context env))
1170
- (emitln ";"))
1171
- (emitln var ".cljs$lang$test = " test ";")))))
1172
-
1173
- (defn emit-apply-to
1174
- [{:keys [name params env]}]
1175
- (let [arglist (gensym "arglist__")
1176
- delegate-name (str (munge name) "__delegate")]
1177
- (emitln "(function (" arglist "){")
1178
- (doseq [[i param] (map-indexed vector (drop-last 2 params))]
1179
- (emits "var ")
1180
- (emit param)
1181
- (emits (cc " = cljs.core.first("))
1182
- (emitln arglist ");")
1183
- (emitln arglist (cc " = cljs.core.next(") arglist ");"))
1184
- (if (< 1 (count params))
1185
- (do
1186
- (emits "var ")
1187
- (emit (last (butlast params)))
1188
- (emitln (cc " = cljs.core.first(") arglist ");")
1189
- (emits "var ")
1190
- (emit (last params))
1191
- (emitln (cc " = cljs.core.rest(") arglist ");")
1192
- (emits "return " delegate-name "(")
1193
- (doseq [param params]
1194
- (emit param)
1195
- (when-not (= param (last params)) (emits ",")))
1196
- (emitln ");"))
1197
- (do
1198
- (emits "var ")
1199
- (emit (last params))
1200
- (emitln (cc " = cljs.core.seq(") arglist ");")
1201
- (emits "return " delegate-name "(")
1202
- (doseq [param params]
1203
- (emit param)
1204
- (when-not (= param (last params)) (emits ",")))
1205
- (emitln ");")))
1206
- (emits "})")))
1207
-
1208
- (defn emit-fn-params [params]
1209
- (doseq [param params]
1210
- (emit param)
1211
- ; Avoid extraneous comma (function greet(x, y, z,)
1212
- (when-not (= param (last params))
1213
- (emits ","))))
1214
-
1215
- (defn emit-fn-method
1216
- [{expr :body :keys [type name params env recurs]}]
1217
- (let [async (:async env)]
1218
- (emit-wrap env
1219
- (emits "(" (when async "async ") "function " (munge name) "(")
1220
- (emit-fn-params params)
1221
- (emitln "){")
1222
- (when type
1223
- (emitln "var self__ = this;"))
1224
- (when recurs (emitln "while(true){"))
1225
- (emits expr)
1226
- (when recurs
1227
- (emitln "break;")
1228
- (emitln "}"))
1229
- (emits "})"))))
1230
-
1231
- (defn emit-arguments-to-array
1232
- "Emit code that copies function arguments into an array starting at an index.
1233
- Returns name of var holding the array."
1234
- [startslice]
1235
- (assert (and (>= startslice 0) (integer? startslice)))
1236
- (let [mname (munge (gensym))
1237
- i (str mname "__i")
1238
- a (str mname "__a")]
1239
- (emitln "var " i " = 0, "
1240
- a " = new Array(arguments.length - " startslice ");")
1241
- (emitln "while (" i " < " a ".length) {"
1242
- a "[" i "] = arguments[" i " + " startslice "]; ++" i ";}")
1243
- a))
1244
-
1245
- (defn emit-variadic-fn-method
1246
- [{expr :body max-fixed-arity :fixed-arity variadic :variadic? :keys [type name params env recurs] :as f}]
1247
- (emit-wrap env
1248
- (let [name (or name (gensym))
1249
- mname (munge name)
1250
- delegate-name (str mname "__delegate")
1251
- async (:async env)]
1252
- (emitln (when *esm-emitting* "/*@__PURE__*/") "(function() { ")
1253
- (emits "var " delegate-name " = " (when async "async ") "function (")
1254
- (doseq [param params]
1255
- (emit param)
1256
- (when-not (= param (last params)) (emits ",")))
1257
- (emitln "){")
1258
- (when type
1259
- (emitln "var self__ = this;"))
1260
- (when recurs (emitln "while(true){"))
1261
- (emits expr)
1262
- (when recurs
1263
- (emitln "break;")
1264
- (emitln "}"))
1265
- (emitln "};")
1266
-
1267
- (emitln "var " mname " = " (when async "async ") "function ("
1268
- (comma-sep
1269
- (if variadic
1270
- (concat (butlast params) ['var_args])
1271
- params)) "){")
1272
- (when type
1273
- (emitln "var self__ = this;"))
1274
- (when variadic
1275
- (emits "var ")
1276
- (emit (last params))
1277
- (emitln " = null;")
1278
- (emitln "if (arguments.length > " (dec (count params)) ") {")
1279
- (let [a (emit-arguments-to-array (dec (count params)))]
1280
- (emitln " " (last params) (cc " = new cljs.core.IndexedSeq(") a ",0,null);"))
1281
- (emitln "} "))
1282
- (emits "return " delegate-name ".call(this,")
1283
- (doseq [param params]
1284
- (emit param)
1285
- (when-not (= param (last params)) (emits ",")))
1286
- (emits ");")
1287
- (emitln "};")
1288
-
1289
- (emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";")
1290
- (emits mname ".cljs$lang$applyTo = ")
1291
- (emit-apply-to (assoc f :name name))
1292
- (emitln ";")
1293
- (emitln mname ".cljs$core$IFn$_invoke$arity$variadic = " delegate-name ";")
1294
- (emitln "return " mname ";")
1295
- (emitln "})()"))))
1296
-
1297
- (defmethod emit* :fn
1298
- [{variadic :variadic? :keys [name named? env methods max-fixed-arity recur-frames in-loop loop-lets]}]
1299
- ;;fn statements get erased, serve no purpose and can pollute scope if named
1300
- (when-not (= :statement (:context env))
1301
- (let [;; Under ESM the name def gives a fn is the module binding of its var:
1302
- ;; the function expression's name would shadow it in the fn's body,
1303
- ;; where calls (as the dispatcher of a variadic defn) must see the
1304
- ;; value set! (and with-redefs) assign. The engine names the fn
1305
- ;; after the binding it's assigned to.
1306
- name (when-not (and *esm-emitting* (not named?)) name)
1307
- recur-params (mapcat :params (filter #(and % @(:flag %)) recur-frames))
1308
- loop-locals
1309
- (->> (concat recur-params
1310
- ;; need to capture locals only if in recur fn or loop
1311
- (when (or in-loop (seq recur-params))
1312
- (mapcat :params loop-lets)))
1313
- (map munge)
1314
- seq)
1315
- async (:async env)]
1316
- (when loop-locals
1317
- (when (= :return (:context env))
1318
- (emits "return "))
1319
- (emitln "((function (" (comma-sep (map munge loop-locals)) "){")
1320
- (when-not (= :return (:context env))
1321
- (emits "return ")))
1322
- (if (= 1 (count methods))
1323
- (if variadic
1324
- (emit-variadic-fn-method (assoc (first methods) :name name))
1325
- (emit-fn-method (assoc (first methods) :name name)))
1326
- (let [name (or name (gensym))
1327
- mname (munge name)
1328
- maxparams (apply max-key count (map :params methods))
1329
- mmap (into {}
1330
- (map (fn [method]
1331
- [(munge (symbol (str mname "__" (count (:params method)))))
1332
- method])
1333
- methods))
1334
- ms (sort-by #(-> % second :params count) (seq mmap))]
1335
- (when (= :return (:context env))
1336
- (emits "return "))
1337
- ;; only defines and returns the fn, marked pure for bundlers
1338
- (emitln (when *esm-emitting* "/*@__PURE__*/") "(function() {")
1339
- (emitln "var " mname " = null;")
1340
- (doseq [[n meth] ms]
1341
- (emits "var " n " = ")
1342
- (if (:variadic? meth)
1343
- (emit-variadic-fn-method meth)
1344
- (emit-fn-method meth))
1345
- (emitln ";"))
1346
- (emitln mname " = " (when async "async ") "function("
1347
- (comma-sep (if variadic
1348
- (concat (butlast maxparams) ['var_args])
1349
- maxparams)) "){")
1350
- (when variadic
1351
- (emits "var ")
1352
- (emit (last maxparams))
1353
- (emitln " = var_args;"))
1354
- (emitln "switch(arguments.length){")
1355
- (doseq [[n meth] ms]
1356
- (if (:variadic? meth)
1357
- (do (emitln "default:")
1358
- (let [restarg (munge (gensym))]
1359
- (emitln "var " restarg " = null;")
1360
- (emitln "if (arguments.length > " max-fixed-arity ") {")
1361
- (let [a (emit-arguments-to-array max-fixed-arity)]
1362
- (emitln restarg (cc " = new cljs.core.IndexedSeq(") a ",0,null);"))
1363
- (emitln "}")
1364
- (emitln "return " n ".cljs$core$IFn$_invoke$arity$variadic("
1365
- (comma-sep (butlast maxparams))
1366
- (when (> (count maxparams) 1) ", ")
1367
- restarg ");")))
1368
- (let [pcnt (count (:params meth))]
1369
- (emitln "case " pcnt ":")
1370
- (emitln "return " n ".call(this" (if (zero? pcnt) nil
1371
- (list "," (comma-sep (take pcnt maxparams)))) ");"))))
1372
- (emitln "}")
1373
- (let [arg-count-js (if (= 'self__ (-> ms first val :params first :name))
1374
- "(arguments.length - 1)"
1375
- "arguments.length")]
1376
- (emitln "throw(new Error('Invalid arity: ' + " arg-count-js "));"))
1377
- (emitln "};")
1378
- (when variadic
1379
- (emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";")
1380
- (emitln mname ".cljs$lang$applyTo = " (some #(let [[n m] %] (when (:variadic? m) n)) ms) ".cljs$lang$applyTo;"))
1381
- (doseq [[n meth] ms]
1382
- (let [c (count (:params meth))]
1383
- (if (:variadic? meth)
1384
- (emitln mname ".cljs$core$IFn$_invoke$arity$variadic = " n ".cljs$core$IFn$_invoke$arity$variadic;")
1385
- (emitln mname ".cljs$core$IFn$_invoke$arity$" c " = " n ";"))))
1386
- (emitln "return " mname ";")
1387
- (emitln "})()")))
1388
- (when loop-locals
1389
- (emitln ";})(" (comma-sep loop-locals) "))")))))
1390
-
1391
- (defmethod emit* :do
1392
- [{:keys [statements ret env]}]
1393
- (let [context (:context env)]
1394
- (when (and (seq statements) (= :expr context)) (emitln (iife-open env)))
1395
- (doseq [s statements] (emitln s))
1396
- (emit ret)
1397
- (when (and (seq statements) (= :expr context)) (emitln (iife-close env)))))
1398
-
1399
- (defmethod emit* :try
1400
- [{try :body :keys [env catch name finally]}]
1401
- (let [context (:context env)]
1402
- (if (or name finally)
1403
- (do
1404
- (when (= :expr context)
1405
- (emits (iife-open env)))
1406
- (emits "try{" try "}")
1407
- (when name
1408
- (emits "catch (" (munge name) "){" catch "}"))
1409
- (when finally
1410
- (assert (not= :const (:op (ana/unwrap-quote finally))) "finally block cannot contain constant")
1411
- (emits "finally {" finally "}"))
1412
- (when (= :expr context)
1413
- (emits (iife-close env))))
1414
- (emits try))))
1415
-
1416
- (defn emit-let
1417
- [{expr :body :keys [bindings env]} is-loop]
1418
- (let [context (:context env)]
1419
- (when (= :expr context)
1420
- (emits (iife-open env)))
1421
- (binding [*lexical-renames*
1422
- (into *lexical-renames*
1423
- (when (= :statement context)
1424
- (map
1425
- (fn [binding]
1426
- (let [name (:name binding)]
1427
- (vector (hash-scope binding)
1428
- (gensym (str name "-")))))
1429
- bindings)))]
1430
- (doseq [{:keys [init] :as binding} bindings]
1431
- (emits "var ")
1432
- (emit binding) ; Binding will be treated as a var
1433
- (emitln " = " init ";"))
1434
- (when is-loop (emitln "while(true){"))
1435
- (emits expr)
1436
- (when is-loop
1437
- (emitln "break;")
1438
- (emitln "}")))
1439
- (when (= :expr context) (emits (iife-close env)))))
1440
-
1441
- (defmethod emit* :let [ast]
1442
- (emit-let ast false))
1443
-
1444
- (defmethod emit* :loop [ast]
1445
- (emit-let ast true))
1446
-
1447
- (defmethod emit* :recur
1448
- [{:keys [frame exprs env]}]
1449
- (let [temps (vec (take (count exprs) (repeatedly gensym)))
1450
- params (:params frame)]
1451
- (dotimes [i (count exprs)]
1452
- (emitln "var " (temps i) " = " (exprs i) ";"))
1453
- (dotimes [i (count exprs)]
1454
- (emitln (munge (params i)) " = " (temps i) ";"))
1455
- (emitln "continue;")))
1456
-
1457
- (defmethod emit* :letfn
1458
- [{expr :body :keys [bindings env]}]
1459
- (let [context (:context env)]
1460
- (when (= :expr context) (emits (iife-open env)))
1461
- (doseq [{:keys [init] :as binding} bindings]
1462
- (emitln "var " (munge binding) " = " init ";"))
1463
- (emits expr)
1464
- (when (= :expr context) (emits (iife-close env)))))
1465
-
1466
- (defn protocol-prefix [psym]
1467
- (symbol (str (-> (str psym)
1468
- (.replace #?(:clj \. :cljs (js/RegExp. "\\." "g")) \$)
1469
- (.replace \/ \$))
1470
- "$")))
1471
-
1472
- (defmethod emit* :invoke
1473
- [{f :fn :keys [args env] :as expr}]
1474
- (let [info (:info f)
1475
- fn? (and ana/*cljs-static-fns*
1476
- (not (:dynamic info))
1477
- (:fn-var info))
1478
- protocol (:protocol info)
1479
- tag (ana/infer-tag env (first (:args expr)))
1480
- proto? (and protocol tag
1481
- (or (and ana/*cljs-static-fns* protocol (= tag 'not-native))
1482
- (and
1483
- (or ana/*cljs-static-fns*
1484
- (:protocol-inline env))
1485
- (or (= protocol tag)
1486
- ;; ignore new type hints for now - David
1487
- (and (not (set? tag))
1488
- (not ('#{any clj clj-or-nil clj-nil number string boolean function object array js} tag))
1489
- (when-let [ps (:protocols
1490
- (ana/resolve-existing-var env
1491
- ;; we're just checking for protocol methods,
1492
- ;; an internal optimization, don't emit warnings
1493
- (vary-meta tag assoc ::ana/no-resolve true)))]
1494
- (ps protocol)))))))
1495
- first-arg-tag (ana/infer-tag env (first (:args expr)))
1496
- opt-not? (and (= (:name info) 'cljs.core/not)
1497
- (= first-arg-tag 'boolean))
1498
- opt-count? (and (= (:name info) 'cljs.core/count)
1499
- (boolean ('#{string array} first-arg-tag)))
1500
- ns (:ns info)
1501
- ftag (ana/infer-tag env f)
1502
- js? (or (= ns 'js) (= ns 'Math) (:foreign info)) ;; foreign - i.e. global / Node.js library
1503
- goog? (when ns
1504
- (or (= ns 'goog)
1505
- (when-let [ns-str (str ns)]
1506
- (= (get (string/split ns-str #"\.") 0 nil) "goog"))
1507
- (not (contains? (::ana/namespaces @env/*compiler*) ns))))
1508
-
1509
- keyword? (or (= 'cljs.core/Keyword ftag)
1510
- (let [f (ana/unwrap-quote f)]
1511
- (and (= (-> f :op) :const)
1512
- (keyword? (-> f :form)))))
1513
- [f variadic-invoke]
1514
- (if fn?
1515
- (let [arity (count args)
1516
- variadic? (:variadic? info)
1517
- mps (:method-params info)
1518
- mfa (:max-fixed-arity info)]
1519
- (cond
1520
- ;; if only one method, no renaming needed
1521
- (and (not variadic?)
1522
- (= (count mps) 1))
1523
- [f nil]
1524
-
1525
- ;; direct dispatch to variadic case
1526
- (and variadic? (> arity mfa))
1527
- [(update-in f [:info]
1528
- (fn [info]
1529
- (-> info
1530
- (assoc :name (symbol (str (munge info) ".cljs$core$IFn$_invoke$arity$variadic")))
1531
- ;; bypass local fn-self-name munging, we're emitting direct
1532
- ;; shadowing already applied
1533
- (update-in [:info]
1534
- #(-> % (dissoc :shadow) (dissoc :fn-self-name))))))
1535
- {:max-fixed-arity mfa}]
1536
-
1537
- ;; direct dispatch to specific arity case
1538
- :else
1539
- (let [arities (map count mps)]
1540
- (if (some #{arity} arities)
1541
- [(update-in f [:info]
1542
- (fn [info]
1543
- (-> info
1544
- (assoc :name (symbol (str (munge info) ".cljs$core$IFn$_invoke$arity$" arity)))
1545
- ;; bypass local fn-self-name munging, we're emitting direct
1546
- ;; shadowing already applied
1547
- (update-in [:info]
1548
- #(-> % (dissoc :shadow) (dissoc :fn-self-name)))))) nil]
1549
- [f nil]))))
1550
- [f nil])]
1551
- (emit-wrap env
1552
- (cond
1553
- opt-not?
1554
- (emits "(!(" (first args) "))")
1555
-
1556
- opt-count?
1557
- (emits "((" (first args) ").length)")
1558
-
1559
- proto?
1560
- (let [pimpl (str (munge (protocol-prefix protocol))
1561
- (munge (name (:name info))) "$arity$" (count args))]
1562
- (emits (first args) "." pimpl "(" (comma-sep (cons "null" (rest args))) ")"))
1563
-
1564
- keyword?
1565
- (emits f ".cljs$core$IFn$_invoke$arity$" (count args) "(" (comma-sep args) ")")
1566
-
1567
- variadic-invoke
1568
- (let [mfa (:max-fixed-arity variadic-invoke)]
1569
- (emits f "(" (comma-sep (take mfa args))
1570
- (when-not (zero? mfa) ",")
1571
- (cc "cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([")
1572
- (comma-sep (drop mfa args)) "], 0))"))
1573
-
1574
- (or fn? js? goog?)
1575
- (emits f "(" (comma-sep args) ")")
1576
-
1577
- :else
1578
- (if (and ana/*cljs-static-fns* (#{:var :local :js-var} (:op f)))
1579
- ;; higher order case, static information missing
1580
- (let [fprop (str ".cljs$core$IFn$_invoke$arity$" (count args))]
1581
- (if ana/*fn-invoke-direct*
1582
- (emits "(" f fprop " ? " f fprop "(" (comma-sep args) ") : "
1583
- f "(" (comma-sep args) "))")
1584
- (emits "(" f fprop " ? " f fprop "(" (comma-sep args) ") : "
1585
- f ".call(" (comma-sep (cons "null" args)) "))")))
1586
- (emits f ".call(" (comma-sep (cons "null" args)) ")"))))))
1587
-
1588
- (defmethod emit* :new
1589
- [{ctor :class :keys [args env]}]
1590
- (emit-wrap env
1591
- (emits "(new " ctor "("
1592
- (comma-sep args)
1593
- "))")))
1594
-
1595
- (defmethod emit* :qualified-method
1596
- [{ctor :class :keys [args env kind name]}]
1597
- (if (= :new kind)
1598
- (emit-wrap env
1599
- (emits "(function (...args) { return Reflect.construct(" ctor ", args) })"))
1600
- (emit-wrap env
1601
- (emits "(function (x, ...args) { return Reflect.apply(" ctor ".prototype." name ", x, args) })"))))
1602
-
1603
- (defn- esm-foreign-var
1604
- "If ast references a var of another namespace compiled to an ES module,
1605
- returns [ns name]. Closure Library namespaces run by the compatibility
1606
- layer are objects, set! assigns their properties; shims are modules."
1607
- [{:keys [op info]}]
1608
- (when (and *esm-emitting* (= :var op))
1609
- (let [sym (:name info)
1610
- ns (namespace sym)]
1611
- (when (and ns
1612
- (or *esm-repl* (not= ns (str ana/*cljs-ns*)))
1613
- (not (string/includes? (name sym) "."))
1614
- (or (not (or (= "goog" ns) (string/starts-with? ns "goog.")))
1615
- #?(:clj (contains? (esm-goog-shim-exports ns ana/*cljs-ns*) (esm-var-name (name sym)))
1616
- :cljs false))
1617
- (esm-module-ns? ns))
1618
- [ns (name sym)]))))
1619
-
1620
- (defmethod emit* :set!
1621
- [{:keys [target val env]}]
1622
- ;; Imported ES module bindings are read-only, set! goes through a setter
1623
- ;; exported by the module owning the var.
1624
- (if-let [[ns nm] (esm-foreign-var target)]
1625
- (let [export (esm-var-name nm)]
1626
- (swap! (:refs *esm*) conj ns)
1627
- (emit-wrap env
1628
- (if (:dynamic (:info target))
1629
- (emits (esm-ns-alias ns) ".$set$" export "(" val ")")
1630
- (emits (esm-ns-alias ns) ".$$set(\"" export "\", " val ")"))))
1631
- (emit-wrap env (emits "(" target " = " val ")"))))
1632
-
1633
- (defn sublib-select
1634
- [sublib]
1635
- (when sublib
1636
- (let [xs (string/split sublib #"\.")]
1637
- (apply str
1638
- (map #(str "['" % "']") xs)))))
1639
-
1640
- (defn emit-global-export [ns-name global-exports lib opts]
1641
- (let [[lib' sublib] (ana/lib&sublib lib)
1642
- ref (str "goog.global"
1643
- ;; Convert object dot access to bracket access
1644
- (->> (string/split (name (or (get global-exports (symbol lib'))
1645
- (get global-exports (name lib'))))
1646
- #"\.")
1647
- (map (fn [prop] (str "[\"" prop "\"]")))
1648
- (apply str)))]
1649
- (when (and (ana/external-dep? lib')
1650
- (= :none (:optimizations opts)))
1651
- (emitln
1652
- "if(!" ref ") throw new Error(\"External library, " lib' ", never provided\");"))
1653
- (emitln
1654
- (munge ns-name) "."
1655
- (ana/munge-global-export lib)
1656
- " = "
1657
- ref
1658
- (sublib-select sublib)
1659
- ";")))
1660
-
1661
- (defn load-libs
1662
- [libs seen reloads deps ns-name]
1663
- (let [{:keys [options js-dependency-index]} @env/*compiler*
1664
- {:keys [target nodejs-rt optimizations]} options
1665
- loaded-libs (munge 'cljs.core.*loaded-libs*)
1666
- loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*))
1667
- [node-libs libs-to-load] (let [libs (remove (set (vals seen)) (filter (set (vals libs)) deps))]
1668
- (if (= :nodejs target)
1669
- (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? libs)]
1670
- [node-libs libs-to-load])
1671
- [nil libs]))
1672
- [goog-modules libs-to-load] (let [{goog-modules true libs-to-load false}
1673
- (group-by ana/goog-module-dep? libs-to-load)]
1674
- [goog-modules libs-to-load])
1675
- global-exports-libs (filter ana/dep-has-global-exports? libs-to-load)]
1676
- (when (-> libs meta :reload-all)
1677
- (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set([\"cljs.core\"]);")
1678
- (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set([\"cljs.core\"]);"))
1679
- (doseq [lib libs-to-load]
1680
- (cond
1681
- #?@(:clj
1682
- [(ana/foreign-dep? lib)
1683
- ;; we only load foreign libraries under optimizations :none
1684
- ;; under :modules we also elide loads, as the module loader will
1685
- ;; have handled it - David
1686
- (when (and (= :none optimizations)
1687
- (not (contains? options :modules)))
1688
- (let [[lib _] (ana/lib&sublib lib)]
1689
- (if nodejs-rt
1690
- ;; under node.js we load foreign libs globally
1691
- (let [ijs (get js-dependency-index (name lib))]
1692
- (emitln "cljs.core.load_file("
1693
- (-> (io/file (util/output-directory options)
1694
- (or (deps/-relative-path ijs)
1695
- (util/relative-name (:url ijs))))
1696
- str
1697
- escape-string
1698
- wrap-in-double-quotes)
1699
- ");"))
1700
- (if-not (ana/external-dep? lib)
1701
- (emitln "goog.require('" (munge lib) "');")
1702
- ;; TODO: validate the lib exists
1703
- ))))]
1704
- :cljs
1705
- [(and (ana/foreign-dep? lib)
1706
- (not (keyword-identical? optimizations :none)))
1707
- nil])
1708
-
1709
- (or (-> libs meta :reload)
1710
- (= (get reloads lib) :reload))
1711
- (emitln "goog.require('" (munge lib) "', 'reload');")
1712
-
1713
- (or (-> libs meta :reload-all)
1714
- (= (get reloads lib) :reload-all))
1715
- (emitln "goog.require('" (munge lib) "', 'reload-all');")
1716
-
1717
- :else
1718
- (when-not (= lib 'goog)
1719
- (emitln "goog.require('" (munge lib) "');"))))
1720
- ;; Node Libraries
1721
- (doseq [lib node-libs]
1722
- (let [[lib' sublib] (ana/lib&sublib lib)]
1723
- (emitln (munge ns-name) "."
1724
- (ana/munge-node-lib lib)
1725
- " = require('" lib' "')" (sublib-select sublib) ";")))
1726
- ;; Google Closure Library Modules (i.e. goog.module(...))
1727
- ;; these must be assigned to vars
1728
- (doseq [lib goog-modules]
1729
- (let [[lib' sublib] (ana/lib&sublib lib)]
1730
- (emitln "goog.require('" lib' "');")
1731
- ;; we emit goog.scope here to suppress a Closure error about
1732
- ;; goog.module.get when compiling - meant to discourage incorrect
1733
- ;; usage by hand written code - not applicable here
1734
- (emitln "goog.scope(function(){")
1735
- (emitln (munge ns-name) "."
1736
- (ana/munge-goog-module-lib lib)
1737
- " = goog.module.get('" lib' "')" (sublib-select sublib) ";")
1738
- (emitln "});")))
1739
- ;; Global Exports
1740
- (doseq [lib global-exports-libs]
1741
- (let [{:keys [global-exports]} (get js-dependency-index (name (-> lib ana/lib&sublib first)))]
1742
- (emit-global-export ns-name global-exports lib options)))
1743
- (when (-> libs meta :reload-all)
1744
- (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))
1745
-
1746
- #?(:clj
1747
- (def esm-goog-shim-exports
1748
- "The names exported by the hand written ES module shim of a Closure
1749
- Library namespace, nil if there's none. Shims implement what cljs.core and
1750
- the standard library use."
1751
- (let [exports
1752
- (memoize
1753
- (fn [ns]
1754
- (when-let [res (io/resource (str "cljs/esm/" (util/ns->relpath ns :js)))]
1755
- (let [src (slurp res)
1756
- decl #"export\s+(/\*\s*stub\s*\*/\s*)?(?:async\s+)?(?:function\*?|class|const|let|var)\s+([\w$]+)"]
1757
- {:all (into #{}
1758
- (concat
1759
- (map #(nth % 2) (re-seq decl src))
1760
- (mapcat (fn [[_ names]]
1761
- (map #(string/trim (last (string/split % #"\s+as\s+")))
1762
- (string/split names #",")))
1763
- (re-seq #"export\s*\{([^}]*)\}" src))))
1764
- ;; export /* stub */ ..., only good enough for cljs.core
1765
- :stubs (into #{} (keep (fn [[_ stub? name]] (when stub? name))) (re-seq decl src))}))))]
1766
- (fn
1767
- ([ns] (:all (exports ns)))
1768
- ([ns from-ns]
1769
- (when-let [{:keys [all stubs]} (exports ns)]
1770
- (if (= 'cljs.core from-ns) all (reduce disj all stubs))))))))
1771
-
1772
- #?(:clj
1773
- (defn esm-goog-lib-path
1774
- "The output path of the module exporting a Closure Library namespace run
1775
- by the compatibility layer, see cljs.esm/install-goog-libs."
1776
- [ns]
1777
- (str "goog-lib/ns/" ns ".js")))
1778
-
1779
- #?(:clj
1780
- (defn- esm-path
1781
- "Import path of path (relative to the output directory) from namespace
1782
- from."
1783
- [from path]
1784
- (let [from-dir (.getParentFile (io/file (util/ns->relpath from :js)))
1785
- rel (if from-dir
1786
- (str (.relativize (.toPath from-dir) (.toPath (io/file path))))
1787
- path)
1788
- rel (string/replace rel File/separator "/")]
1789
- (if (string/starts-with? rel ".")
1790
- rel
1791
- (str "./" rel)))))
1792
-
1793
- #?(:clj
1794
- (defn- esm-goog-lib!
1795
- "Records a Closure Library namespace used without a shim, returns the
1796
- import path of its module. Recorded in the analysis of namespace ns-name,
1797
- which the analysis cache keeps: a build compiling none of its users still
1798
- writes its module."
1799
- [ns-name ns]
1800
- (when-not (deps/closure-lib (:js-dependency-index @env/*compiler*) ns)
1801
- (ana/warning :esm-unsupported-goog-ns (ana/empty-env) {:ns ns}))
1802
- (swap! env/*compiler* update-in [::ana/namespaces ns-name :cljs.esm/goog-libs]
1803
- (fnil conj #{}) (str ns))
1804
- (esm-path ns-name (esm-goog-lib-path ns))))
1805
-
1806
- #?(:clj
1807
- (defn esm-ns-path
1808
- "Returns the import path of namespace to relative to namespace from."
1809
- [from to]
1810
- (esm-path from (util/ns->relpath to :js))))
1811
-
1812
- #?(:clj
1813
- (defn- esm-import-specifier
1814
- "Returns the import specifier of a JavaScript library. Package names are
1815
- left as is for the bundler to resolve, paths relative to the source file
1816
- are made relative to the output file."
1817
- [lib]
1818
- (let [lib (str lib)
1819
- {:keys [^File src ^File dest]} *esm*
1820
- ;; canonical paths, the output or source directory may be a symlink
1821
- canonical (fn ^java.nio.file.Path [^File f] (.toPath (.getCanonicalFile f)))
1822
- relative (fn [^java.nio.file.Path target]
1823
- (let [^java.nio.file.Path target (canonical (.toFile target))
1824
- ^java.nio.file.Path from (.getParent ^java.nio.file.Path (canonical dest))
1825
- rel (str (.relativize from target))
1826
- rel (string/replace rel File/separator "/")]
1827
- (if (string/starts-with? rel ".")
1828
- rel
1829
- (str "./" rel))))]
1830
- (cond
1831
- (and (or (string/starts-with? lib "./") (string/starts-with? lib "../"))
1832
- src dest)
1833
- (relative (.normalize (.resolve (.getParent (.toAbsolutePath (.toPath src))) lib)))
1834
-
1835
- ;; classpath relative, i.e. "/my/lib.js"
1836
- (and (string/starts-with? lib "/") dest)
1837
- (let [path (subs lib 1)
1838
- path (if (re-find #"\.[cm]?[jt]sx?$" path) path (str path ".js"))
1839
- res (io/resource path)]
1840
- (cond
1841
- (nil? res)
1842
- (throw (ex-info (str "Classpath JavaScript file " lib " not found")
1843
- {:lib lib :clojure.error/phase :compilation}))
1844
-
1845
- (= "file" (.getProtocol ^java.net.URL res))
1846
- (relative (.toAbsolutePath (.toPath (io/file res))))
1847
-
1848
- ;; in a jar, copied to the output directory
1849
- :else
1850
- (let [out (io/file (util/output-directory (:options @env/*compiler*)) path)]
1851
- (util/mkdirs out)
1852
- (with-open [in (io/input-stream res)]
1853
- (io/copy in out))
1854
- (relative (.toAbsolutePath (.toPath out))))))
1855
-
1856
- :else lib))))
1857
-
1858
- #?(:clj
1859
- (defn- emit-esm-import [ns-name dep]
1860
- (let [dep-str (str dep)]
1861
- (cond
1862
- (ana/node-module-dep? dep)
1863
- (let [[lib sublib] (ana/lib&sublib dep)
1864
- alias (str (munge (symbol (str ns-name) (ana/munge-node-lib dep))))
1865
- spec (esm-import-specifier lib)]
1866
- ;; REPL forms reach the binding through the namespace's
1867
- ;; registration, see emit-esm-repl-footer
1868
- (some-> (:libs *esm*) (swap! assoc (esm-var-name (ana/munge-node-lib dep)) alias))
1869
- (cond
1870
- ;; like shadow-cljs, :as binds CommonJS modules' module.exports and
1871
- ;; ES modules' namespace, decided by the bundler plugin
1872
- (and (nil? sublib) (= :shadow (:npm-interop (:options @env/*compiler*))))
1873
- (emitln "import " alias " from \"cljs-npm-as:" spec "\";")
1874
-
1875
- (nil? sublib)
1876
- (emitln "import * as " alias " from \"" spec "\";")
1877
-
1878
- (= "default" sublib)
1879
- (emitln "import " alias " from \"" spec "\";")
1880
-
1881
- :else
1882
- (do
1883
- (emitln "import * as " alias "$module from \"" spec "\";")
1884
- (emitln "const " alias " = " alias "$module" (sublib-select sublib) ";"))))
1885
-
1886
- ;; Closure Library shims are imported when referenced, Closure
1887
- ;; Library namespaces without one are loaded for their side effects
1888
- (or (= "goog" dep-str) (string/starts-with? dep-str "goog."))
1889
- (when-not (or (= "goog" dep-str) (esm-goog-shim-exports dep-str))
1890
- (emitln "import \"" (esm-goog-lib! ns-name dep-str) "\";"))
1891
-
1892
- ;; Closure style libraries on the classpath, run by the Closure
1893
- ;; Library compatibility layer, i.e. transit-js
1894
- (and (not (contains? (::ana/namespaces @env/*compiler*) (symbol dep-str)))
1895
- (deps/closure-lib (:js-dependency-index @env/*compiler*) dep-str))
1896
- (emitln "import \"" (esm-goog-lib! ns-name dep-str) "\";")
1897
-
1898
- (contains? (::ana/namespaces @env/*compiler*) (symbol dep-str))
1899
- (when-not (contains? @(:imported *esm*) dep-str)
1900
- (swap! (:imported *esm*) conj dep-str)
1901
- (emitln "import * as " (esm-ns-alias dep-str) " from \"" (esm-ns-path ns-name dep-str) "\";"))
1902
-
1903
- :else
1904
- (throw
1905
- (ex-info (str "Unsupported dependency " dep-str " for ES module output")
1906
- {:ns ns-name :dep dep :clojure.error/phase :compilation}))))))
1907
-
1908
- #?(:clj
1909
- (defn- emit-esm-preloads
1910
- "Main namespaces import the :preloads first, ES modules evaluate their
1911
- imports in order."
1912
- [ns-name]
1913
- (let [{:keys [main preloads esm-repl]} (:options @env/*compiler*)
1914
- mains (set (map symbol (cond (coll? main) main main [main])))]
1915
- (when (and (contains? mains ns-name)
1916
- (not (some #{ns-name} (map symbol preloads))))
1917
- (doseq [preload (cond->> preloads esm-repl (cons esm-repl-runtime))]
1918
- (emitln "import \"" (esm-ns-path ns-name preload) "\";"))))))
1919
-
1920
- #?(:clj (declare emit-esm-ns-import))
1921
-
1922
- #?(:clj
1923
- (defn- emit-esm-imports [ns-name deps]
1924
- (emit-esm-preloads ns-name)
1925
- (when-not (= 'cljs.core ns-name)
1926
- (emit-esm-import ns-name 'cljs.core)
1927
- (when (-> @env/*compiler* :options :emit-constants)
1928
- (emit-esm-ns-import ns-name ana/constants-ns-sym)))
1929
- (doseq [dep deps]
1930
- (emit-esm-import ns-name dep))))
1931
-
1932
- #?(:clj
1933
- (defn- emit-esm-ns-import
1934
- "Emits the import of a namespace referenced by ns-name."
1935
- [ns-name ns]
1936
- (emitln "import * as " (esm-ns-alias ns) " from \"" (esm-ns-path ns-name ns) "\";")))
1937
-
1938
- #?(:clj
1939
- (defn emit-esm-hmr-header
1940
- "Under :esm-hmr, a reloaded module first restores the values of the
1941
- instance it replaces, so that defonce keeps its value."
1942
- []
1943
- (emitln "if (import.meta.hot && import.meta.hot.data.$$vals) { "
1944
- "const vals = import.meta.hot.data.$$vals; "
1945
- "for (const k in vals) { try { $$set(k, vals[k]); } catch (e) {} } }")))
1946
-
1947
- #?(:clj
1948
- (defn- emit-esm-hmr-footer
1949
- "Under :esm-hmr modules accept their own updates. Importers keep the
1950
- bindings of the module instance they imported, the new instance updates
1951
- the bindings of all previous ones through their $$set. Runs
1952
- ^:dev/before-load and ^:dev/after-load hooks around updates."
1953
- [ns-name exports]
1954
- (let [opts (:options @env/*compiler*)
1955
- ;; ^:dev/before-load / ^:dev/after-load fns, and fns of this
1956
- ;; namespace given as :esm-before-load / :esm-after-load
1957
- hooks (fn [k]
1958
- (->> (concat
1959
- (->> (get-in @env/*compiler* [::ana/namespaces ns-name :defs])
1960
- (filter (fn [[_ v]] (get-in v [:meta k])))
1961
- (map (fn [[sym _]] (symbol (str ns-name) (str sym)))))
1962
- (->> (get opts ({:dev/before-load :esm-before-load
1963
- :dev/after-load :esm-after-load} k))
1964
- (filter #(= (str ns-name) (namespace %)))))
1965
- distinct
1966
- (map #(str (munge %)))
1967
- sort))
1968
- vals (str "{" (string/join ", " (map (fn [[export local]] (str "\"" export "\": " local)) exports)) "}")]
1969
- (emitln "if (import.meta.hot) {")
1970
- (emitln "const data = import.meta.hot.data;")
1971
- (emitln "const vals = " vals ";")
1972
- (emitln "for (const set of (data.$$sets || [])) for (const k in vals) { try { set(k, vals[k]); } catch (e) {} }")
1973
- (emitln "(data.$$sets || (data.$$sets = [])).push($$set);")
1974
- (emitln "import.meta.hot.dispose((data) => { data.$$vals = " vals "; });")
1975
- (doseq [[k hook event] [[:dev/before-load "before_load" "vite:beforeUpdate"]
1976
- [:dev/after-load "after_load" "vite:afterUpdate"]]]
1977
- (when-let [fns (seq (hooks k))]
1978
- (emitln "data.$$" hook " = () => { " (string/join " " (map #(str % "();") fns)) " };")
1979
- (emitln "if (!data.$$" hook "_on) { data.$$" hook "_on = true; "
1980
- "import.meta.hot.on(\"" event "\", () => data.$$" hook "()); }")))
1981
- (if (= 'cljs.core ns-name)
1982
- ;; values of a previous cljs.core aren't compatible with a new one
1983
- (emitln "import.meta.hot.accept(() => { if (typeof location !== \"undefined\") location.reload(); });")
1984
- (emitln "import.meta.hot.accept();"))
1985
- (emitln "}"))))
1986
-
1987
- #?(:clj (def ^:private esm-repl-stamps (java.util.concurrent.atomic.AtomicLong.)))
1988
-
1989
- #?(:clj
1990
- (defn- emit-esm-repl-footer
1991
- "Under :esm-repl modules register their namespace object, the setters
1992
- of all their instances (see emit-esm-hmr-footer) and the npm modules
1993
- they import, for the forms evaluated at the REPL, see
1994
- cljs.esm.repl-runtime. gen counts the instances, it changes once a
1995
- hot reload ran the module again. stamp identifies the compile, also
1996
- recorded as :cljs.esm/repl-stamp of the namespace's analysis (which the
1997
- analysis cache keeps): the REPL waits for a page to run the module of a
1998
- compile."
1999
- [ns-name libs]
2000
- (let [ns (pr-str (str ns-name))
2001
- stamp (str (System/currentTimeMillis) "-" (.incrementAndGet ^java.util.concurrent.atomic.AtomicLong esm-repl-stamps))]
2002
- (swap! env/*compiler* assoc-in [::ana/namespaces ns-name :cljs.esm/repl-stamp] stamp)
2003
- (emitln "{ const $$r = globalThis.$CLJS_ESM || (globalThis.$CLJS_ESM = { nses: new Map() }), $$e = $$r.nses.get(" ns ");")
2004
- (emitln "$$r.nses.set(" ns ", { mod: " (esm-ns-alias ns-name) ", "
2005
- "sets: import.meta.hot ? import.meta.hot.data.$$sets : [$$set], "
2006
- "libs: {" (string/join ", " (map (fn [[k v]] (str "\"" k "\": " v)) (sort libs))) "}, "
2007
- "gen: $$e ? $$e.gen + 1 : 1, stamp: \"" stamp "\" });")
2008
- (emitln "if ($$r.registered) $$r.registered(" ns "); }"))))
2009
-
2010
- #?(:clj
2011
- (defn- dts-param
2012
- "A TypeScript parameter name for a ClojureScript param, destructuring
2013
- forms and gensyms become argN."
2014
- [param i]
2015
- (let [n (when (symbol? param) (str (munge param)))]
2016
- (if (and n
2017
- (re-matches #"[A-Za-z_][\w]*" n)
2018
- (not (string/starts-with? n "p__"))
2019
- (not= "_" n))
2020
- n
2021
- (str "arg" i)))))
2022
-
2023
- #?(:clj
2024
- (defn esm-dts
2025
- "TypeScript declarations of the exports of namespace ns-name, so that
2026
- TypeScript code can import ClojureScript modules."
2027
- [ns-name export-syms]
2028
- (let [defs (get-in @env/*compiler* [::ana/namespaces ns-name :defs])
2029
- doc (fn [{:keys [doc]}]
2030
- (when (and (string? doc) (not (string/blank? doc)))
2031
- (str "/**\n"
2032
- (apply str (map #(str " * " (string/replace % "*/" "* /") "\n")
2033
- (string/split-lines doc)))
2034
- " */\n")))]
2035
- (apply str
2036
- "// TypeScript declarations of ClojureScript namespace " ns-name "\n"
2037
- (for [[export sym] (sort export-syms)
2038
- :let [info (get defs (symbol (name sym)))]]
2039
- (str (doc info)
2040
- (if (and (:fn-var info) (seq (:method-params info)))
2041
- (apply str
2042
- (for [params (:method-params info)
2043
- :let [variadic? (and (:variadic? info)
2044
- (> (count params) (or (:max-fixed-arity info) 0)))
2045
- fixed (if variadic? (butlast params) params)]]
2046
- (str "export declare function " export "("
2047
- (string/join ", "
2048
- (concat
2049
- (map-indexed (fn [i p] (str (dts-param p i) ": any")) fixed)
2050
- (when variadic? ["...rest: any[]"])))
2051
- "): any;\n")))
2052
- (str "export declare const " export ": any;\n"))))))))
2053
-
2054
- #?(:clj
2055
- (defn emit-esm-footer
2056
- "Emits the imports of namespaces only referenced by fully qualified
2057
- names, the declarations of vars only defined in functions, setters and
2058
- exports."
2059
- [ns-name]
2060
- (let [{:keys [refs imported hoist exports dynamic self-import goog-lib-refs libs]} *esm*
2061
- exports (sort @exports)
2062
- repl? (:esm-repl (:options @env/*compiler*))]
2063
- (when repl?
2064
- (reset! self-import true))
2065
- (emitln)
2066
- ;; Closure Library namespaces run by the compatibility layer, default
2067
- ;; exports are the namespace objects
2068
- (doseq [ns (sort @goog-lib-refs)]
2069
- (emitln "import " (esm-ns-alias ns) "$ from \"" (esm-goog-lib! ns-name ns) "\";"))
2070
- (when @self-import
2071
- (emitln "import * as " (esm-ns-alias ns-name) " from \"./"
2072
- (.getName (io/file (util/ns->relpath ns-name :js))) "\";"))
2073
- (doseq [ns (sort (remove (conj @imported (str ns-name)) @refs))]
2074
- (emit-esm-ns-import ns-name ns))
2075
- (when (seq @hoist)
2076
- (emitln "var " (string/join ", " (sort @hoist)) ";"))
2077
- ;; imported bindings can't be assigned, set! of another namespace's var
2078
- ;; is emitted as a call of one of these
2079
- (doseq [export (sort @dynamic)]
2080
- (emitln "export function $set$" export "(v) { return ("
2081
- (get (into {} exports) export) " = v); }"))
2082
- (emitln "export function $$set(name, v) {")
2083
- (emitln "switch (name) {")
2084
- (doseq [[export local] exports]
2085
- (emitln "case \"" export "\": return (" local " = v);"))
2086
- (emitln "}")
2087
- (emitln "throw new Error(\"No var " ns-name "/\" + name);")
2088
- (emitln "}")
2089
- (emitln "export { "
2090
- (string/join ", " (map (fn [[export local]] (str local " as " export)) exports))
2091
- " };")
2092
- (when (:esm-hmr (:options @env/*compiler*))
2093
- (emit-esm-hmr-footer ns-name exports))
2094
- (when repl?
2095
- (emit-esm-repl-footer ns-name (some-> libs deref))))))
2096
-
2097
- #?(:clj
2098
- (defn emit-esm-repl
2099
- "Emits ast, a form analyzed for the REPL in :return context, as the body
2100
- of a function, see *esm-repl*. Returns {:js :refs :goog-lib-refs}, refs
2101
- the namespaces it references as esm-ns-alias, goog-lib-refs the Closure
2102
- Library namespaces run by the compatibility layer as esm-ns-alias plus
2103
- $: the function's parameters."
2104
- [ast]
2105
- (let [state {:refs (atom #{})
2106
- :imported (atom #{})
2107
- :hoist (atom #{})
2108
- :exports (atom {})
2109
- :dynamic (atom #{})
2110
- :self-import (atom false)
2111
- :goog-lib-refs (atom #{})
2112
- :libs (atom {})
2113
- :export-syms (atom {})}
2114
- js (binding [*esm* state
2115
- *esm-emitting* true
2116
- *esm-repl* true]
2117
- (emit-str ast))]
2118
- {:js js
2119
- :refs @(:refs state)
2120
- :goog-lib-refs @(:goog-lib-refs state)})))
2121
-
2122
- (defmethod emit* :ns*
2123
- [{:keys [name requires uses require-macros reloads env deps]}]
2124
- (cond
2125
- ;; cljs.esm.repl loads the namespaces
2126
- *esm-repl*
2127
- (emit-wrap env (emits "null"))
2128
-
2129
- *esm-emitting*
2130
- #?(:clj (emit-esm-imports ana/*cljs-ns* deps)
2131
- :cljs nil)
2132
-
2133
- :else
2134
- (do
2135
- (load-libs requires nil (:require reloads) deps name)
2136
- (load-libs uses requires (:use reloads) deps name)))
2137
- (when (:repl-env env)
2138
- (emitln "'nil';")))
2139
-
2140
- (defmethod emit* :ns
2141
- [{:keys [name requires uses require-macros reloads env deps]}]
2142
- (cond
2143
- *esm-repl*
2144
- (emit-wrap env (emits "null"))
2145
-
2146
- *esm-emitting*
2147
- #?(:clj (emit-esm-imports name deps)
2148
- :cljs nil)
2149
-
2150
- :else
2151
- (do
2152
- (emitln "goog.provide('" (munge name) "');")
2153
- (when-not (= name 'cljs.core)
2154
- (emitln "goog.require('cljs.core');")
2155
- (when (-> @env/*compiler* :options :emit-constants)
2156
- (emitln "goog.require('" (munge ana/constants-ns-sym) "');")))
2157
- (load-libs requires nil (:require reloads) deps name)
2158
- (load-libs uses requires (:use reloads) deps name))))
2159
-
2160
- (defmethod emit* :deftype
2161
- [{:keys [t fields pmasks body protocols]}]
2162
- (let [fields (map munge fields)]
2163
- (emitln "")
2164
- (emitln "/**")
2165
- (emitln "* @constructor")
2166
- (doseq [protocol protocols]
2167
- (emitln " * @implements {" (munge (str protocol)) "}"))
2168
- (emitln "*/")
2169
- (emit-esm-type-binding t)
2170
- (emitln " = (function (" (comma-sep fields) "){")
2171
- (doseq [fld fields]
2172
- (emitln "this." fld " = " fld ";"))
2173
- (doseq [[pno pmask] pmasks]
2174
- (emitln "this.cljs$lang$protocol_mask$partition" pno "$ = " pmask ";"))
2175
- (emitln "});")
2176
- (emit body)))
2177
-
2178
- (defmethod emit* :class
2179
- [{:keys [env name base params body]}]
2180
- (emit-wrap env
2181
- (emits "(class " (munge name))
2182
- (when base
2183
- (emits " extends " base))
2184
- (emitln " {")
2185
- (when body
2186
- (emitln "constructor(" (comma-sep (map munge params)) ") {")
2187
- ;; this is accessible once the base class' constructor returned
2188
- (emitln (if base "var self__;" "var self__ = this;"))
2189
- (emit body)
2190
- (emitln "}"))
2191
- (emits "})")))
2192
-
2193
- (defmethod emit* :super
2194
- [{:keys [env args]}]
2195
- ;; super() evaluates to this
2196
- (emit-wrap env
2197
- (emits "(self__ = super(" (comma-sep args) "))")))
2198
-
2199
- (defmethod emit* :defrecord
2200
- [{:keys [t fields pmasks body protocols]}]
2201
- (let [fields (concat (map munge fields) '[__meta __extmap __hash])]
2202
- (emitln "")
2203
- (emitln "/**")
2204
- (emitln "* @constructor")
2205
- (doseq [protocol protocols]
2206
- (emitln " * @implements {" (munge (str protocol)) "}"))
2207
- (emitln "*/")
2208
- (emit-esm-type-binding t)
2209
- (emitln " = (function (" (comma-sep fields) "){")
2210
- (doseq [fld fields]
2211
- (emitln "this." fld " = " fld ";"))
2212
- (doseq [[pno pmask] pmasks]
2213
- (emitln "this.cljs$lang$protocol_mask$partition" pno "$ = " pmask ";"))
2214
- (emitln "});")
2215
- (emit body)))
2216
-
2217
- (defn emit-dot
2218
- [{:keys [target field method args env]}]
2219
- (emit-wrap env
2220
- (if field
2221
- (emits target "." (munge field #{}))
2222
- (emits target "." (munge method #{}) "("
2223
- (comma-sep args)
2224
- ")"))))
2225
-
2226
- (defmethod emit* :host-field [ast] (emit-dot ast))
2227
- (defmethod emit* :host-call [ast] (emit-dot ast))
2228
-
2229
- (defmethod emit* :js
2230
- [{:keys [op env code segs args]}]
2231
- (if (and code #?(:clj (.startsWith ^String (string/trim code) "/*")
2232
- :cljs (gstring/startsWith (string/trim code) "/*")))
2233
- (emits code)
2234
- (emit-wrap env
2235
- (if code
2236
- (emits (esm-rewrite-core-refs code))
2237
- (emits (interleave (concat (map esm-rewrite-core-refs segs) (repeat nil))
2238
- (concat args [nil])))))))
2239
-
2240
- ;; TODO: unify renaming helpers - this one was hard to find - David
2241
-
2242
- #?(:clj
2243
- (defn rename-to-js
2244
- "Change the file extension from .cljs to .js. Takes a File or a
2245
- String. Always returns a String."
2246
- [^String file-str]
2247
- (cond
2248
- (.endsWith file-str ".cljs")
2249
- (clojure.string/replace file-str #"\.cljs$" ".js")
2250
-
2251
- (.endsWith file-str ".cljc")
2252
- (if (= "cljs/core.cljc" file-str)
2253
- "cljs/core$macros.js"
2254
- (clojure.string/replace file-str #"\.cljc$" ".js"))
2255
-
2256
- :else
2257
- (throw (util/compilation-error (IllegalArgumentException.
2258
- (str "Invalid source file extension " file-str)))))))
2259
-
2260
- #?(:clj
2261
- (defn with-core-cljs
2262
- "Ensure that core.cljs has been loaded."
2263
- ([] (with-core-cljs
2264
- (when env/*compiler*
2265
- (:options @env/*compiler*))))
2266
- ([opts] (with-core-cljs opts (fn [])))
2267
- ([opts body]
2268
- {:pre [(or (nil? opts) (map? opts))
2269
- (fn? body)]}
2270
- (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs])
2271
- (ana/analyze-file "cljs/core.cljs" opts))
2272
- (body))))
2273
-
2274
- #?(:clj
2275
- (defn url-path [^File f]
2276
- (.getPath (.toURL (.toURI f)))))
2277
-
2278
- #?(:clj
2279
- (defn compiled-by-string
2280
- ([]
2281
- (compiled-by-string
2282
- (when env/*compiler*
2283
- (:options @env/*compiler*))))
2284
- ([opts]
2285
- (str "// Compiled by ClojureScript "
2286
- (util/clojurescript-version)
2287
- (when opts
2288
- (str " " (pr-str (ana/build-affecting-options opts))))))))
2289
-
2290
- #?(:clj
2291
- (defn cached-core [ns ext opts]
2292
- (and (= :none (:optimizations opts))
2293
- (not (esm-mode? opts))
2294
- (not= "cljc" ext)
2295
- (= 'cljs.core ns)
2296
- (io/resource "cljs/core.aot.js"))))
2297
-
2298
- #?(:clj
2299
- (defn macro-ns? [ns ext opts]
2300
- (or (= "clj" ext)
2301
- (= 'cljs.core$macros ns)
2302
- (and (= ns 'cljs.core) (= "cljc" ext))
2303
- (:macros-ns opts))))
2304
-
2305
- #?(:clj
2306
- (defn emit-cached-core [src dest cached opts]
2307
- ;; no need to bother with analysis cache reading, handled by
2308
- ;; with-core-cljs
2309
- (when (or ana/*verbose* (:verbose opts))
2310
- (util/debug-prn "Using cached cljs.core" (str src)))
2311
- (spit dest (slurp cached))
2312
- (.setLastModified ^File dest (util/last-modified src))
2313
- (when (true? (:source-map opts))
2314
- (spit (io/file (str dest ".map"))
2315
- (json/write-str
2316
- (assoc
2317
- (json/read-str (slurp (io/resource "cljs/core.aot.js.map")))
2318
- "file"
2319
- (str (io/file (util/output-directory opts) "cljs" "core.js"))))))
2320
- (merge
2321
- (ana/parse-ns src dest nil)
2322
- {:out-file dest})))
2323
-
2324
- #?(:clj
2325
- (defn emit-source-map [src dest sm-data opts]
2326
- (let [sm-file (io/file (str (.getPath ^File dest) ".map"))]
2327
- (if-let [smap (:source-map-asset-path opts)]
2328
- (emitln "\n//# sourceMappingURL=" smap
2329
- (string/replace (util/path sm-file)
2330
- (str (util/path (io/file (:output-dir opts))))
2331
- "")
2332
- (if (true? (:source-map-timestamp opts))
2333
- (str
2334
- (if-not (string/index-of smap "?") "?" "&")
2335
- "rel=" (System/currentTimeMillis))
2336
- ""))
2337
- (emitln "\n//# sourceMappingURL="
2338
- (or (:source-map-url opts) (.getName sm-file))
2339
- (if (true? (:source-map-timestamp opts))
2340
- (str "?rel=" (System/currentTimeMillis))
2341
- "")))
2342
- (spit sm-file
2343
- (sm/encode {(url-path src) (:source-map sm-data)}
2344
- {:lines (+ (:gen-line sm-data) 2)
2345
- :file (url-path dest)
2346
- :source-map-path (:source-map-path opts)
2347
- :source-map-timestamp (:source-map-timestamp opts)
2348
- :source-map-pretty-print (:source-map-pretty-print opts)
2349
- ;; ES modules are bundled from the output directory, which
2350
- ;; doesn't have the sources (jar ones aren't files)
2351
- :sources-content (when (esm-mode? opts) [(slurp src)])
2352
- :relpaths {(util/path src)
2353
- (util/ns->relpath (first (:provides opts)) (:ext opts))}})))))
2354
-
2355
- #?(:clj
2356
- (defn- emit-source* [src dest out-file ext opts]
2357
- (with-open [out ^java.io.Writer (io/make-writer out-file {})]
2358
- (binding [*out* out
2359
- ana/*cljs-ns* 'cljs.user
2360
- ana/*cljs-file* (.getPath ^File src)
2361
- reader/*alias-map* (or (ana/get-bridged-alias-map) reader/*alias-map* {})
2362
- ana/*checked-arrays* (or ana/*checked-arrays* (:checked-arrays opts))
2363
- ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts))
2364
- *source-map-data* (when (:source-map opts)
2365
- (atom
2366
- {:source-map (sorted-map)
2367
- :gen-line 0}))
2368
- *source-map-data-gen-col* (AtomicLong.)
2369
- find-ns-starts-with (memoize find-ns-starts-with)
2370
- *esm* (when (esm-mode? opts)
2371
- {:src src
2372
- :dest dest
2373
- :refs (atom #{})
2374
- :imported (atom #{})
2375
- :hoist (atom #{})
2376
- :exports (atom {})
2377
- :dynamic (atom #{})
2378
- :self-import (atom false)
2379
- :goog-lib-refs (atom #{})
2380
- :libs (atom {})
2381
- :export-syms (atom {})})]
2382
- (emitln (compiled-by-string opts))
2383
- (when (and *esm* (:esm-hmr opts))
2384
- (emit-esm-hmr-header))
2385
- (with-open [rdr (io/reader src)]
2386
- (let [env (ana/empty-env)
2387
- emitter (when (:parallel-build opts)
2388
- (Executors/newSingleThreadExecutor))
2389
- emit (if *esm*
2390
- #(binding [*esm-emitting* true] (emit %))
2391
- emit)
2392
- emit (if emitter
2393
- #(.execute emitter ^Runnable (bound-fn [] (emit %)))
2394
- emit)]
2395
- (loop [forms (ana/forms-seq* rdr (util/path src))
2396
- ns-name nil
2397
- deps []]
2398
- (if (seq forms)
2399
- (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*))
2400
- {:keys [op] :as ast} (ana/analyze env (first forms) nil opts)]
2401
- (cond
2402
- (= op :ns)
2403
- (let [ns-name (:name ast)
2404
- ns-name (if (and (= 'cljs.core ns-name)
2405
- (= "cljc" ext))
2406
- 'cljs.core$macros
2407
- ns-name)]
2408
- (emit ast)
2409
- (recur (rest forms) ns-name (into deps (:deps ast))))
2410
-
2411
- (= :ns* (:op ast))
2412
- (let [ns-emitted? (some? ns-name)
2413
- ns-name (if-not ns-emitted?
2414
- (ana/gen-user-ns src)
2415
- ns-name)]
2416
- (if-not ns-emitted?
2417
- (emit (assoc ast :name ns-name :op :ns))
2418
- (emit ast))
2419
- (recur (rest forms) ns-name (into deps (:deps ast))))
2420
-
2421
- :else
2422
- (let [ns-emitted? (some? ns-name)
2423
- ns-name (if-not ns-emitted?
2424
- (ana/gen-user-ns src)
2425
- ns-name)]
2426
- (when-not ns-emitted?
2427
- (emit {:op :ns
2428
- :name ns-name}))
2429
- (emit ast)
2430
- (recur (rest forms) ns-name deps))))
2431
- (let [_ (when emitter
2432
- (.shutdown emitter)
2433
- (.awaitTermination emitter 1000 TimeUnit/HOURS))
2434
- _ (when *esm*
2435
- (binding [*esm-emitting* true]
2436
- (emit-esm-footer (or ns-name 'cljs.user))))
2437
- dts (when *esm*
2438
- (esm-dts (or ns-name 'cljs.user) @(:export-syms *esm*)))
2439
- sm-data (when *source-map-data* (assoc @*source-map-data*
2440
- :gen-col (.get ^AtomicLong *source-map-data-gen-col*)))
2441
- ret (merge
2442
- {:ns (or ns-name 'cljs.user)
2443
- :macros-ns (:macros-ns opts)
2444
- :provides [ns-name]
2445
- :requires (if (= ns-name 'cljs.core)
2446
- (vec (distinct deps))
2447
- (cond-> (conj (vec (distinct deps)) 'cljs.core)
2448
- (get-in @env/*compiler* [:options :emit-constants])
2449
- (conj ana/constants-ns-sym)))
2450
- :file dest
2451
- :out-file (.toString ^File dest)
2452
- :source-file src}
2453
- (when sm-data
2454
- {:source-map (:source-map sm-data)})
2455
- (when dts
2456
- {:esm-dts dts}))]
2457
- (when (and sm-data (= :none (:optimizations opts)))
2458
- (emit-source-map src dest sm-data
2459
- (merge opts {:ext ext :provides [ns-name]})))
2460
- (let [path (.getPath (.toURL ^File dest))]
2461
- (swap! env/*compiler* assoc-in [::compiled-cljs path] ret))
2462
- (ana/ensure-defs ns-name)
2463
- (let [{:keys [output-dir cache-analysis]} opts]
2464
- (when (and (true? cache-analysis) output-dir)
2465
- (ana/write-analysis-cache ns-name
2466
- (ana/cache-file src (ana/parse-ns src) output-dir :write)
2467
- src))
2468
- ret))))))))))
2469
-
2470
- #?(:clj
2471
- (defn emit-source [src ^File dest ext opts]
2472
- (if (esm-mode? opts)
2473
- ;; written to a temporary file first, dev servers watching the output
2474
- ;; directory never see a partially written module
2475
- (let [tmp (io/file (str (.getPath dest) ".tmp"))
2476
- ret (emit-source* src dest tmp ext opts)
2477
- dts (io/file (string/replace (.getPath dest) #"\.js$" ".d.ts"))]
2478
- (when-not (false? (:esm-dts opts))
2479
- (spit dts (:esm-dts ret)))
2480
- (java.nio.file.Files/move (.toPath tmp) (.toPath dest)
2481
- (into-array java.nio.file.CopyOption
2482
- [java.nio.file.StandardCopyOption/REPLACE_EXISTING
2483
- java.nio.file.StandardCopyOption/ATOMIC_MOVE]))
2484
- ret)
2485
- (emit-source* src dest dest ext opts))))
2486
-
2487
- #?(:clj
2488
- (defn compile-file*
2489
- ([^File src ^File dest]
2490
- (compile-file* src dest
2491
- (when env/*compiler*
2492
- (:options @env/*compiler*))))
2493
- ([^File src ^File dest opts]
2494
- (ensure
2495
- (with-core-cljs opts
2496
- (fn []
2497
- (when (and (or ana/*verbose* (:verbose opts))
2498
- (not (:compiler-stats opts)))
2499
- (util/debug-prn "Compiling" (str src) "to" (str dest)))
2500
- (util/measure (and (or ana/*verbose* (:verbose opts))
2501
- (:compiler-stats opts))
2502
- (str "Compiling " (str src) " to " (str dest))
2503
- (let [ext (util/ext src)
2504
- {:keys [ns] :as ns-info} (ana/parse-ns src)]
2505
- (if-let [cached (cached-core ns ext opts)]
2506
- [(emit-cached-core src dest cached opts) false]
2507
- (let [opts (if (macro-ns? ns ext opts)
2508
- (assoc opts :macros-ns true)
2509
- opts)
2510
- dest-exists? (.exists dest)
2511
- ret [(emit-source src dest ext opts) dest-exists?]]
2512
- (.setLastModified ^File dest (util/last-modified src))
2513
- ret))))))))))
2514
-
2515
- #?(:clj
2516
- (defn requires-compilation?
2517
- "Return true if the src file requires compilation."
2518
- ([src dest]
2519
- (requires-compilation? src dest
2520
- (when env/*compiler*
2521
- (:options @env/*compiler*))))
2522
- ([^File src ^File dest opts]
2523
- (let [{:keys [ns requires]} (ana/parse-ns src)]
2524
- (if (and (= 'cljs.loader ns) (not (contains? opts :cache-key)))
2525
- false
2526
- (ensure
2527
- (or (not (.exists dest))
2528
- (util/changed? src dest)
2529
- (let [version' (util/compiled-by-version dest)
2530
- version (util/clojurescript-version)]
2531
- (and version (not= version version')))
2532
- (and opts
2533
- (not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns)))
2534
- (not= (ana/build-affecting-options opts)
2535
- (ana/build-affecting-options (util/build-options dest))))
2536
- (and opts (:source-map opts)
2537
- (if (= (:optimizations opts) :none)
2538
- (not (.exists (io/file (str (.getPath dest) ".map"))))
2539
- (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)]))))
2540
- (when-let [recompiled' (and *recompiled* @*recompiled*)]
2541
- (some requires recompiled')))))))))
2542
-
2543
- #?(:clj
2544
- (defn compile-file
2545
- "Compiles src to a file of the same name, but with a .js extension,
2546
- in the src file's directory.
2547
-
2548
- With dest argument, write file to provided location. If the dest
2549
- argument is a file outside the source tree, missing parent
2550
- directories will be created. The src file will only be compiled if
2551
- the dest file has an older modification time.
2552
-
2553
- Both src and dest may be either a String or a File.
2554
-
2555
- Returns a map containing {:ns .. :provides .. :requires .. :file ..}.
2556
- If the file was not compiled returns only {:file ...}"
2557
- ([src]
2558
- (let [dest (rename-to-js src)]
2559
- (compile-file src dest
2560
- (when env/*compiler*
2561
- (:options @env/*compiler*)))))
2562
- ([src dest]
2563
- (compile-file src dest
2564
- (when env/*compiler*
2565
- (:options @env/*compiler*))))
2566
- ([src dest opts]
2567
- {:post [map?]}
2568
- (binding [ana/*file-defs* (atom #{})
2569
- ana/*unchecked-if* false
2570
- ana/*unchecked-arrays* false
2571
- ana/*cljs-warnings* ana/*cljs-warnings*]
2572
- (let [nses (get @env/*compiler* ::ana/namespaces)
2573
- src-file (io/file src)
2574
- dest-file (io/file dest)
2575
- opts (merge {:optimizations :none} opts)]
2576
- (if (.exists src-file)
2577
- (try
2578
- (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts)
2579
- opts (if (and (not= (util/ext src) "clj") ;; skip cljs.core macro-ns
2580
- (= ns 'cljs.core))
2581
- (cond-> opts
2582
- (not (false? (:static-fns opts))) (assoc :static-fns true)
2583
- true (dissoc :checked-arrays))
2584
- opts)]
2585
- (if (or (requires-compilation? src-file dest-file opts)
2586
- (:force opts))
2587
- (do
2588
- (util/mkdirs dest-file)
2589
- (when (and (get-in nses [ns :defs])
2590
- (not= 'cljs.core ns)
2591
- (not= :interactive (:mode opts)))
2592
- (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns))
2593
- (let [[ret recompiled?] (compile-file* src-file dest-file opts)]
2594
- (when (and *recompiled*
2595
- recompiled?)
2596
- (swap! *recompiled* conj ns))
2597
- ret))
2598
- (do
2599
- ;; populate compilation environment with analysis information
2600
- ;; when constants are optimized
2601
- (when (or (and (= ns 'cljs.loader)
2602
- (not (contains? opts :cache-key)))
2603
- (and (true? (:optimize-constants opts))
2604
- (nil? (get-in nses [ns :defs]))))
2605
- (with-core-cljs opts (fn [] (ana/analyze-file src-file opts))))
2606
- (assoc ns-info :out-file (.toString dest-file)))))
2607
- (catch Exception e
2608
- (throw (ex-info (str "failed compiling file:" src) {:file src :clojure.error/phase :compilation} e))))
2609
- (throw (util/compilation-error (java.io.FileNotFoundException. (str "The file " src " does not exist."))))))))))
2610
-
2611
- #?(:clj
2612
- (defn cljs-files-in
2613
- "Return a sequence of all .cljs and .cljc files in the given directory."
2614
- [dir]
2615
- (map io/file
2616
- (reduce
2617
- (fn [m x]
2618
- (if (.endsWith ^String x ".cljs")
2619
- (cond-> (conj m x)
2620
- (contains? m (str (subs x 0 (dec (count x))) "c"))
2621
- (set/difference #{(str (subs x 0 (dec (count x))) "c")}))
2622
- ;; ends with .cljc
2623
- (cond-> m
2624
- (not (contains? m (str (subs x 0 (dec (count x))) "s")))
2625
- (conj x))))
2626
- #{}
2627
- (into []
2628
- (comp
2629
- (filter
2630
- #(let [name (.getName ^File %)]
2631
- (and (or (.endsWith name ".cljs")
2632
- (.endsWith name ".cljc"))
2633
- (not= \. (first name))
2634
- (not (contains? cljs-reserved-file-names name)))))
2635
- (map #(.getPath ^File %)))
2636
- (file-seq dir))))))
2637
-
2638
- #?(:clj
2639
- (defn compile-root
2640
- "Looks recursively in src-dir for .cljs files and compiles them to
2641
- .js files. If target-dir is provided, output will go into this
2642
- directory mirroring the source directory structure. Returns a list
2643
- of maps containing information about each file which was compiled
2644
- in dependency order."
2645
- ([src-dir]
2646
- (compile-root src-dir "out"))
2647
- ([src-dir target-dir]
2648
- (compile-root src-dir target-dir
2649
- (when env/*compiler*
2650
- (:options @env/*compiler*))))
2651
- ([src-dir target-dir opts]
2652
- (swap! env/*compiler* assoc :root src-dir)
2653
- (let [src-dir-file (io/file src-dir)
2654
- inputs (deps/dependency-order
2655
- (map #(ana/parse-ns %)
2656
- (cljs-files-in src-dir-file)))]
2657
- (binding [*inputs* (zipmap (map :ns inputs) inputs)]
2658
- (loop [inputs (seq inputs) compiled []]
2659
- (if inputs
2660
- (let [{:keys [source-file] :as ns-info} (first inputs)
2661
- output-file (util/to-target-file target-dir ns-info)
2662
- ijs (compile-file source-file output-file opts)]
2663
- (recur
2664
- (next inputs)
2665
- (conj compiled
2666
- (assoc ijs :file-name (.getPath output-file)))))
2667
- compiled)))))))
2668
-
2669
- #?(:clj
2670
- (defn find-source [file]
2671
- (ana/parse-ns file)))
2672
-
2673
- #?(:clj
2674
- (defn find-root-sources
2675
- [src-dir]
2676
- (let [src-dir-file (io/file src-dir)]
2677
- (map find-source (cljs-files-in src-dir-file)))))
2678
-
2679
- ;; TODO: needs fixing, table will include other things than keywords - David
2680
-
2681
- (defn emit-constants-table [table]
2682
- (emitln "goog.provide('" (munge ana/constants-ns-sym) "');")
2683
- (emitln "goog.require('cljs.core');")
2684
- (doseq [[sym value] table]
2685
- (let [ns (namespace sym)
2686
- name (name sym)]
2687
- (emits "cljs.core." value " = ")
2688
- (cond
2689
- (keyword? sym) (emits-keyword sym)
2690
- (symbol? sym) (emits-symbol sym)
2691
- :else (throw
2692
- (ex-info
2693
- (str "Cannot emit constant for type " (type sym))
2694
- {:error :invalid-constant-type
2695
- :clojure.error/phase :compilation})))
2696
- (emits ";\n"))))
2697
-
2698
- #?(:clj
2699
- (defn emit-esm-constants-table
2700
- "The constants module of ES module output (:emit-constants): an export
2701
- of each keyword and symbol constant in table."
2702
- [table]
2703
- (binding [*esm-emitting* true
2704
- ana/*cljs-ns* ana/constants-ns-sym
2705
- ;; the namespaces cc references, cljs.core is imported below
2706
- *esm* {:refs (atom #{}) :self-import (atom false)}]
2707
- (emitln "import * as " (esm-ns-alias 'cljs.core) " from \""
2708
- (esm-ns-path ana/constants-ns-sym 'cljs.core) "\";")
2709
- (doseq [[x value] (sort-by (comp str val) table)]
2710
- (emits "export const " value " = /*@__PURE__*/ ")
2711
- (if (keyword? x) (emits-keyword x) (emits-symbol x))
2712
- (emitln ";")))))
2713
-
2714
- #?(:clj
2715
- (defn emit-constants-table-to-file [table dest]
2716
- (io/make-parents dest)
2717
- (with-open [out ^java.io.Writer (io/make-writer dest {})]
2718
- (binding [*out* out]
2719
- (emit-constants-table table)))))
2720
-
2721
- (defn emit-externs
2722
- ([externs]
2723
- (emit-externs [] externs (atom #{})
2724
- (when env/*compiler* (ana/get-externs))))
2725
- ([prefix externs top-level known-externs]
2726
- (loop [ks (seq (keys externs))]
2727
- (when ks
2728
- (let [k (first ks)
2729
- [top :as prefix'] (conj prefix k)]
2730
- (when (and (not= 'prototype k)
2731
- (nil? (get-in known-externs prefix')))
2732
- (if-not (or (contains? @top-level top)
2733
- (contains? known-externs top))
2734
- (do
2735
- (emitln "var " (string/join "." (map munge prefix')) ";")
2736
- (swap! top-level conj top))
2737
- (emitln (string/join "." (map munge prefix')) ";")))
2738
- (let [m (get externs k)]
2739
- (when-not (empty? m)
2740
- (emit-externs prefix' m top-level known-externs))))
2741
- (recur (next ks))))))
2742
-
2743
- #?(:clj
2744
- (defn emit-inferred-externs-to-file [externs dest]
2745
- (io/make-parents dest)
2746
- (with-open [out ^java.io.Writer (io/make-writer dest {})]
2747
- (binding [*out* out]
2748
- (emit-externs externs)))))