{"id":13760488,"url":"https://github.com/clojure/tools.namespace","last_synced_at":"2025-05-14T15:05:25.984Z","repository":{"id":42519766,"uuid":"1650502","full_name":"clojure/tools.namespace","owner":"clojure","description":"Tools for managing namespaces in Clojure","archived":false,"fork":false,"pushed_at":"2024-07-15T17:53:46.000Z","size":600,"stargazers_count":609,"open_issues_count":1,"forks_count":62,"subscribers_count":47,"default_branch":"master","last_synced_at":"2025-04-10T04:53:19.528Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clojure.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2011-04-22T16:59:54.000Z","updated_at":"2025-03-11T08:17:07.000Z","dependencies_parsed_at":"2024-06-21T02:17:00.155Z","dependency_job_id":"ac113ddc-2e62-44af-afa3-0a294407bf89","html_url":"https://github.com/clojure/tools.namespace","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure%2Ftools.namespace","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure%2Ftools.namespace/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure%2Ftools.namespace/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure%2Ftools.namespace/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clojure","download_url":"https://codeload.github.com/clojure/tools.namespace/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254168753,"owners_count":22026206,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T13:01:11.346Z","updated_at":"2025-05-14T15:05:25.940Z","avatar_url":"https://github.com/clojure.png","language":"Clojure","funding_links":[],"categories":["Clojure","Libraries"],"sub_categories":[],"readme":"clojure.tools.namespace\n========================================\n\nTools for managing namespaces in Clojure. Parse `ns` declarations from\nsource files, extract their dependencies, build a graph of namespace\ndependencies within a project, update that graph as files change, and\nreload files in the correct order.\n\nThis is only about namespace dependencies **within** a single project.\nIt has nothing to do with Leiningen, Maven, JAR files, or\nrepositories.\n\n\n\nReleases and Dependency Information\n----------------------------------------\n\nThis project follows the version scheme MAJOR.MINOR.PATCH where each component provides some relative indication of the size of the change, but does not follow semantic versioning. In general, all changes endeavor to be non-breaking (by moving to new names rather than by breaking existing names).\n\n[Change Log](CHANGES.md)\n\n[All Released Versions](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22tools.namespace%22)\n\n### Stable Release ###\n\nLatest stable release is [1.5.0](https://github.com/clojure/tools.namespace/tree/v1.5.0)\n\n[CLI/`deps.edn`](https://clojure.org/reference/deps_edn) dependency information:\n```clojure\norg.clojure/tools.namespace {:mvn/version \"1.5.0\"}\n```\n\n[Leiningen](https://leiningen.org/) stable dependency information:\n\n    [org.clojure/tools.namespace \"1.5.0\"]\n\n[Maven](https://maven.apache.org/) stable dependency information:\n\n    \u003cdependency\u003e\n      \u003cgroupId\u003eorg.clojure\u003c/groupId\u003e\n      \u003cartifactId\u003etools.namespace\u003c/artifactId\u003e\n      \u003cversion\u003e1.5.0\u003c/version\u003e\n    \u003c/dependency\u003e\n\n### Development Snapshots ###\n\nGit master branch is at **1.5.1-SNAPSHOT**\n\n[All Snapshot Versions](https://oss.sonatype.org/content/groups/public/org/clojure/tools.namespace/)\n\nLeiningen dependency information for development snapshots:\n\n    :dependencies [[org.clojure/tools.namespace \"1.5.1-SNAPSHOT\"]]\n    :repositories [[\"sonatype-oss-public\"\n                    \"https://oss.sonatype.org/content/groups/public/\"]]\n\nSee also [Maven Settings and Repositories](https://clojure.org/releases/downloads#_using_clojure_snapshot_releases) on dev.clojure.org.\n\n\n\nOverview\n----------------------------------------\n\n[API Documentation](https://clojure.github.io/tools.namespace/)\n\ntools.namespace consists of several parts:\n\n**clojure.tools.namespace.parse:** A parser for namespace declarations\nin Clojure source files. Given a stream of characters from a Clojure\nsource file, it can find the `ns` declaration and parse the `:require`\nand `:use` clauses to find the names of other namespaces that file\ndepends on. This is all syntactic analysis: it does not \nevaluate any code.\n\n**clojure.tools.namespace.find:** Utilities to search for Clojure\nnamespaces on the filesystem, in directories or JAR files. Combined\nwith [java.classpath](https://clojure.github.io/java.classpath/), it\ncan search for namespaces on the Java classpath. This namespace\ncontains most of the functions in clojure.tools.namespace version\n0.1.x.\n\n**clojure.tools.namespace.repl:** Utilities to load and reload code\nbased on the namespace dependency graph. This takes some explaining,\nsee below. c.t.n.repl is built out of smaller parts:\n\n* c.t.n.dependency - generic dependency graph data structure\n* c.t.n.track - namespace dependency tracker\n* c.t.n.file - file-reader extension to tracker\n* c.t.n.dir - directory-scanner extension to tracker\n* c.t.n.reload - namespace-reloading extension to tracker\n\nYou can recombine these parts in other ways, but c.t.n.repl is the\nprimary public entry-point to their functionality.\n\n**clojure.tools.namespace.move:** Utilities to aid in moving and\nrenaming Clojure namespaces. This code is still ALPHA, and it modifies\nyour source files, so be careful.\n\n\n\nClojureScript support\n-----------------------\n\n**New in version 0.3.0-alpha1**\n\nThese namespaces are `.cljc` files usable from both Clojure(JVM) and\nClojureScript:\n\n* c.t.n.dependency\n* c.t.n.track\n* c.t.n.parse\n\nThese namespaces are usable on Clojure(JVM) only but can analyze both\nClojure(JVM) and ClojureScript source files:\n\n* c.t.n.file\n* c.t.n.dir\n* c.t.n.find\n\nMost functions now take an optional \"platform\" argument, which is one\nof the constant values defined in c.t.n.find: `clj` or `cljs`. The\ndefault is `clj`.\n\nThese namespaces are still Clojure(JVM) only: \n\n* c.t.n.reload\n* c.t.n.repl\n* c.t.n.move.\n\n\n\nReloading Code: Motivation\n----------------------------\n\nc.t.n.repl is a smarter way to reload code. \n\nThe traditional way to reload Clojure code without restarting the JVM\nis `(require ... :reload)` or `:reload-all` or an editor/IDE feature\nthat does the same thing. This has several problems:\n\n* If you modify two namespaces which depend on each other, you must\n  remember to reload them in the correct order to avoid compilation\n  errors.\n\n* If you remove definitions from a source file and then reload it,\n  those definitions are still available in memory. If other code\n  depends on those definitions, it will continue to work but will\n  break the next time you restart the JVM.\n\n* If the reloaded namespace contains `defmulti`, you must also reload\n  all of the associated `defmethod` expressions.\n\n* If the reloaded namespace contains `defprotocol`, you must also\n  reload any records or types implementing that protocol and replace\n  any existing instances of those records/types with new instances.\n\n* If the reloaded namespace contains macros, you must also reload any\n  namespaces which use those macros.\n\n* If the running program contains functions which close over values in\n  the reloaded namespace, those closed-over values are not updated.\n  (This is common in web applications which construct the \"handler\n  stack\" as a composition of functions.)\n\nOften the only surefire way to reload Clojure code is to restart the\nJVM. A large Clojure application can take 20 seconds or more just to\ncompile. I wrote tools.namespace to help speed up this development\ncycle.\n\nFor more detail on how I use tools.namespace in my development\nworkflow, see the article [My Clojure Workflow, Reloaded](https://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded).\n\n\n\nReloading Code: Usage\n-----------------------\n\nThere's only one important function, `refresh`:\n\n    user=\u003e (require '[clojure.tools.namespace.repl :refer [refresh]])\n    nil\n\n    user=\u003e (refresh)\n    :reloading (com.example.util com.example.app com.example.app-test)\n    :ok\n\nThe `refresh` function will scan all the directories on the classpath\nfor Clojure source files, read their `ns` declarations, build a graph\nof their dependencies, and load them in dependency order. (You can\nchange the directories it scans with `set-refresh-dirs`.)\n\nLater on, after you have changed and saved a few files in your editor,\nrun it again:\n\n    user=\u003e (refresh)\n    :reloading (com.example.app com.example.app-test)\n    :ok\n\nBased on file modification timestamps and the graph of dependencies,\nthe `refresh` function will reload *only* the namespaces that have\nchanged, in dependency order. But first, it will *unload* (remove) the\nnamespaces that changed to clear out any old definitions.\n\nThis is quite unlike `(require ... :reload)`. Calling `refresh` will\n*blow away your old code*. Sometimes this is helpful: it can catch\ntrivial mistakes like deleting a function that another piece of code\ndepends on. But sometimes it hurts when you have built-up application\nstate stored in a Var that got deleted by `refresh`.\n\nThis brings us to the next section:\n\n\n\nReloading Code: Preparing Your Application\n--------------------------------------------\n\nBeing able to safely destroy and reload namespaces without breaking\nyour application requires some discipline and careful design. It won't\n\"just work\" on any Clojure project.\n\n\n### No Global State\n\nThe first rule for making your application reload-safe is **no global\nstate**. That means you should avoid things like this:\n\n    (def state-of-world (ref {}))\n    (def object-handle (atom nil))\n\nc.t.n.repl/refresh will destroy those Vars when it reloads the\nnamespace (even if you used `defonce`).\n\nInstead of storing your state in global Vars, store it *locally* in an\nobject that represents the running state of your application. Then\nprovide a constructor function to initialize that state:\n\n    (defn create-application []\n      {:state-of-world (ref {})\n       :object-handle (atom nil)})\n\nYou can choose what representation works best for your application:\nmap, vector, record, or even just a single Ref by itself.\n\nTypically you'll still need one global `def` somewhere, perhaps in the\nREPL itself, to hold the current application instance. See the next\nsection.\n\n\n### Managed Lifecycle\n\nThe second rule for making your application reload-safe is to have a\nconsistent way to **start and stop the entire system**.\n\nThe \"start\" function should:\n\n- Acquire stateful resources such as sockets, files, and database\n  connections\n\n- Start threads or background processes\n\n- Initialize application state such as caches or counters\n\n- Return an object encapsulating the state of the application\n\nThe \"stop\" function should take the state returned by \"start\" as an\nargument and do the opposite:\n\n- Close or release stateful resources\n\n- Stop all background processes\n\n- Clear out application state\n\nIt might take a few tries to get it right, but once you have working\nstart and stop functions you can have a workflow like this:\n\nStep 1. Start up a REPL.\n\nStep 2. Load the app:\n\n    user=\u003e (require '[clojure.tools.namespace.repl :refer [refresh]])\n    user=\u003e (refresh)\n    user=\u003e (def my-app (start-my-app))\n\nStep 3. Test it out.\n\nStep 4. Modify some source files.\n\nStep 5. Restart:\n\n    user=\u003e (stop-my-app my-app)\n    user=\u003e (refresh)\n    user=\u003e (def my-app (start-my-app))\n\n(You could also combine all those steps in a single utility function,\nbut see warnings below.)\n\nAfter that, you've got a squeaky-clean new instance of your app\nrunning, in a fraction of the time it takes to restart the JVM.\n\n\n### Handling Errors\n\nIf an exception is thrown while loading a namespace, `refresh` stops,\nprints the namespace that caused the exception, and returns the\nexception. You can print the rest of the stacktrace with\n`clojure.repl/pst`; the exception itself is bound to `*e`.\n\n    user=\u003e (refresh)\n    :reloading (com.example.app com.example.app-test)\n    :error-while-loading com.example.app\n    #\u003cIllegalArgumentException java.lang.IllegalArgumentException:\n      Parameter declaration cond should be a vector\u003e\n\n    user=\u003e (clojure.repl/pst)\n    IllegalArgumentException Parameter declaration cond should be a vector\n            clojure.core/assert-valid-fdecl (core.clj:6567)\n            clojure.core/sigs (core.clj:220)\n            clojure.core/defn (core.clj:294)\n            clojure.lang.Var.invoke (Var.java:427)\n            ...\n\nRemember that any namespaces which depend on the namespace that caused\nthe exception **do not exist** at this point: they have been removed\nbut not yet reloaded.\n\nAfter you fix the problem, call `refresh` again and it will resume\nreloading where it left off. \n\n**NOTE:** If your current REPL namespace is one of those that has not\nyet been reloaded, then none of the functions you defined in that\nnamespace will exist! Starting with version 0.2.8, tools.namespace\nwill *attempt* to restore aliases to the namespaces which were\nsuccessfully loaded.\n\nSo, for example, if your current REPL namespace is named `dev` and\ncontains this ns declaration:\n\n    (ns dev\n      (:require [com.example.foo :as foo]\n                [com.example.bar :as bar]\n                [clojure.tools.namespace.repl :as tns]))\n\nAnd you get an error on refresh like this:\n\n    dev=\u003e (tns/refresh)\n    :reloading (com.example.bar dev)\n    :error-while-loading com.example.bar\n    #\u003cCompilerException ... compiling:(com/example/bar.clj:1:21)\u003e\n\nThen the functions in `com.example.foo` should still be available in\nthe `dev` namespace via the alias `foo`.\n\n\n\nWarnings and Potential Problems\n-------------------------------\n\n**`ns` syntax:** Clojure's `ns` macro is notoriously lax in what\nsyntax it accepts. tools.namespace.parse is somewhat liberal, but it\ncannot handle every possible variation of syntax that `ns` does. Stick\nto the docstrings of [ns] and [require] and everything should be fine.\n\n**AOT-compilation:** Reloading code does not work in the presence of\n[AOT-compiled] namespaces. If you are using AOT-compilation in your\nproject, make sure it is disabled and you have deleted any\nAOT-compiled `.class` files before starting a REPL development\nsession. (In Leiningen, run `lein clean`.)\n\nNote that the presence of `:main` in project.clj triggers\nAOT-compilation in some versions of Leiningen.\n\n**Conflicts:** Other libraries which also do code-reloading may\nconflict with tools.namespace. One known example is ring-devel (as of\n[Ring] version 1.1.6) which uses [ns-tracker], which uses an older\nversion of tools.namespace.\n\n**REPL namespace:** Be careful when reloading the namespace in which\nyou run your REPL. Because namespaces are removed when reloading, all\nyour past definitions are lost. Either keep your REPL in a namespace\nwhich has no file associated with it, such as `user`, or put all your\nREPL definitions in a file so that they can be reloaded.\n\n**Fully-qualified names:** Be careful when using fully-qualified\nsymbol names without namespace aliases (`require` with no `:as`). If\nthe namespace happens to be loaded already, it will not necessarily\ncause an error if you forget to `require` it, but the dependency graph\nof namespaces will be incorrect.\n\n**Old definitions:** Beware of code which has references to old\ndefinitions, especially references to things you created in the REPL.\n\n**Rolling your own:** If you create your own instance of the\ndependency tracker, do not store it in a namespace which gets\nreloaded.\n\n[AOT-compiled]: https://clojure.org/reference/compilation\n[Ring]: https://github.com/ring-clojure/ring\n[ns-tracker]: https://github.com/weavejester/ns-tracker\n[ns]: https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/ns\n[require]: https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/require\n\n\n### Warnings for Helper Functions\n\nBe careful defining a helper function in a namespace which calls\n`refresh` if that namespace also could get reloaded. For example, you\nmight try to combine the stop-refresh-start code from the \"Managed\nLifecycle\" section into a single function:\n\n    (def my-app nil)\n\n    (defn restart []\n      (stop-my-app my-app)\n      (refresh)\n      (alter-var-root #'my-app (constantly (start-my-app))))\n\nThis won't work if the namespace containing `restart` could get\nreloaded. After `refresh`, the namespace containing `restart` has been\ndropped, but the function continues to run in the *old* namespace and\nrefer to old Vars.\n\nIf you want to run some code after `refresh`, you can pass an option\nnaming a function you want to run *after* a successful reload. The\nvalue of this option must be a symbol, and it must be fully\nnamespace-qualified. The previous example could be correctly written\n(assuming these functions are defined in the `dev` namespace):\n\n    (def my-app nil)\n\n    (defn start []\n      (alter-var-root #'my-app (constantly (start-my-app))))\n\n    (defn restart []\n      (stop-my-app my-app)\n      (refresh :after 'dev/start))\n\n\n### Warnings for Aliases\n\nNamespace aliases created at the REPL will still refer to the *old* namespace  after `refresh`. For example:\n\n    user=\u003e (require '[com.example.foo :as foo])\n    \n    user=\u003e foo/bar\n\n    user=\u003e (refresh)\n    :reloading (com.example.foo)\n    :ok\n\n    user=\u003e foo/bar   ; this is the *old* foo/bar\n\nIf you try to recreate the alias with the new namespace, you will get an error:\n\n    user=\u003e (require '[com.example.foo :as foo])\n    IllegalStateException Alias foo already exists in\n    namespace user, aliasing com.example.foo\n    clojure.lang.Namespace.addAlias (Namespace.java:224)\n\nThe only way out is to remove the alias before recreating it:\n\n    user=\u003e (ns-unalias *ns* 'foo)\n    nil\n    user=\u003e (alias 'foo 'com.example.foo)\n\n\n### Warnings for Protocols\n\nWhen reloading namespaces which contain protocols, be careful that you\ndo not leave any old instances of records or types implementing those\nprotocols.\n\nFor example, if you have a namespace like this:\n\n    (ns com.example.foo)\n    \n    (defprotocol IFoo\n      (foo [this]))\n    \n    (defrecord FooRecord []\n      IFoo (foo [this] nil))\n\nAnd you do something like the following at the REPL:\n\n    user=\u003e (def my-foo (-\u003eFooRecord))\n    user=\u003e (clojure.tools.namespace.repl/refresh)\n    user=\u003e (foo my-foo)\n\nYou will get a confusing error message like this:\n\n    IllegalArgumentException\n    No implementation of method: :foo\n    of protocol: #'com.example.foo/IFoo\n    found for class: com.example.foo.FooRecord\n    clojure.core/-cache-protocol-fn (core_deftype.clj:527)\n\nThat's because `my-foo` is an **instance** of the **old** version of\n`FooRecord`, implementing the **old** version of `IFoo`. As far as the\nJVM is concerned, the old `IFoo` and the new `IFoo` are completely\ndifferent classes.\n\nTo avoid this problem, always create new instances of records after a\nrefresh.\n\n\n### Warnings for Multimethods\n\nCalling `prefer-method` is a global side-effect. If you modify a call\nto `prefer-method` and reload the namespace containing it, Clojure may\nthrow \"java.lang.IllegalStateException: Preference conflict in multimethod.\"\nThe workaround is to call `remove-method` before reloading.\ntools.namespace cannot detect this situation automatically. See [TNS-23].\n\n\n### Heap Usage and PermGen (JDK 1.7 and before)\n\nIn rare cases, reloading a lot of code may lead to out-of-memory\nerrors from the JVM like `java.lang.OutOfMemoryError: PermGen space`.\n\nYou may be able to mitigate this by increasing the size of the\n\"Permanent Generation\" where the JVM stores compiled classes. To do\nthis, add the following command-line argument to your JVM startup:\n\n    -XX:MaxPermSize=\u003cN\u003e\n\nwhere `\u003cN\u003e` is a number with a suffix like `m` for megabytes.\n\nTo find the default MaxPermSize for your JDK, run\n`java -XX:+PrintFlagsFinal` and search the results for \"MaxPermSize\".\nTry doubling it.\n\nThe Permanent Generation was removed in JDK 1.8 ([JEP 122]) so this\nsection no longer applies.\n\nIn some older JDKs (1.5) the default garbage collector did not collect\nthe Permanent Generation at all unless it was explicitly enabled with\n`-XX:+CMSPermGenSweepingEnabled`.\n\n\n\nDisabling Refresh In a Namespace\n--------------------------------\n\nSome projects have a \"project REPL\" or a \"scratch\" namespace where you\nwant keep state during development. You can use the functions\n`disable-unload!` and `disable-reload!` in\n`clojure.tools.namespace.repl` to prevent `refresh` from automatically\nun/reloading those namespaces.\n\nUse this feature sparingly: it exists as a development-time\nconvenience, not a work-around for code that is not reload-safe. Also,\nsee the [warnings about aliases](#warnings-for-aliases), below. Aliases to reloaded namespaces\nwill break if the namespace *containing* the alias is not reloaded\nalso.\n\nAfter an error, `refresh` will **not** attempt to recover symbol\nmappings and aliases for namespaces with `disable-unload!` or\n`disable-reload!` set.\n\n\n\nDeveloper Information\n----------------------------------------\n\n* [Change Log](CHANGES.md)\n* [GitHub project](https://github.com/clojure/tools.namespace)\n* [How to contribute](https://clojure.org/community/contributing)\n* [Bug Tracker](https://clojure.atlassian.net/browse/TNS)\n* [Continuous Integration](https://github.com/clojure/tools.namespace/actions/workflows/test.yml)\n\n\n\nCopyright and License\n----------------------------------------\n\nCopyright © Rich Hickey, Alessandra Sierra, and contributors\n\nAll rights reserved. The use and\ndistribution terms for this software are covered by the\n[Eclipse Public License 1.0] which can be found in the file\nepl-v10.html at the root of this distribution. By using this software\nin any fashion, you are agreeing to be bound by the terms of this\nlicense. You must not remove this notice, or any other, from this\nsoftware.\n\n[Eclipse Public License 1.0]: https://opensource.org/license/epl-1-0/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclojure%2Ftools.namespace","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclojure%2Ftools.namespace","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclojure%2Ftools.namespace/lists"}