- 
      
- 
        Save Chouser/5796967 to your computer and use it in GitHub Desktop. 
| (ns n01se.externs-for-cljs | |
| (:require [clojure.java.io :as io] | |
| [cljs.compiler :as comp] | |
| [cljs.analyzer :as ana])) | |
| (defn read-file [file] | |
| (let [eof (Object.)] | |
| (with-open [stream (clojure.lang.LineNumberingPushbackReader. (io/reader file))] | |
| (vec (take-while #(not= % eof) | |
| (repeatedly #(read stream false eof))))))) | |
| (defn file-analysis [file] | |
| (binding [ana/*cljs-ns* 'cljs.user | |
| ana/*cljs-file* file] | |
| (mapv #(ana/analyze (ana/empty-env) %) (read-file file)))) | |
| (defn flat-file-analysis [file] | |
| (mapcat #(tree-seq :children :children %) | |
| (file-analysis file))) | |
| (defn get-vars-used [ffa] | |
| (->> ffa | |
| (filter #(and (= (:op %) :var) (-> % :info :ns))) | |
| (map #(-> % :info :name)) | |
| distinct)) | |
| (defn var-defined? [sym] | |
| (contains? (:defs (get @ana/namespaces (symbol (namespace sym)))) | |
| (symbol (name sym)))) | |
| (defn get-undefined-vars [ffa] | |
| (remove var-defined? (get-vars-used ffa))) | |
| (defn externs-for-var [sym] | |
| (if (= "js" (namespace sym)) | |
| (format "var %s={};\n" (name sym)) | |
| (format "var %s={};\n%s.%s=function(){};\n" | |
| (namespace sym) (namespace sym) (name sym)))) | |
| (defn get-interop-used [ffa] | |
| (->> ffa | |
| (filter #(and (= (:op %) :dot))) | |
| (map #(or (:method %) (:field %))) | |
| distinct)) | |
| (defn externs-for-interop [sym] | |
| (format "DummyExternClass.%s=function(){};\n" sym)) | |
| (defn externs-for-cljs [file] | |
| (swap! ana/namespaces empty) | |
| (ana/analyze-file "cljs/core.cljs") | |
| (let [ffa (flat-file-analysis file)] | |
| (apply str | |
| (concat | |
| (map externs-for-var (get-undefined-vars ffa)) | |
| ["var DummyExternClass={};\n"] | |
| (map externs-for-interop (get-interop-used ffa)))))) | |
| ;; (externs-for-cljs "/home/chouser/proj/cljs-example/src/main.cljs") | 
        
      
            cemerick
  
      
      
      commented 
        Jun 17, 2013 
      
    
  
- Does this really work? ;-)
- Why the hell doesn't gclosure just do this automatically‽
- It's sufficient for some input sources, but I've probably missed some cases.
- Maybe because in ClojureScript we can tell the difference between interop, vars, and locals more easily than in JavaScript? Not sure.
This is terrific - just saved me some serious headache. Much appreciated!
It's probably worthwhile to tidy this up and get it into the compiler.
This is a nice example of using the CLJS analyzer, too. Thanks @Chouser.
I'm a bit lost here; Without knowing ClojureScript (just got it working on my Windows machine) - how do I run this script to generate the externs? (Also: it is meant to create externals for plain .js files, right?)
What version of clojurescript was this written/work against? I've just run it on some code of my own and I'm getting:
ClassCastException cljs.analyzer$reify__4241 cannot be cast to clojure.lang.Atom  clojure.core/swap! (core.clj:2160)
I'm using 0.0-2202.
Also, for @dertseha there's a blog on using this at http://www.dotkam.com/2013/07/15/clojurescript-use-any-javascript-library/