Skip to content

Instantly share code, notes, and snippets.

@chase-lambert
Created January 16, 2025 01:56
Show Gist options
  • Save chase-lambert/f97bd336ddeedbabb979a42ba4463847 to your computer and use it in GitHub Desktop.
Save chase-lambert/f97bd336ddeedbabb979a42ba4463847 to your computer and use it in GitHub Desktop.
Concatenates all your git repo's clojure files into one file
#!/usr/bin/env bb
;; A Babashka script to concatenate all Clojure files in a repository into a single file.
;; Respects .gitignore patterns and includes clear file separation markers.
;;
;; Usage:
;; ./onefile.clj ; uses current git repo and default output
;; ./onefile.clj /path/to/repo ; uses custom repo path
;; ./onefile.clj /path/to/repo out.clj ; specifies output filename
;;
;; The output file will be created in the current directory.
(ns onefile
(:require [babashka.fs :as fs]
[clojure.string :as str]
[babashka.process :refer [shell]]))
(defn gitignore-pattern->regex [pattern]
(-> pattern
(str/replace #"\." "\\.")
(str/replace #"\*" ".*")
(str/replace #"\?" ".")
(str/replace #"\[" "\\[")
(str/replace #"\]" "\\]")
(str/replace #"\(" "\\(")
(str/replace #"\)" "\\)")
(str/replace #"\+" "\\+")
(str/replace #"\{" "\\{")
(str/replace #"\}" "\\}")
(str/replace #"\^" "\\^")
(str/replace #"\$" "\\$")))
(defn read-gitignore
"Read .gitignore patterns and convert them to regex patterns"
[path]
(when (fs/exists? (fs/path path ".gitignore"))
(->> (slurp (str path "/.gitignore"))
(str/split-lines)
(remove str/blank?)
(remove #(str/starts-with? % "#"))
(map gitignore-pattern->regex))))
(defn matches-gitignore?
"Check if a path matches any gitignore patterns"
[patterns path]
(when (seq patterns)
(let [path-str (str path)]
(some #(re-find (re-pattern %) path-str) patterns))))
(defn clojure-file? [path]
(let [ext (fs/extension path)]
(contains? #{"clj" "cljc" "cljs"} ext)))
(defn get-clojure-files
"Get all Clojure files in directory respecting .gitignore"
[dir]
(let [gitignore-patterns (read-gitignore dir)]
(->> (fs/glob dir "**")
(remove #(matches-gitignore? gitignore-patterns %))
(filter fs/regular-file?)
(filter clojure-file?)
(sort))))
(defn format-file-content
"Format file content with header comment"
[file content]
(format "\n;; =====================================\n;; File: %s\n;; =====================================\n\n%s"
(str file)
content))
(defn concatenate-files [dir output-file]
(let [files (get-clojure-files dir)
content (->> files
(map (fn [file]
(format-file-content file (slurp (str file)))))
(str/join "\n"))]
(spit output-file content)
(println "Created" output-file "with" (count files) "files concatenated.")))
(defn get-git-root []
(str/trim (:out (shell {:out :string} "git rev-parse --show-toplevel"))))
(defn -main [& args]
(let [dir (or (first args) (get-git-root))
output-file (or (second args) "concatenated_repo.clj")]
(concatenate-files dir output-file)))
(when (= *file* (System/getProperty "babashka.file"))
(apply -main *command-line-args*))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment