diff --git a/README.md b/README.md index 6a98b63..4a988d6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ +### Notes about this fork + +- Focused on extracting structured data (EDN) from jars. +- Any writing-related (HTML etc.) dependencies have been removed, code is kept to keep diff to mainline manageable. +- Various tweaks have been done to align the results of Clojure and ClojureScript analysis. + + +--- + # Codox A tool for generating API documentation from Clojure or ClojureScript diff --git a/codox/deps.edn b/codox/deps.edn new file mode 100644 index 0000000..7bc1466 --- /dev/null +++ b/codox/deps.edn @@ -0,0 +1,4 @@ +{:paths ["src" "resources" "test-sources"] + :deps {org.clojure/clojure {:mvn/version "1.9.0"} + org.clojure/tools.namespace {:mvn/version "0.2.11"} + org.clojure/clojurescript {:mvn/version "1.10.339"}}} diff --git a/codox/src/codox/main.clj b/codox/src/codox/main.clj index 1a11ab3..777da7f 100644 --- a/codox/src/codox/main.clj +++ b/codox/src/codox/main.clj @@ -2,8 +2,10 @@ "Main namespace for generating documentation" (:use [codox.utils :only (add-source-paths)]) (:require [clojure.string :as str] + [clojure.pprint] [clojure.java.shell :as shell] [codox.reader.clojure :as clj] + [codox.reader.clojurescript :as cljs] [codox.reader.plaintext :as text])) (defn- writer [{:keys [writer]}] @@ -19,29 +21,9 @@ (throw (Exception. (str "Could not resolve codox writer " writer-sym)))))) -(defn- macro? [var] - (= (:type var) :macro)) - -(defn- read-macro-namespaces [paths read-opts] - (->> (clj/read-namespaces paths read-opts) - (map (fn [ns] (update-in ns [:publics] #(filter macro? %)))) - (remove (comp empty? :publics)))) - -(defn- merge-namespaces [namespaces] - (for [[name namespaces] (group-by :name namespaces)] - (assoc (first namespaces) :publics (mapcat :publics namespaces)))) - -(defn- cljs-read-namespaces [paths read-opts] - ;; require is here to allow Clojure 1.3 and 1.4 when not using ClojureScript - (require 'codox.reader.clojurescript) - (let [reader (find-var 'codox.reader.clojurescript/read-namespaces)] - (merge-namespaces - (concat (reader paths read-opts) - (read-macro-namespaces paths read-opts))))) - (def ^:private namespace-readers {:clojure clj/read-namespaces - :clojurescript cljs-read-namespaces}) + :clojurescript cljs/read-namespaces}) (defn- var-symbol [namespace var] (symbol (name (:name namespace)) (name (:name var)))) @@ -125,3 +107,29 @@ (write-fn (assoc options :namespaces namespaces :documents documents))))) + +(defn -main + "The main entry point for reading API information from files in a directory. + + To analyze a project (debugging etc.) follow these steps: + + 1. unzip the project's jar into a directory + 2. add the project's coordinates to the local `deps.edn` file + 3. add the jar contents directory to `:paths` in `deps.edn` + + You can then call this main function as follows: + + clj -m codox.main clojurescript jar-contents-dir/ + clj -m codox.main clojure jar-contents-dir/" + [lang path] + (println "Analyzing lang:" lang) + (println "Analyzing path:" path) + (assert (#{"clojure" "clojurescript"} lang)) + (->> (generate-docs {:writer 'clojure.core/identity + :source-paths [path] + :language (keyword lang)}) + :namespaces + ;; Walk/realize entire structure, otherwise "Excluding ->Xyz" + ;; messages will be mixed with the pretty printed output + (clojure.walk/prewalk identity) + clojure.pprint/pprint)) diff --git a/codox/src/codox/reader/clojure.clj b/codox/src/codox/reader/clojure.clj index f37346c..119f2e8 100644 --- a/codox/src/codox/reader/clojure.clj +++ b/codox/src/codox/reader/clojure.clj @@ -116,8 +116,8 @@ (defn- find-namespaces [file] (cond - (.isDirectory file) (ns/find-namespaces-in-dir file) - (jar-file? file) (ns/find-namespaces-in-jarfile (JarFile. file)))) + (.isDirectory file) (set (ns/find-namespaces-in-dir file)) + (jar-file? file) (set (ns/find-namespaces-in-jarfile (JarFile. file))))) (defn read-namespaces "Read Clojure namespaces from a set of source directories (defaults diff --git a/codox/src/codox/reader/clojurescript.clj b/codox/src/codox/reader/clojurescript.clj index dee1892..1db1296 100644 --- a/codox/src/codox/reader/clojurescript.clj +++ b/codox/src/codox/reader/clojurescript.clj @@ -4,6 +4,8 @@ (:require [clojure.java.io :as io] [cljs.analyzer :as an] [cljs.analyzer.api :as ana] + [cljs.closure] + [cljs.env] [clojure.string :as str])) (defn- cljs-filename? [filename] @@ -15,7 +17,7 @@ (-> file .getName cljs-filename?))) (defn- remove-quote [x] - (if (and (list? x) (= (first x) 'quote)) + (if (and (seq? x) (= (first x) 'quote)) (second x) x)) @@ -50,15 +52,16 @@ :else :var)) (defn- read-var [file vars var] - (-> var - (select-keys [:name :line :arglists :doc :dynamic :added :deprecated :doc/format]) - (update-some :name (comp symbol name)) - (update-some :arglists remove-quote) - (update-some :doc correct-indent) - (assoc-some :file (.getPath file) - :type (var-type var) - :members (map (partial read-var file vars) - (protocol-methods var vars))))) + (let [vt (var-type var)] + (-> var + (select-keys [:name :line :arglists :doc :dynamic :added :deprecated :doc/format]) + (update-some :name (comp symbol name)) + (update-some :arglists remove-quote) + (update-some :doc correct-indent) + (assoc-some :file (if (= vt :macro) (:file var) (.getPath file)) + :type vt + :members (map (partial read-var file vars) + (protocol-methods var vars)))))) (defn- read-publics [state namespace file] (let [vars (vals (ana/ns-publics state namespace))] @@ -70,10 +73,11 @@ (sort-by (comp str/lower-case :name))))) (defn- analyze-file [file] - (let [state (ana/empty-state)] - (binding [an/*analyze-deps* false] - (ana/no-warn - (ana/analyze-file state file {}))) + (let [opts (cljs.closure/add-implicit-options {}) + state (cljs.env/default-compiler-env opts)] + (ana/no-warn + (cljs.closure/validate-opts opts) + (ana/analyze-file state file opts)) state)) (defn- read-file [path file exception-handler] diff --git a/codox/test-sources/codox_test/macro.cljc b/codox/test-sources/codox_test/macro.cljc new file mode 100644 index 0000000..0023108 --- /dev/null +++ b/codox/test-sources/codox_test/macro.cljc @@ -0,0 +1,18 @@ +(ns codox-test.macro) + +(defmacro test + [a b] + `(+ ~a ~b)) + +(defmacro test2 + [a & xs] + `(reduce + ~a ~(vec xs))) + +;; https://github.com/jarohen/nomad/blob/5520c332c5c6d5eef4dcb0930c735900d63dea2a/src/nomad/config.clj#L98-L111 + +(defn with-config-override* [{:keys [switches secret-keys override-switches] :as opts-override} f] + nil) + +(doto (defmacro with-config-override [opts & body] + `(with-config-override* ~opts (fn [] ~@body))) + (alter-meta! assoc :arglists '([{:keys [switches secret-keys override-switches] :as opts-override} & body]))) diff --git a/codox/test-sources/codox_test/multimethod.cljc b/codox/test-sources/codox_test/multimethod.cljc new file mode 100644 index 0000000..00b9d53 --- /dev/null +++ b/codox/test-sources/codox_test/multimethod.cljc @@ -0,0 +1,9 @@ +(ns codox-test.multimethod) + +(defmulti start (fn [k opts] k)) + +(defmethod start :car [_ opts] + (println "Starting car..." opts)) + +(defmethod start :helicopter [_ opts] + (println "Starting helicopter..." opts)) diff --git a/codox/test-sources/codox_test/record.cljc b/codox/test-sources/codox_test/record.cljc new file mode 100644 index 0000000..480302d --- /dev/null +++ b/codox/test-sources/codox_test/record.cljc @@ -0,0 +1,3 @@ +(ns codox-test.record) + +(defrecord CodoxSystem [components]) diff --git a/codox/test-sources/codox_test/spec.cljc b/codox/test-sources/codox_test/spec.cljc new file mode 100644 index 0000000..c228c6e --- /dev/null +++ b/codox/test-sources/codox_test/spec.cljc @@ -0,0 +1,4 @@ +(ns codox-test.spec + (:require [clojure.spec.test.alpha :as st])) + +(st/with-instrument-disabled (+ 1 2 3)) diff --git a/codox/test-sources/codox_test/spec_two.cljs b/codox/test-sources/codox_test/spec_two.cljs new file mode 100644 index 0000000..a985d42 --- /dev/null +++ b/codox/test-sources/codox_test/spec_two.cljs @@ -0,0 +1,4 @@ +(ns codox-test.spec-two + (:require [clojure.spec.test.alpha :as st])) + +(st/with-instrument-disabled (+ 1 2 3))