Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mhuebert/shadow-env
cljc environment variables with shadow-cljs
https://github.com/mhuebert/shadow-env
clojure shadow-cljs
Last synced: 3 months ago
JSON representation
cljc environment variables with shadow-cljs
- Host: GitHub
- URL: https://github.com/mhuebert/shadow-env
- Owner: mhuebert
- Created: 2020-02-24T13:37:29.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2020-02-25T17:19:37.000Z (almost 5 years ago)
- Last Synced: 2024-10-29T07:59:18.483Z (3 months ago)
- Topics: clojure, shadow-cljs
- Language: Clojure
- Homepage:
- Size: 5.86 KB
- Stars: 20
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# shadow-env
![Version Badge](https://img.shields.io/clojars/v/mhuebert/shadow-env)
Clojure(Script) environment injection with shadow-cljs live-reload support
----
I often set up applications to read config from EDN files using something like [juxt/aero](https://github.com/juxt/aero), and run into a couple minor issues:
- how to get my cljs app to pick up changes in these EDN files without recompiling from scratch
- how to cleanly expose environment to both Clojure and ClojureScript while avoiding secrets being exposed in the wrong placesThis small library provides:
- automatic propagation of changes made to environment files on each recompile
- explicit & simple control of what is exposed to ClojureScript vs Clojure## Usage
in a shadow-cljs.edn build:
```clj
:build-hooks [(shadow-env.core/hook)]
```in your app's env namespace:
```clj
(ns my-app.env
(:refer-clojure :exclude [get])
(:require [shadow-env.core :as env]));; write a Clojure function that returns variables to expose to :clj and :cljs.
;; the function must accept one variable, the shadow-cljs build-state
;; (which will be `nil` initially, before compile starts)
#?(:clj
(defn read-env [build-state]
{:common
:clj
:cljs });; define & link a new var to your reader function.
;; you must pass a fully qualified symbol here, so syntax-quote (`) is useful.
;; in this example I use `get` as the name, because we can call Clojure maps
;; as functions, I like to alias my env namespace as `env`, and (env/get :some-key)
;; is very readable.
(env/link get `read-env)
```usage elsewhere:
```clj
(ns my-app.client
(:require [my-app.env :as env]))(env/get :my/attribute)
```
## How it works
Every time your app recompiles, we
1) re-bind the `:clj` environment to the var you create using `env/link` (using `alter-var-root`), and
2) if the `:cljs` environment has changed, we invalidate the env's namespace so that it + its dependents will recompile.## Example usage with aero
I like to use [juxt/aero](https://github.com/juxt/aero) to read EDN files from disk.
I use a handful of different files to separate config that is server-only, client-only,
common, and secret (ie. not in source control).```clj
#?(:clj
(defn read-env [build-state]
(let [aero-config {:profile (or (System/getenv "ENV") :dev)}
[common
server
secret
client] (->> ["common.edn"
"server.edn"
".secret.server.edn"
"client.edn"]
(map #(some-> (io/resource %)
(aero/read-config aero-config))))]
{:common common
:clj (merge server secret)
:cljs client})))(env/link get `read-env)
```## Dead code elimination (DCE)
ClojureScript can remove unused code automatically based on compile-time constants.
For this to work, we can't read from our environment map at runtime - instead, we can
write a tiny macro that runs at compile-time and reads from our env:```clj
(env/link get `read-env)(defmacro get-static [k]
(clojure.core/get get k));; ClojureScript code can call (get-static :some/key) and the value will be replaced
;; at compile-time, enabling DCE
```# Dev
## Releases
To tag a version for release:
```
clj -A:release tag
```To deploy:
```
clj -A:release
```