Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
The initial commit, gutted out from Fusion.
  • Loading branch information
rschmukler committed May 2, 2021
0 parents commit 01ba656
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.cpcache
50 changes: 50 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Shadow-Cljs Tailwind JIT

Build hooks for enabling [Tailwind
JIT](https://tailwindcss.com/docs/just-in-time-mode) within Shadow Projects.

## Installation and configuration

Install the required node dependencies in your project:

```
npm install --save-dev postcss-cli tailwindcss autoprefixer cssnano
```

Add the clojure library to your project via your preferred method (either
shadow's own `deps` or in your `deps.edn` file).

Next, add the required build hooks to your `shadow-cljs.edn` build configuration:


```clj
{:builds
{:ui
{;; ...
:dev
{:build-hooks
[(teknql.tailwind/start-watch!)]}}
:release
{:build-hooks
[(teknql.tailwind/compile-release!)]}
:devtools
{:http-root "resources/public/" ;; Must be set to infer default purge targets.
:http-port 3000}
:tailwind/output "resources/public/css/site.css"}}}
```

## Customization

The following options are supported via namespaced keys within the `shadow-cljs` build config:


- `:tailwind/output` - Where the generated CSS will be written to. Default:
`resources/public/css/site.css`
- `:tailwind/config` - A map that is used for the `tailwind.config.js`. Automatically encodes kebab
cased keys into camel cased.

## How it works

This library works by sourcing options in your `shadow-cljs.edn` to make a temporary project
project directory with the required files to configure PostCSS + Tailwind to compile CSS for
your project. We then shell out to `postcss-cli` using the temporary configs.
11 changes: 11 additions & 0 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{:paths ["src"]
:pablo/config
{:github "teknql/shadow-cljs-tailwind-jit"
:desc "Tailwind JIT buildhooks for Shadow CLJS"
:target :jar
:group-id teknql
:artifact-id shadow-cljs-tailwind-jit}
:deps
{metosin/jsonista {:mvn/version "0.3.2"}
funcool/cuerdas {:mvn/version "2020.03.26-3"}
babashka/process {:mvn/version "0.0.2"}}}
112 changes: 112 additions & 0 deletions src/teknql/tailwind.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
(ns teknql.tailwind
(:require [jsonista.core :as j]
[cuerdas.core :as str]
[babashka.process :as proc])
(:import [java.nio.file Files]
[java.nio.file.attribute FileAttribute]))

(def default-config
"Default tailwind config"
{:future {}
:purge []
:mode "jit"
:theme {:extend {}}
:variants {}
:plugins []})

(defn- ->json
"Encode the provided value to JSON"
[val]
(j/write-value-as-string
val
(j/object-mapper {:encode-key-fn (comp str/camel name)})))

(defn ->export-json
"Return the provided val as an string with a `module.exports`.
Used for generating the various *.config.js files that the Node ecosystem loves"
[val]
(str "module.exports = " (->json val) ";"))

(defn- cfg-get
"Behaves identical to `get` but logs the default value back to the user."
[config key default]
(or (get config key)
(do (println "No build config value for " key ". Using default value.")
default)))

(defn create-tmp-tailwind-project!
"Create a temporary tailwind project with the necessary assets to build the project using the JIT.
Return the path to the temporary directory."
[postcss-cfg tailwind-cfg]
(let [tmp-dir (-> (Files/createTempDirectory "tailwind" (make-array FileAttribute 0))
(.toFile)
(.getAbsolutePath))
tmp-css-path (str tmp-dir "/" "tailwind.css")
tmp-tw-cfg-path (str tmp-dir "/" "tailwind.config.js")
tmp-postcss-cfg-path (str tmp-dir "/" "postcss.config.js")]
(spit tmp-css-path "@tailwind base;\n@tailwind components;\n@tailwind utilities;")
(spit tmp-tw-cfg-path (->export-json tailwind-cfg))
(spit tmp-postcss-cfg-path (-> postcss-cfg
(assoc-in [:plugins :tailwindcss :config] tmp-tw-cfg-path)
(->export-json)))
tmp-dir))

(defn start-watch!
"Start the tailwind JIT"
{:shadow.build/stage :configure}
[build-state]
(let [config (:shadow.build/config build-state)
output-path (cfg-get config :tailwind/output "resources/public/css/site.css")
http-root (-> config
:devtools
:http-root)
tmp-dir (create-tmp-tailwind-project!
{:plugins {:tailwindcss {}}}
(merge default-config
{:purge [(str http-root "/**/*.js")
(str http-root "/**/*.html")]}
(cfg-get config :tailwind/config nil)))]
(proc/process
["./node_modules/.bin/postcss"
(str tmp-dir "/tailwind.css")
"--config"
tmp-dir
"--watch"
"-o"
output-path]
{:env {"NODE_ENV" "development"
"TAILWIND_MODE" "watch"}
:err :inherit
:out :inheirt})
build-state))

(defn compile-release!
"Compile the release build of the CSS generated by tailwind."
{:shadow.build/stage :flush}
[build-state]
(let [config (:shadow.build/config build-state)
output-path (cfg-get config :tailwind/output "resources/public/css/site.css")
http-root (-> config
:devtools
:http-root)
tmp-dir (create-tmp-tailwind-project!
{:plugins {:tailwindcss {}
:autoprefixer {}
:cssnano {:preset "default"}}}
(merge default-config
{:purge [(str http-root "/**/*.js")
(str http-root "/**/*.html")]}
(cfg-get config :tailwind/config nil)))]
(-> (proc/process
["./node_modules/.bin/postcss"
(str tmp-dir "/tailwind.css")
"--config"
tmp-dir
"-o"
output-path]
{:env {"NODE_ENV" "production"
"TAILWIND_MODE" "build"}})
deref)
build-state))

0 comments on commit 01ba656

Please sign in to comment.