Skip to content

Commit 67bca29

Browse files
committed
report: initial draft
1 parent c5e8c76 commit 67bca29

File tree

3 files changed

+20
-302
lines changed

3 files changed

+20
-302
lines changed

src/eden/core.clj

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
[clojure.java.io :as io]
55
[clojure.java.process :as process]
66
[hawkeye.core :as hawk]
7-
[eden.report :as report]
7+
[eden.init :as init]
88
[eden.mcp :as mcp]
9-
[eden.loader :as loader]
109
[eden.pipeline :refer [|>] :as pipeline]
11-
[eden.init :as init])
10+
[eden.report :as report])
1211
(:import [java.io File]))
1312

1413
(defn- find-available-port
@@ -80,10 +79,9 @@
8079
pipeline/copy-static-step
8180
pipeline/write-output-step
8281
)]
83-
;; TODO: reports
84-
;; (report/print-build-report result)
85-
;; (report/generate-html-report result)
86-
(println "\nBuild complete!")))
82+
(report/print-build-report result)
83+
(when (:error result)
84+
(System/exit 1))))
8785

8886
(defn- start-watch
8987
"Start watching for file changes and rebuild on change.

src/eden/report.clj

Lines changed: 13 additions & 293 deletions
Original file line numberDiff line numberDiff line change
@@ -1,299 +1,19 @@
1-
(ns eden.report
2-
(:require [replicant.string :as rs]
3-
[clojure.string :as str]
4-
[eden.loader :as loader]))
1+
(ns eden.report)
52

6-
(defn- format-file-size
7-
"Format bytes into human-readable size"
8-
[bytes]
9-
(cond
10-
(< bytes 1024) (format "%db" bytes)
11-
(< bytes (* 1024 1024)) (format "%.1fkb" (/ bytes 1024.0))
12-
:else (format "%.1fmb" (/ bytes (* 1024.0 1024.0)))))
3+
(defmulti print-warning :type)
134

14-
(defn generate-html-report
15-
"Generate an HTML build report for dev mode"
16-
[{:keys [timings warnings results error site-edn mode]}]
17-
(let [total-time (reduce + 0 (vals timings))
18-
html-count (get-in results [:write-output :html-count] 0)
19-
timestamp (java.time.LocalDateTime/now)
20-
status (if error "failed" "success")
21-
max-time (apply max 1 (vals timings))]
22-
(str "<!DOCTYPE html>"
23-
(rs/render
24-
[:html
25-
[:head
26-
[:title "Build Report"]
27-
[:meta {:charset "UTF-8"}]
28-
[:style "body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
29-
max-width: 1200px; margin: 0 auto; padding: 20px; background: #f5f5f5; }
30-
.header { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px;
31-
box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
32-
.status { display: inline-block; padding: 4px 12px; border-radius: 4px;
33-
font-weight: 600; font-size: 14px; }
34-
.status.success { background: #d4edda; color: #155724; }
35-
.status.failed { background: #f8d7da; color: #721c24; }
36-
.info { color: #666; margin: 5px 0; font-size: 14px; }
37-
.section { background: white; padding: 20px; border-radius: 8px;
38-
margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
39-
h2 { margin-top: 0; color: #333; border-bottom: 2px solid #e9ecef; padding-bottom: 10px; }
40-
.timing-table { width: 100%; border-collapse: collapse; }
41-
.timing-table th { text-align: left; padding: 8px; background: #f8f9fa;
42-
border-bottom: 2px solid #dee2e6; }
43-
.timing-table td { padding: 8px; border-bottom: 1px solid #dee2e6; }
44-
.timing-bar { background: #007bff; height: 20px; border-radius: 3px;
45-
min-width: 2px; display: inline-block; }
46-
.warning { background: #fff3cd; border-left: 4px solid #ffc107;
47-
padding: 12px; margin: 10px 0; border-radius: 4px; }
48-
.warning-title { font-weight: 600; color: #856404; margin-bottom: 8px; }
49-
.warning-list { margin: 5px 0 5px 20px; color: #856404; }
50-
.error { background: #f8d7da; border-left: 4px solid #dc3545;
51-
padding: 12px; margin: 10px 0; border-radius: 4px; color: #721c24; }
52-
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
53-
gap: 15px; margin-top: 20px; }
54-
.stat-card { background: #f8f9fa; padding: 15px; border-radius: 6px; }
55-
.stat-value { font-size: 24px; font-weight: 600; color: #333; }
56-
.stat-label { color: #666; font-size: 14px; margin-top: 5px; }"]]
57-
[:body
58-
[:div.header
59-
[:h1 "Build Report"]
60-
[:span.status {:class status} (str/upper-case status)]
61-
[:div.info "Generated: " (str timestamp)]
62-
[:div.info "Site: " site-edn]
63-
[:div.info "Mode: " (name mode)]]
5+
(defmethod print-warning :missing-key [{:keys [directive key template]}]
6+
(println (format " Missing key: %s for directive %s in template %s" key directive template)))
647

65-
(when error
66-
[:div.section
67-
[:h2 "❌ Build Error"]
68-
[:div.error error]])
8+
(defmethod print-warning :default [{:keys [type] :as warning}]
9+
(println "Unknown warning:" type)
10+
(prn warning))
6911

70-
[:div.section
71-
[:h2 "⏱️ Build Performance"]
72-
[:table.timing-table
73-
[:thead
74-
[:tr
75-
[:th "Step"]
76-
[:th "Time (ms)"]
77-
[:th {:style "width: 50%"} "Timeline"]]]
78-
[:tbody
79-
(for [[step elapsed] (sort-by val > timings)]
80-
[:tr
81-
[:td (name step)]
82-
[:td elapsed]
83-
[:td
84-
[:div.timing-bar
85-
{:style (str "width: " (* 100 (/ elapsed max-time)) "%")}]]])
86-
[:tr {:style "font-weight: 600; background: #f8f9fa;"}
87-
[:td "Total"]
88-
[:td total-time]
89-
[:td]]]]]
12+
(defn print-build-report [ctx]
13+
(when-let [warnings (seq (into #{} (:warnings ctx)))]
14+
(println "\n⚠️ Warnings")
15+
(doseq [warning warnings]
16+
(print-warning warning)))
9017

91-
[:div.section
92-
[:h2 "📊 Build Statistics"]
93-
[:div.stats
94-
[:div.stat-card
95-
[:div.stat-value html-count]
96-
[:div.stat-label "HTML Files Generated"]]
97-
[:div.stat-card
98-
[:div.stat-value (count timings)]
99-
[:div.stat-label "Build Steps"]]
100-
[:div.stat-card
101-
[:div.stat-value (+ (count (:missing-keys warnings []))
102-
(count (:missing-pages warnings [])))]
103-
[:div.stat-label "Total Warnings"]]
104-
(when-let [copied (get-in results [:copy-static :copied])]
105-
[:div.stat-card
106-
[:div.stat-value copied]
107-
[:div.stat-label "Static Files Copied"]])]]
108-
109-
(when (or (seq (:missing-keys warnings))
110-
(seq (:missing-pages warnings)))
111-
[:div.section
112-
[:h2 "⚠️ Warnings"]
113-
114-
(when (seq (:missing-keys warnings))
115-
[:div.warning
116-
[:div.warning-title "Missing Template Keys"]
117-
(let [grouped (group-by :template (:missing-keys warnings))]
118-
(for [[template keys] grouped]
119-
[:div
120-
[:strong "In " template ":"]
121-
[:ul.warning-list
122-
(for [{:keys [key page-id]} keys]
123-
[:li key " (rendering " page-id ")"])]]))])
124-
125-
(when (seq (:missing-pages warnings))
126-
[:div.warning
127-
[:div.warning-title "Missing Page References"]
128-
[:ul.warning-list
129-
(for [{:keys [page-id template]} (:missing-pages warnings)]
130-
[:li "Page ID: " [:code page-id]
131-
(when template (str " (referenced in " template ")"))])]])])
132-
133-
[:div {:style "text-align: center; color: #999; padding: 20px; font-size: 12px;"}
134-
"Generated by Anteo Website Builder"]]]))))
135-
136-
(defn print-build-report
137-
"Print a formatted build report with timings and warnings"
138-
[{:keys [timings warnings results error] :as ctx}]
139-
(println " Build steps:")
140-
141-
;; Print each step timing
142-
(doseq [[step elapsed] timings]
143-
(println (format " %-20s %dms" (name step) elapsed)))
144-
145-
;; Print totals
146-
(println (format " Total time: %dms" (reduce + (vals timings))))
147-
(when-let [html-count (get-in results [:write-output :html-count])]
148-
(println (format " Generated %d HTML files" html-count)))
149-
150-
;; Print bundle info if available
151-
(when-let [bundle-info (get-in results [:bundle-assets])]
152-
(let [css-files (get-in bundle-info [:css :files])
153-
js-files (get-in bundle-info [:js :files])
154-
all-files (concat css-files js-files)]
155-
(when (seq all-files)
156-
(println "\n Bundled assets:")
157-
(doseq [{:keys [file size type]} all-files]
158-
(println (format " %s/%s %s"
159-
(name type)
160-
file
161-
(format-file-size size)))))))
162-
163-
;; Group warnings by type
164-
(let [page-warnings (:page-warnings warnings)
165-
warnings-by-type (group-by :type page-warnings)
166-
unconfigured-langs (:unconfigured-language warnings-by-type)
167-
;; Get all warnings that are NOT unconfigured-language
168-
other-warnings (mapcat val (dissoc warnings-by-type :unconfigured-language))
169-
;; Group unconfigured language warnings by language
170-
langs-by-lang (group-by :lang unconfigured-langs)]
171-
172-
;; Print unconfigured language warnings (grouped)
173-
(when (seq langs-by-lang)
174-
(println "\n⚠️ Language configuration issues:")
175-
(doseq [[lang lang-warnings] langs-by-lang]
176-
(let [first-warning (first lang-warnings)
177-
page-count (count lang-warnings)]
178-
;; Use the helpful message from the first warning
179-
(if-let [message (:message first-warning)]
180-
(println (format " - %s" message))
181-
(println (format " - Language '%s' not configured (%d pages affected)"
182-
(name lang) page-count)))
183-
;; Don't list individual pages when there are many
184-
(when (<= page-count 5)
185-
(doseq [w lang-warnings]
186-
(println (format " Page: %s" (:content-key w))))))))
187-
188-
;; Print other page-level warnings
189-
(when (seq other-warnings)
190-
(println "\n⚠️ Page warnings:")
191-
(doseq [w other-warnings]
192-
;; Prefer the :message field if available
193-
(if-let [message (:message w)]
194-
(println (format " - %s" message))
195-
;; Fall back to type-specific formatting
196-
(case (:type w)
197-
:missing-template
198-
(println (format " - Template '%s' not found for page '%s'"
199-
(:template-name w) (:content-key w)))
200-
201-
:defaulted-template
202-
(println (format " - Page '%s' has no :template field, defaulting to '%s'"
203-
(:content-key w) (:defaulted-to w)))
204-
205-
:missing-content
206-
(println (format " - No content for %s in %s"
207-
(:content-key w) (:lang-code w)))
208-
209-
:missing-key
210-
(println (format " - Missing key '%s' in template %s (page: %s)"
211-
(:key w) (:template w) (:page w)))
212-
213-
:ambiguous-link
214-
(println (format " - Ambiguous link '%s' exists as both page and section"
215-
(:link-id w)))
216-
217-
:missing-page
218-
(let [stack-str (when (seq (:render-stack w))
219-
(->> (:render-stack w)
220-
(map (fn [[type id]]
221-
(str (name id)
222-
(when (not= type :content)
223-
(str " (" (name type) ")")))))
224-
(str/join "")))]
225-
(if stack-str
226-
(println (format " - Missing page '%s' in: %s" (:content-key w) stack-str))
227-
(println (format " - Missing page '%s'" (:content-key w)))))
228-
229-
:invalid-render-spec
230-
(let [stack-str (when (seq (:render-stack w))
231-
(->> (:render-stack w)
232-
(map (fn [[type id]]
233-
(str (name id)
234-
(when (not= type :content)
235-
(str " (" (name type) ")")))))
236-
(str/join "")))]
237-
(println (format " - Invalid :eden/render spec with data=%s, template=%s%s"
238-
(pr-str (:data-key w))
239-
(pr-str (:template-id w))
240-
(if stack-str (str " in: " stack-str) ""))))
241-
242-
:missing-translation
243-
nil ; Will be handled separately below
244-
245-
;; Default
246-
(println (format " - %s: %s" (:type w) (pr-str w))))))))
247-
248-
;; Print missing translations grouped by language
249-
(let [missing-translations (filter #(= :missing-translation (:type %))
250-
(:page-warnings warnings))]
251-
(when (seq missing-translations)
252-
(let [by-lang (group-by :lang missing-translations)
253-
config (get-in ctx [:results :load :config])
254-
root-path (:root-path config)]
255-
(println "\n⚠️ Missing translations:")
256-
(doseq [[lang translations] by-lang]
257-
(println (format " Language: %s" (name lang)))
258-
(doseq [t translations]
259-
(println (format " - %s" (:key t))))
260-
;; Show where we looked for translations
261-
(when root-path
262-
(let [strings-path (loader/translation-file-path root-path lang)]
263-
(println (format " Expected in: %s" strings-path))))))))
264-
265-
;; Print warnings (non-page warnings)
266-
(when (seq (:missing-keys warnings))
267-
(println "\n⚠️ Missing template keys:")
268-
(let [grouped (group-by :template (:missing-keys warnings))]
269-
(doseq [[template keys] grouped]
270-
(println (format " In %s:" template))
271-
(doseq [{:keys [key content-key]} keys]
272-
(println (format " - %s (rendering %s)" key content-key))))))
273-
274-
(when (seq (:missing-pages warnings))
275-
(println "\n⚠️ Missing page references:")
276-
(doseq [{:keys [page-id render-stack]} (:missing-pages warnings)]
277-
(let [stack-str (when (seq render-stack)
278-
(->> render-stack
279-
(map (fn [[type id]]
280-
(str (name id)
281-
(when (not= type :content)
282-
(str " (" (name type) ")")))))
283-
(str/join "")))]
284-
(println) ;; Add blank line before each warning for better readability
285-
(if stack-str
286-
(println (format " - Page ID: %s\n Found in: %s" page-id stack-str))
287-
(println (format " - Page ID: %s" page-id))))))
288-
289-
;; Print orphan content warning
290-
(when-let [orphan-content (:orphan-content warnings)]
291-
(when (seq orphan-content)
292-
(println "\n📝 Found content files not linked from your site:")
293-
(doseq [content-key (sort orphan-content)]
294-
(println (format " - content/%s.edn (or .md)" (name content-key))))
295-
(println " To include them, add links from existing pages or add to :render-roots")))
296-
297-
;; Print error if any
298-
(when error
18+
(when-let [error (:error ctx)]
29919
(println (format "\n❌ Build failed: %s" error))))

src/eden/site_generator3.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@
7272
(process default-value context)
7373
(do (warn! context
7474
{:type :missing-key
75+
:template (some-> context :data :template)
7576
:key processed-key
76-
;; TODO: rendering stack
77-
})
77+
:directive :eden/get})
7878
[:span.missing-content (str "[:eden/get " processed-key "]")]))
7979

8080
(= processed-key :html/content)

0 commit comments

Comments
 (0)