Skip to content

Commit 3e3e0ad

Browse files
committed
redo asset handling
1 parent c1585d4 commit 3e3e0ad

File tree

5 files changed

+230
-298
lines changed

5 files changed

+230
-298
lines changed

src/eden/assets.clj

Lines changed: 140 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,79 @@
33
[clojure.java.io :as io]
44
[clojure.java.process :as process]
55
[clojure.data.json :as json]
6-
[eden.site-generator :as sg]
7-
[eden.image-processor :as img])
6+
[eden.image-processor :as img]
7+
[eden.esbuild :as esbuild])
88
(:import [java.lang ProcessBuilder ProcessBuilder$Redirect]
99
[java.io File]))
1010

11+
(defn- extract-image-urls
12+
"Extract image URLs with query parameters from HTML or CSS."
13+
[content]
14+
(let [url-pattern #"(?:src=[\"']?|url\([\"']?)(/assets/images/[^\"')\s]+\?[^\"')\s]+)"
15+
parse-params (fn [query-string]
16+
(let [params (java.net.URLDecoder/decode ^String query-string "UTF-8")
17+
pairs (str/split params #"&")]
18+
(reduce (fn [m pair]
19+
(let [[k v] (str/split pair #"=" 2)]
20+
(case k
21+
"size" (if-let [[_ w h] (re-matches #"^(\d+)x(.*)$" v)]
22+
(cond
23+
(not (str/blank? h))
24+
(if (re-matches #"\d+" h)
25+
(assoc m :width (Long/parseLong w)
26+
:height (Long/parseLong h))
27+
(assoc m :error (str "Invalid height: " h)))
28+
:else
29+
(assoc m :width (Long/parseLong w)))
30+
(assoc m :error (str "Invalid size format: " v)))
31+
m)))
32+
{}
33+
pairs)))
34+
35+
generate-replace-url (fn [path params]
36+
(let [[base-path ext] (let [last-dot (.lastIndexOf ^String path ".")]
37+
[(subs path 0 last-dot)
38+
(subs path (inc last-dot))])
39+
{:keys [width height]} params
40+
size-suffix (cond
41+
(and width height) (str "-" width "x" height)
42+
width (str "-" width "x")
43+
:else "")]
44+
(str base-path size-suffix "." ext)))]
45+
46+
(into []
47+
(map (fn [[_ url]]
48+
(let [[path query-string] (str/split url #"\?" 2)
49+
params (parse-params query-string)]
50+
(cond-> {:url url
51+
:source-path path}
52+
(not (:error params)) (merge (select-keys params [:width :height])
53+
{:replace-url (generate-replace-url path params)})
54+
(:error params) (assoc :error (:error params))))))
55+
(re-seq url-pattern content))))
56+
57+
1158
(defn process-images
1259
"Process images in HTML and CSS files based on query parameters."
13-
[html-files css-files root-path]
14-
(let [;; Extract image URLs from all HTML and CSS
15-
html-image-urls (mapcat #(sg/extract-image-urls (:html %)) html-files)
16-
css-image-urls (mapcat #(sg/extract-image-urls (slurp %)) css-files)
17-
all-image-urls (concat html-image-urls css-image-urls)
18-
19-
;; Ensure .temp/images directory exists
20-
temp-dir (io/file ".temp/images")
21-
_ (io/make-parents (io/file temp-dir "dummy.txt"))
22-
23-
;; Process each unique image
24-
_ (doall
25-
(for [img-data all-image-urls]
26-
(when-not (:error img-data)
27-
(let [;; Convert web path to file system path
28-
source-path (str root-path (:source-path img-data))
29-
output-dir ".temp/images"]
30-
;; Call image processor with consistent keys
31-
(img/process-image (merge {:source-path source-path
32-
:output-dir output-dir}
33-
(select-keys img-data [:width :height])))))))
60+
[{:keys [site-config rendered css] :as _ctx}]
61+
(let [root-path (:root-path site-config)
62+
output-path (:output-path site-config)
63+
64+
;; Extract image URLs from all HTML and CSS
65+
html-image-urls (into #{} (mapcat #(extract-image-urls (:html/output %)) rendered))
66+
css-image-urls (into #{} (mapcat #(extract-image-urls (:content %)) css))
67+
all-image-urls (concat css-image-urls html-image-urls)
68+
69+
image-results (mapv
70+
(fn [img-data]
71+
(if (:error img-data)
72+
img-data
73+
(let [opts (-> (select-keys img-data [:height :width])
74+
(assoc :source-path (str root-path (:source-path img-data))
75+
:output-path (str output-path (:source-path img-data))))]
76+
(merge opts (img/process-image opts)))))
77+
all-image-urls)
78+
3479

3580
;; Build URL replacement map
3681
url-replacements (reduce (fn [m img-data]
@@ -40,107 +85,61 @@
4085
{}
4186
all-image-urls)]
4287

43-
;; Replace URLs in HTML files
44-
(map (fn [html-file]
45-
(let [updated-html (reduce (fn [html [old-url new-url]]
46-
(str/replace html old-url new-url))
47-
(:html html-file)
48-
url-replacements)]
49-
(assoc html-file :html updated-html)))
50-
html-files)))
51-
52-
(defn- ensure-npm-setup
53-
"Ensure npm is set up in the site directory with esbuild"
54-
[site-dir]
55-
(let [package-json (io/file site-dir "package.json")
56-
node-modules (io/file site-dir "node_modules")]
57-
;; Only install dependencies if package.json exists
58-
(when (and (.exists package-json)
59-
(not (.exists node-modules)))
60-
(println " Installing npm dependencies...")
61-
(process/exec {:dir site-dir} "npm" "install")
62-
(println " npm dependencies installed"))))
63-
64-
(defn- bundle-css
65-
"Bundle CSS with esbuild, or copy files if esbuild not available"
66-
[site-root output-dir mode]
67-
(let [css-dir (io/file site-root "assets" "css")
68-
css-files (when (.exists css-dir)
69-
(seq (.listFiles css-dir (reify java.io.FilenameFilter
70-
(accept [_ _dir name]
71-
(.endsWith name ".css"))))))]
72-
(when css-files
73-
(let [css-start (System/currentTimeMillis)
74-
out-dir (io/file output-dir "assets" "css")
75-
_ (io/make-parents (io/file out-dir "dummy"))
76-
;; esbuild path is relative to site-root
77-
esbuild-path (io/file site-root "node_modules" ".bin" "esbuild")
78-
bundled-files (if (.exists esbuild-path)
79-
;; Use esbuild if available
80-
(into []
81-
(mapcat (fn [css-file]
82-
(let [css-path (File/.getPath css-file)
83-
out-file (io/file out-dir (File/.getName css-file))
84-
args (into-array String
85-
(cond-> [(File/.getPath esbuild-path) css-path "--bundle"
86-
(str "--outfile=" (File/.getPath out-file))
87-
"--metafile=/dev/stdout"
88-
"--external:/assets/*"
89-
"--loader:.css=css"
90-
"--log-level=error"]
91-
(= mode :prod) (conj "--minify")))]
92-
(try
93-
;; Use ProcessBuilder to capture stdout separately
94-
(let [pb (new ProcessBuilder ^"[Ljava.lang.String;" args)
95-
_ (.redirectError pb ProcessBuilder$Redirect/DISCARD)
96-
p (.start pb)
97-
output (slurp (.getInputStream p))
98-
exit-code (.waitFor p)]
99-
(if (zero? exit-code)
100-
;; Parse JSON from stdout
101-
(if (and output (not (str/blank? output)))
102-
(let [meta-data (json/read-str output :key-fn keyword)
103-
outputs (:outputs meta-data)]
104-
(map (fn [[out-path out-info]]
105-
{:file (File/.getName (io/file (str out-path)))
106-
:size (:bytes out-info)
107-
:type :css})
108-
outputs))
109-
;; Fallback if no metadata
110-
[{:file (File/.getName css-file)
111-
:size (.length out-file)
112-
:type :css}])
113-
(do
114-
(println (format " CSS %s failed with exit code %d"
115-
(File/.getName css-file) exit-code))
116-
[])))
117-
(catch Exception e
118-
(println (format " CSS %s failed: %s" (File/.getName css-file) (.getMessage e)))
119-
[]))))
120-
css-files))
121-
;; Otherwise just copy the files
122-
(mapv (fn [css-file]
123-
(let [out-file (io/file out-dir (File/.getName css-file))]
124-
(io/copy css-file out-file)
125-
{:file (File/.getName css-file)
126-
:size (.length out-file)
127-
:type :css}))
128-
css-files))]
129-
{:elapsed (- (System/currentTimeMillis) css-start)
130-
:files bundled-files}))))
88+
{:image-results image-results
89+
90+
;; Replace urls in HTML
91+
:rendered
92+
(mapv (fn [html-file]
93+
(let [updated-html (reduce (fn [html [old-url new-url]]
94+
(str/replace html old-url new-url))
95+
(:html/output html-file)
96+
url-replacements)]
97+
(assoc html-file :html/replaced updated-html)))
98+
rendered)
99+
100+
;; Replace urls in css
101+
:css
102+
(mapv (fn [css-file]
103+
(let [updated-css (reduce (fn [css [old-url new-url]]
104+
(str/replace css old-url new-url))
105+
(:content css-file)
106+
url-replacements)]
107+
(assoc css-file :replaced updated-css)))
108+
css)}))
109+
110+
(defn- copy-css
111+
"For now just copy css files over"
112+
[ctx]
113+
(when-let [css (seq (:css ctx))]
114+
(let [copy-start (System/currentTimeMillis)
115+
output-dir (io/file (:output-path (:site-config ctx)))
116+
files (mapv (fn [{:keys [content relative-path]}]
117+
(let [output-file (io/file output-dir relative-path)]
118+
(io/make-parents output-file)
119+
(io/copy content output-file)
120+
{:file (File/.getName output-file)
121+
:path (str output-file)
122+
:size (File/.length output-file)
123+
:type :css}))
124+
css)]
125+
{:elapsed (- (System/currentTimeMillis) copy-start)
126+
:files files})))
131127

132128
(defn- bundle-js
133129
"Bundle JavaScript with esbuild, or copy files if esbuild not available"
134-
[site-root output-dir mode]
135-
(let [js-dir (io/file site-root "assets" "js")
136-
js-files (when (.exists js-dir)
137-
(seq (.listFiles js-dir (reify java.io.FilenameFilter
138-
(accept [_ _dir name]
139-
(.endsWith name ".js"))))))]
130+
[ctx]
131+
(let [site-root (-> ctx :site-config :root-path)
132+
assets-path (or (-> ctx :site-config :assets) "assets")
133+
js-dir (io/file site-root assets-path "js")
134+
js-files (when (and (File/.exists js-dir)
135+
(File/.isDirectory js-dir))
136+
(filter #(str/ends-with? % ".js") (file-seq js-dir)))]
140137
(when js-files
141138
(let [js-start (System/currentTimeMillis)
142-
out-dir (io/file output-dir "assets" "js")
143-
_ (io/make-parents (io/file out-dir "dummy"))
139+
output-path (-> ctx :site-config :output-path)
140+
mode (:mode ctx)
141+
out-dir (io/file output-path assets-path "js")
142+
_ (io/make-parents (io/file out-dir "."))
144143
;; esbuild path is relative to site-root
145144
esbuild-path (io/file site-root "node_modules" ".bin" "esbuild")
146145
bundled-files (if (.exists esbuild-path)
@@ -149,17 +148,20 @@
149148
(mapcat (fn [js-file]
150149
(let [js-path (File/.getPath js-file)
151150
out-file (io/file out-dir (File/.getName js-file))
152-
args (into-array String
153-
(cond-> [(File/.getPath esbuild-path) js-path "--bundle"
154-
(str "--outfile=" (File/.getPath out-file))
155-
"--metafile=/dev/stdout"
156-
"--format=iife"
157-
"--log-level=error"]
158-
(= mode :dev) (conj "--sourcemap")
159-
(= mode :prod) (conj "--minify")))]
151+
args (into
152+
[(File/.getPath esbuild-path) js-path]
153+
(esbuild/args (cond-> {:bundle true
154+
:outfile (File/.getPath out-file)
155+
:metafile "/dev/stdout"
156+
:format "iife"
157+
:log-level "error"}
158+
(= mode :dev) (assoc :sourcemap true)
159+
(= mode :prod) (assoc :minify true))))]
160+
160161
(try
161162
;; Use ProcessBuilder to capture stdout separately
162-
(let [pb (new ProcessBuilder ^"[Ljava.lang.String;" args)
163+
;; TODO: clojure.java.process
164+
(let [pb (new ProcessBuilder ^"[Ljava.lang.String;" (into-array String args))
163165
_ (.redirectError pb ProcessBuilder$Redirect/DISCARD)
164166
p (.start pb)
165167
output (slurp (.getInputStream p))
@@ -170,9 +172,11 @@
170172
(let [meta-data (json/read-str output :key-fn keyword)
171173
outputs (:outputs meta-data)]
172174
(map (fn [[out-path out-info]]
173-
{:file (File/.getName (io/file (str out-path)))
174-
:size (:bytes out-info)
175-
:type :js})
175+
(let [output-file (io/file (name out-path))]
176+
{:file (File/.getName output-file)
177+
:size (:bytes out-info)
178+
:path (File/.getAbsolutePath output-file)
179+
:type :js}))
176180
outputs))
177181
;; Fallback if no metadata
178182
[{:file (File/.getName js-file)
@@ -199,20 +203,8 @@
199203

200204
(defn bundle-assets
201205
"Bundle all CSS and JS assets"
202-
[site-root output-dir mode]
203-
;; Only ensure npm setup if we're in a traditional Eden project structure
204-
;; (with site/ subdirectory), not in a generated site
205-
(let [site-subdir (io/file site-root "site")]
206-
(when (.exists site-subdir)
207-
(ensure-npm-setup site-root)))
208-
(println " Bundling assets:")
209-
(let [css-result (bundle-css site-root output-dir mode)
210-
js-result (bundle-js site-root output-dir mode)]
211-
;; Print timing info
212-
(when css-result
213-
(println (format " CSS: %dms" (:elapsed css-result))))
214-
(when js-result
215-
(println (format " JS: %dms" (:elapsed js-result))))
216-
;; Return bundle info for reporting
217-
{:css css-result
218-
:js js-result}))
206+
[ctx]
207+
{:assets-output {:css (copy-css ctx)
208+
:js (bundle-js ctx)}})
209+
210+

0 commit comments

Comments
 (0)