Skip to content

Commit 342923b

Browse files
author
dnolen
committed
CLJS-1895: Externs inference needs to support user supplied externs
1 parent 0e258d8 commit 342923b

6 files changed

Lines changed: 66 additions & 24 deletions

File tree

src/main/clojure/cljs/build/api.clj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@
190190
(build source opts
191191
(if-not (nil? env/*compiler*)
192192
env/*compiler*
193-
(env/default-compiler-env opts))))
193+
(env/default-compiler-env
194+
(closure/add-externs-sources opts)))))
194195
([source opts compiler-env]
195196
(doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) closure/known-opts)]
196197
(when suggested-opt
@@ -204,7 +205,8 @@
204205
(watch source opts
205206
(if-not (nil? env/*compiler*)
206207
env/*compiler*
207-
(env/default-compiler-env opts))))
208+
(env/default-compiler-env
209+
(closure/add-externs-sources opts)))))
208210
([source opts compiler-env]
209211
(watch source opts compiler-env nil))
210212
([source opts compiler-env stop]

src/main/clojure/cljs/closure.clj

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1992,13 +1992,19 @@
19921992
(defn load-data-readers! [compiler]
19931993
(swap! compiler update-in [:cljs.analyzer/data-readers] merge (get-data-readers)))
19941994

1995+
(defn add-externs-sources [opts]
1996+
(cond-> opts
1997+
(:infer-externs opts)
1998+
(assoc :externs-sources (load-externs (dissoc opts :infer-externs)))))
1999+
19952000
(defn build
19962001
"Given a source which can be compiled, produce runnable JavaScript."
19972002
([source opts]
19982003
(build source opts
19992004
(if-not (nil? env/*compiler*)
20002005
env/*compiler*
2001-
(env/default-compiler-env opts))))
2006+
(env/default-compiler-env
2007+
(add-externs-sources opts)))))
20022008
([source opts compiler-env]
20032009
(env/with-compiler-env compiler-env
20042010
(let [compiler-stats (:compiler-stats opts)

src/main/clojure/cljs/env.cljc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ state that is accessed/maintained by many different components."}
5252
:cljs.analyzer/constant-table {}
5353
:cljs.analyzer/data-readers {}
5454
:cljs.analyzer/externs #?(:clj (when (:infer-externs options)
55-
(externs/default-externs))
55+
(externs/externs-map (:externs-sources options)))
5656
:cljs nil)
5757
:options options}
5858
#?(:clj {:js-dependency-index (js-dependency-index options)})))))

src/main/clojure/cljs/externs.clj

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -127,39 +127,43 @@
127127
(seq xs) (update-in xs merge {})))
128128
{} externs))
129129

130-
(defn default-externs
130+
(defn externs-map
131131
([]
132-
(default-externs
133-
'{eval {}
134-
global {}
135-
goog {nodeGlobalRequire {}}
136-
COMPILED {}
137-
TypeError {}
138-
Error {prototype {number {} columnNumber {}}}
139-
ReferenceError {}}))
140-
([defaults]
141-
(let [xs (CommandLineRunner/getDefaultExterns)]
132+
(externs-map (CommandLineRunner/getDefaultExterns)))
133+
([sources]
134+
(externs-map sources
135+
'{eval {}
136+
global {}
137+
goog {nodeGlobalRequire {}}
138+
COMPILED {}
139+
TypeError {}
140+
Error {prototype {number {} columnNumber {}}}
141+
ReferenceError {}}))
142+
([sources defaults]
143+
(let [sources (if-not (empty? sources)
144+
sources
145+
(CommandLineRunner/getDefaultExterns))]
142146
(reduce
143147
(fn [externs externs-file]
144148
(util/map-merge
145149
externs (index-externs (parse-externs externs-file))))
146-
defaults xs))))
150+
defaults sources))))
147151

148152
(comment
149-
(default-externs)
153+
(externs-map)
150154

151-
(-> (default-externs)
155+
(-> (externs-map)
152156
(find 'console) first meta)
153157

154-
(get (default-externs) 'Function)
158+
(get (externs-map) 'Function)
155159

156-
(get (default-externs) 'Error)
160+
(get (externs-map) 'Error)
157161

158162
;; values are not on the prototype
159-
(get (default-externs) 'Symbol)
160-
(get (default-externs) 'Number)
163+
(get (externs-map) 'Symbol)
164+
(get (externs-map) 'Number)
161165

162-
(-> (get-in (default-externs) '[Window prototype])
166+
(-> (get-in (externs-map) '[Window prototype])
163167
(find 'performance) first meta)
164168

165169
;; webkit_dom.js defines Console and Window.prototype.console

src/test/clojure/cljs/analyzer_tests.clj

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,9 +621,10 @@
621621

622622
(comment
623623
(require '[cljs.compiler :as cc])
624+
(require '[cljs.closure :as closure])
624625

625626
;; TODO: need to handle the method/fn case
626-
(let [test-cenv (atom {::a/externs (externs/default-externs)})]
627+
(let [test-cenv (atom {::a/externs (externs/externs-map)})]
627628
(binding [a/*cljs-ns* a/*cljs-ns*
628629
a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
629630
(e/with-compiler-env test-cenv
@@ -641,4 +642,22 @@
641642
(map (comp :externs second)
642643
(get @test-cenv ::a/namespaces))))))
643644

645+
;; User supplied externs
646+
(let [test-cenv (atom {::a/externs (externs/externs-map
647+
(closure/load-externs
648+
{:externs ["src/test/externs/test.js"]
649+
:use-only-custom-externs true}))})]
650+
(binding [a/*cljs-ns* a/*cljs-ns*
651+
a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
652+
(e/with-compiler-env test-cenv
653+
(a/analyze-form-seq
654+
'[(ns foo.core)
655+
(defn bar [^js/Foo a]
656+
(.wozMethod a)
657+
(.gozMethod a))]))
658+
(cc/emit-externs
659+
(reduce util/map-merge {}
660+
(map (comp :externs second)
661+
(get @test-cenv ::a/namespaces))))))
662+
644663
)

src/test/externs/test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
@constructor
3+
*/
4+
var Foo = function() {};
5+
Foo.prototype.wozMethod = function() {
6+
};
7+
8+
/**
9+
@return {Foo}
10+
*/
11+
baz = function() {};

0 commit comments

Comments
 (0)