-
Notifications
You must be signed in to change notification settings - Fork 2
Implement linters.one-resource-per-ns
#112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| (ns formatting-stack.linters.one-resource-per-ns | ||
| "This linter ensures that there's exactly once classpath resource per namespace and extension. | ||
|
|
||
| Note that generally it's fine to define identically-named Clojure/Script namespaces with _different_ extensions, | ||
| so that is allowed." | ||
| (:require | ||
| [clojure.spec.alpha :as spec] | ||
| [clojure.string :as string] | ||
| [clojure.tools.namespace.file :as file] | ||
| [formatting-stack.protocols.linter :as linter] | ||
| [formatting-stack.util :refer [process-in-parallel!]] | ||
| [formatting-stack.util.ns :as util.ns] | ||
| [nedap.speced.def :as speced] | ||
| [nedap.utils.modular.api :refer [implement]] | ||
| [nedap.utils.spec.predicates :refer [present-string?]])) | ||
|
|
||
| (spec/def ::resource-path (spec/and present-string? | ||
| (complement #{\. \! \? \-}) | ||
| (fn [x] | ||
| (re-find #"\.clj([cs])?$" x)))) | ||
|
|
||
| (speced/defn ^::resource-path ns-decl->resource-path [^::util.ns/ns-form ns-decl, extension] | ||
| (-> ns-decl | ||
| second | ||
| str | ||
| munge | ||
| (string/replace "." "/") | ||
| (str extension))) | ||
|
|
||
| (speced/defn resource-path->filenames [^::resource-path resource-path] | ||
| (->> (-> (Thread/currentThread) | ||
| (.getContextClassLoader) | ||
| (.getResources resource-path)) | ||
| (enumeration-seq) | ||
| (distinct) ;; just in case | ||
| (mapv str))) | ||
|
|
||
| (speced/defn analyze [^present-string? filename] | ||
| (for [extension [".clj" ".cljs" ".cljc"] | ||
| :let [decl (-> filename file/read-file-ns-decl) | ||
| resource-path (ns-decl->resource-path decl extension) | ||
| filenames (resource-path->filenames resource-path)] | ||
| :when (-> filenames count (> 1))] | ||
| {:extension extension | ||
| :ns-name (-> decl second) | ||
| :filenames filenames})) | ||
|
|
||
| (defn lint! [this filenames] | ||
| (->> filenames | ||
| (process-in-parallel! (fn [filename] | ||
| (->> filename | ||
| analyze | ||
| (run! (speced/fn [{:keys [^symbol? ns-name, ^coll? filenames]}] | ||
| (println "Warning: the namespace" | ||
| (str "`" ns-name "`") | ||
| "is defined over more than one file.\nFound:" | ||
| (->> filenames (interpose ", ") (apply str)))))))))) | ||
|
|
||
| (speced/defn new [^map? opts] | ||
| (implement opts | ||
| linter/--lint! lint!)) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| (ns orpn | ||
| "This namespace aides testing `formatting-stack.linters.one-resource-per-ns`") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| (ns orpn | ||
| "This namespace aides testing `formatting-stack.linters.one-resource-per-ns`") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| (ns orpn | ||
| "This namespace aides testing `formatting-stack.linters.one-resource-per-ns`") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| (ns orpn | ||
| "This namespace aides testing `formatting-stack.linters.one-resource-per-ns`") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| (ns orpn | ||
| "This namespace aides testing `formatting-stack.linters.one-resource-per-ns`") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| (ns orpn | ||
| "This namespace aides testing `formatting-stack.linters.one-resource-per-ns`") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| (ns formatting-stack.test-helpers | ||
| (:require | ||
| [nedap.speced.def :as speced]) | ||
| (:import | ||
| (java.io File))) | ||
|
|
||
| (speced/defn filename-as-resource [^string? filename] | ||
| (str "file:" (-> filename | ||
| File. | ||
| .getAbsolutePath))) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| (ns integration.formatting-stack.linters.one-resource-per-ns | ||
| (:require | ||
| [clojure.test :refer :all] | ||
| [formatting-stack.linters.one-resource-per-ns :as sut] | ||
| [formatting-stack.test-helpers :as test-helpers] | ||
| [formatting-stack.util :refer [rcomp]])) | ||
|
|
||
| (defn first! [coll] | ||
| {:pre [(->> coll count #{1})]} | ||
| (->> coll first)) | ||
|
|
||
| (deftest analyze | ||
|
|
||
| (testing "This namespace is unambiguous" | ||
| (is (= (sut/analyze "test/integration/formatting_stack/linters/one_resource_per_ns.clj") | ||
| ()))) | ||
|
|
||
|
Comment on lines
+15
to
+17
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dunno. Asserting values is more specific than asserting computations (not good to author tests that pass on many inputs - seems sloppy); and also seeing the actual output of given function seems pretty useful. |
||
| (testing "Sample files are ambiguous (on a per-extension basis)" | ||
| (are [input extension expected] (testing input | ||
| (is (= (into #{} | ||
| (map test-helpers/filename-as-resource) | ||
| expected) | ||
| (->> input | ||
| sut/analyze | ||
| (filter (rcomp :extension #{extension})) | ||
| (first!) | ||
| :filenames | ||
| set))) | ||
| true) | ||
| "test-resources/orpn.clj" ".clj" #{"test-resources-extra/orpn.clj" | ||
| "test-resources/orpn.clj"} | ||
| "test-resources-extra/orpn.clj" ".clj" #{"test-resources-extra/orpn.clj" | ||
| "test-resources/orpn.clj"} | ||
|
|
||
| "test-resources/orpn.cljs" ".cljs" #{"test-resources-extra/orpn.cljs" | ||
| "test-resources/orpn.cljs"} | ||
| "test-resources-extra/orpn.cljs" ".cljs" #{"test-resources-extra/orpn.cljs" | ||
| "test-resources/orpn.cljs"} | ||
|
|
||
| "test-resources/orpn.cljc" ".cljc" #{"test-resources-extra/orpn.cljc" | ||
| "test-resources/orpn.cljc"} | ||
| "test-resources-extra/orpn.cljc" ".cljc" #{"test-resources-extra/orpn.cljc" | ||
| "test-resources/orpn.cljc"}))) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| (ns unit.formatting-stack.linters.one-resource-per-ns | ||
| (:require | ||
| [clojure.test :refer :all] | ||
| [formatting-stack.linters.one-resource-per-ns :as sut] | ||
| [formatting-stack.test-helpers :as test-helpers])) | ||
|
|
||
| (deftest ns-decl->resource-path | ||
| (are [input expected] (testing input | ||
| (is (= expected | ||
| (sut/ns-decl->resource-path input ".clj"))) | ||
| true) | ||
| '(ns unit) "unit.clj" | ||
| '(ns unit.formatting-stack.linters.one-resource-per-ns) "unit/formatting_stack/linters/one_resource_per_ns.clj" | ||
| '(ns foo!?) "foo_BANG__QMARK_.clj")) | ||
|
|
||
| (deftest resource-path->filenames | ||
| (are [input] (testing input | ||
| (is (= (-> "test/" (str input) test-helpers/filename-as-resource vector) | ||
| (sut/resource-path->filenames input))) | ||
| true) | ||
| "unit/formatting_stack/linters/one_resource_per_ns.clj" | ||
| "unit/formatting_stack/linters/ns_aliases.clj")) |
Uh oh!
There was an error while loading. Please reload this page.