From 616ed516ce2b50778dbd036aecd275620bc4e893 Mon Sep 17 00:00:00 2001 From: Esko Luontola Date: Sun, 27 Oct 2024 19:20:21 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Hiccup=20macro=20which=20u?= =?UTF-8?q?ses=20always=20the=20same=20output=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Why: - Hiccup generates 8 code paths every time the hiccup2.core/html macro is used, one for each combination of the output formats (html, xhtml, xml, sgml) and string escape mode (true, false). In practice, an application uses only one or two of them, and the rest is dead code. - This fixes the problem of exponential amount of generated code in some situations. See https://github.com/weavejester/hiccup/issues/210 --- src/territory_bro/ui/congregation_page.clj | 2 +- src/territory_bro/ui/documentation_page.clj | 2 +- src/territory_bro/ui/error_page.clj | 2 +- src/territory_bro/ui/hiccup.clj | 16 +++++++++++ src/territory_bro/ui/home_page.clj | 2 +- src/territory_bro/ui/html.clj | 7 ++--- src/territory_bro/ui/info_box.clj | 4 +-- src/territory_bro/ui/join_page.clj | 4 +-- src/territory_bro/ui/layout.clj | 4 +-- src/territory_bro/ui/map_interaction_help.clj | 2 +- src/territory_bro/ui/markdown.clj | 4 +-- src/territory_bro/ui/printout_templates.clj | 4 +-- src/territory_bro/ui/printouts_page.clj | 2 +- src/territory_bro/ui/privacy_policy_page.clj | 2 +- src/territory_bro/ui/registration_page.clj | 4 +-- src/territory_bro/ui/settings_page.clj | 2 +- src/territory_bro/ui/support_page.clj | 2 +- src/territory_bro/ui/territory_list_page.clj | 2 +- src/territory_bro/ui/territory_page.clj | 2 +- test/territory_bro/ui/hiccup_test.clj | 28 +++++++++++++++++++ test/territory_bro/ui/html_test.clj | 2 +- test/territory_bro/ui/layout_test.clj | 2 +- 22 files changed, 71 insertions(+), 30 deletions(-) create mode 100644 src/territory_bro/ui/hiccup.clj create mode 100644 test/territory_bro/ui/hiccup_test.clj diff --git a/src/territory_bro/ui/congregation_page.clj b/src/territory_bro/ui/congregation_page.clj index ab96146b..d08b0576 100644 --- a/src/territory_bro/ui/congregation_page.clj +++ b/src/territory_bro/ui/congregation_page.clj @@ -4,9 +4,9 @@ (ns territory-bro.ui.congregation-page (:require [clojure.string :as str] - [hiccup2.core :as h] [territory-bro.domain.dmz :as dmz] [territory-bro.ui.css :as css] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.info-box :as info-box] diff --git a/src/territory_bro/ui/documentation_page.clj b/src/territory_bro/ui/documentation_page.clj index 162644a1..5029dee4 100644 --- a/src/territory_bro/ui/documentation_page.clj +++ b/src/territory_bro/ui/documentation_page.clj @@ -4,8 +4,8 @@ (ns territory-bro.ui.documentation-page (:require [clojure.java.io :as io] - [hiccup2.core :as h] [territory-bro.infra.resources :as resources] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.layout :as layout] diff --git a/src/territory_bro/ui/error_page.clj b/src/territory_bro/ui/error_page.clj index 314c5b73..eb05b933 100644 --- a/src/territory_bro/ui/error_page.clj +++ b/src/territory_bro/ui/error_page.clj @@ -4,8 +4,8 @@ (ns territory-bro.ui.error-page (:require [clojure.tools.logging :as log] - [hiccup2.core :as h] [ring.util.response :as response] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.layout :as layout])) diff --git a/src/territory_bro/ui/hiccup.clj b/src/territory_bro/ui/hiccup.clj new file mode 100644 index 00000000..46015b4d --- /dev/null +++ b/src/territory_bro/ui/hiccup.clj @@ -0,0 +1,16 @@ +;; Copyright © 2015-2024 Esko Luontola +;; This software is released under the Apache License 2.0. +;; The license text is at http://www.apache.org/licenses/LICENSE-2.0 + +(ns territory-bro.ui.hiccup + (:require [hiccup.compiler :as compiler] + [hiccup.util :as util])) + +(defmacro html [& content] + (binding [util/*html-mode* :html ; compile time options + util/*escape-strings?* true] + `(binding [util/*html-mode* :html ; runtime options + util/*escape-strings?* true] + (util/raw-string ~(apply compiler/compile-html content))))) + +(def raw util/raw-string) diff --git a/src/territory_bro/ui/home_page.clj b/src/territory_bro/ui/home_page.clj index 0096061d..b186d65d 100644 --- a/src/territory_bro/ui/home_page.clj +++ b/src/territory_bro/ui/home_page.clj @@ -4,10 +4,10 @@ (ns territory-bro.ui.home-page (:require [clojure.java.io :as io] - [hiccup2.core :as h] [territory-bro.domain.dmz :as dmz] [territory-bro.infra.resources :as resources] [territory-bro.ui.css :as css] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.layout :as layout] diff --git a/src/territory_bro/ui/html.clj b/src/territory_bro/ui/html.clj index 1c63b9d9..e7964a8c 100644 --- a/src/territory_bro/ui/html.clj +++ b/src/territory_bro/ui/html.clj @@ -5,19 +5,16 @@ (ns territory-bro.ui.html (:require [clojure.java.io :as io] [clojure.string :as str] - [hiccup.util :as hiccup.util] - [hiccup2.core :as h] [net.cgrand.enlive-html :as en] [reitit.core :as reitit] [ring.middleware.anti-forgery :as anti-forgery] [ring.util.http-response :as http-response] [ring.util.response :as response] - [territory-bro.infra.json :as json]) + [territory-bro.infra.json :as json] + [territory-bro.ui.hiccup :as h]) (:import (org.reflections Reflections) (org.reflections.scanners Scanners))) -(alter-var-root #'hiccup.util/*html-mode* (constantly :html)) ; change default from :xhtml to :html - (def ^:dynamic *page-path*) diff --git a/src/territory_bro/ui/info_box.clj b/src/territory_bro/ui/info_box.clj index 0978a041..22d7a418 100644 --- a/src/territory_bro/ui/info_box.clj +++ b/src/territory_bro/ui/info_box.clj @@ -3,8 +3,8 @@ ;; The license text is at http://www.apache.org/licenses/LICENSE-2.0 (ns territory-bro.ui.info-box - (:require [hiccup2.core :as h] - [territory-bro.ui.css :as css] + (:require [territory-bro.ui.css :as css] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html])) (defn view [{:keys [title]} content] diff --git a/src/territory_bro/ui/join_page.clj b/src/territory_bro/ui/join_page.clj index f1f6a522..10deadaf 100644 --- a/src/territory_bro/ui/join_page.clj +++ b/src/territory_bro/ui/join_page.clj @@ -3,9 +3,9 @@ ;; The license text is at http://www.apache.org/licenses/LICENSE-2.0 (ns territory-bro.ui.join-page - (:require [hiccup2.core :as h] - [territory-bro.domain.dmz :as dmz] + (:require [territory-bro.domain.dmz :as dmz] [territory-bro.infra.authentication :as auth] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.layout :as layout])) diff --git a/src/territory_bro/ui/layout.clj b/src/territory_bro/ui/layout.clj index 709fc5d4..a95a64d6 100644 --- a/src/territory_bro/ui/layout.clj +++ b/src/territory_bro/ui/layout.clj @@ -5,13 +5,13 @@ (ns territory-bro.ui.layout (:require [clojure.string :as str] [hiccup.page :as hiccup.page] - [hiccup2.core :as h] [territory-bro.domain.dmz :as dmz] [territory-bro.infra.auth0 :as auth0] [territory-bro.infra.authentication :as auth] [territory-bro.infra.config :as config] [territory-bro.infra.resources :as resources] [territory-bro.ui.css :as css] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.info-box :as info-box])) @@ -181,8 +181,8 @@ (defn page [view model] (let [styles (:Layout (css/modules)) title (parse-title view)] - (assert (= :html hiccup.util/*html-mode*)) (str (h/html + (assert (= :html hiccup.util/*html-mode*)) (hiccup.page/doctype :html5) [:html {:lang (name i18n/*lang*)} [:head diff --git a/src/territory_bro/ui/map_interaction_help.clj b/src/territory_bro/ui/map_interaction_help.clj index ed0890ab..807674da 100644 --- a/src/territory_bro/ui/map_interaction_help.clj +++ b/src/territory_bro/ui/map_interaction_help.clj @@ -4,7 +4,7 @@ (ns territory-bro.ui.map-interaction-help (:require [clojure.string :as str] - [hiccup2.core :as h] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.info-box :as info-box])) diff --git a/src/territory_bro/ui/markdown.clj b/src/territory_bro/ui/markdown.clj index 5561233e..0d95a555 100644 --- a/src/territory_bro/ui/markdown.clj +++ b/src/territory_bro/ui/markdown.clj @@ -3,8 +3,8 @@ ;; The license text is at http://www.apache.org/licenses/LICENSE-2.0 (ns territory-bro.ui.markdown - (:require [hiccup2.core :as h] - [ring.util.http-response :as http-response]) + (:require [ring.util.http-response :as http-response] + [territory-bro.ui.hiccup :as h]) (:import (com.vladsch.flexmark.ext.anchorlink AnchorLinkExtension) (com.vladsch.flexmark.html HtmlRenderer) (com.vladsch.flexmark.parser Parser) diff --git a/src/territory_bro/ui/printout_templates.clj b/src/territory_bro/ui/printout_templates.clj index c140c766..256e4b6d 100644 --- a/src/territory_bro/ui/printout_templates.clj +++ b/src/territory_bro/ui/printout_templates.clj @@ -3,8 +3,8 @@ ;; The license text is at http://www.apache.org/licenses/LICENSE-2.0 (ns territory-bro.ui.printout-templates - (:require [hiccup2.core :as h] - [territory-bro.ui.css :as css] + (:require [territory-bro.ui.css :as css] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n]) (:import (java.time LocalDate))) diff --git a/src/territory_bro/ui/printouts_page.clj b/src/territory_bro/ui/printouts_page.clj index f8c35821..1eaf2963 100644 --- a/src/territory_bro/ui/printouts_page.clj +++ b/src/territory_bro/ui/printouts_page.clj @@ -4,12 +4,12 @@ (ns territory-bro.ui.printouts-page (:require [clojure.string :as str] - [hiccup2.core :as h] [ring.util.response :as response] [territory-bro.domain.dmz :as dmz] [territory-bro.gis.geometry :as geometry] [territory-bro.infra.json :as json] [territory-bro.infra.middleware :as middleware] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.layout :as layout] diff --git a/src/territory_bro/ui/privacy_policy_page.clj b/src/territory_bro/ui/privacy_policy_page.clj index 638ac95f..b8fd7b03 100644 --- a/src/territory_bro/ui/privacy_policy_page.clj +++ b/src/territory_bro/ui/privacy_policy_page.clj @@ -6,8 +6,8 @@ (:require [clojure.java.io :as io] [clojure.java.shell :as shell] [clojure.string :as str] - [hiccup2.core :as h] [territory-bro.infra.resources :as resources] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.layout :as layout] [territory-bro.ui.markdown :as markdown])) diff --git a/src/territory_bro/ui/registration_page.clj b/src/territory_bro/ui/registration_page.clj index 2b9dfca5..1ce05b98 100644 --- a/src/territory_bro/ui/registration_page.clj +++ b/src/territory_bro/ui/registration_page.clj @@ -3,10 +3,10 @@ ;; The license text is at http://www.apache.org/licenses/LICENSE-2.0 (ns territory-bro.ui.registration-page - (:require [hiccup2.core :as h] - [ring.util.http-response :as http-response] + (:require [ring.util.http-response :as http-response] [territory-bro.domain.dmz :as dmz] [territory-bro.ui.forms :as forms] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.layout :as layout]) diff --git a/src/territory_bro/ui/settings_page.clj b/src/territory_bro/ui/settings_page.clj index 73d3a3bf..c5fca386 100644 --- a/src/territory_bro/ui/settings_page.clj +++ b/src/territory_bro/ui/settings_page.clj @@ -4,7 +4,6 @@ (ns territory-bro.ui.settings-page (:require [clojure.string :as str] - [hiccup2.core :as h] [ring.util.codec :as codec] [ring.util.http-response :as http-response] [ring.util.response :as response] @@ -13,6 +12,7 @@ [territory-bro.infra.config :as config] [territory-bro.ui.css :as css] [territory-bro.ui.forms :as forms] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.info-box :as info-box] diff --git a/src/territory_bro/ui/support_page.clj b/src/territory_bro/ui/support_page.clj index 11c2502a..81f95c72 100644 --- a/src/territory_bro/ui/support_page.clj +++ b/src/territory_bro/ui/support_page.clj @@ -4,9 +4,9 @@ (ns territory-bro.ui.support-page (:require [clojure.string :as str] - [hiccup2.core :as h] [territory-bro.infra.authentication :as auth] [territory-bro.infra.config :as config] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.layout :as layout])) diff --git a/src/territory_bro/ui/territory_list_page.clj b/src/territory_bro/ui/territory_list_page.clj index 9fc1ccc2..c51db2b4 100644 --- a/src/territory_bro/ui/territory_list_page.clj +++ b/src/territory_bro/ui/territory_list_page.clj @@ -4,11 +4,11 @@ (ns territory-bro.ui.territory-list-page (:require [clojure.string :as str] - [hiccup2.core :as h] [territory-bro.domain.dmz :as dmz] [territory-bro.infra.authentication :as auth] [territory-bro.infra.json :as json] [territory-bro.ui.css :as css] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.info-box :as info-box] diff --git a/src/territory_bro/ui/territory_page.clj b/src/territory_bro/ui/territory_page.clj index 74e4f115..00cbaf70 100644 --- a/src/territory_bro/ui/territory_page.clj +++ b/src/territory_bro/ui/territory_page.clj @@ -4,13 +4,13 @@ (ns territory-bro.ui.territory-page (:require [clojure.string :as str] - [hiccup2.core :as h] [ring.util.response :as response] [territory-bro.domain.dmz :as dmz] [territory-bro.gis.geometry :as geometry] [territory-bro.infra.config :as config] [territory-bro.infra.middleware :as middleware] [territory-bro.ui.css :as css] + [territory-bro.ui.hiccup :as h] [territory-bro.ui.html :as html] [territory-bro.ui.i18n :as i18n] [territory-bro.ui.layout :as layout] diff --git a/test/territory_bro/ui/hiccup_test.clj b/test/territory_bro/ui/hiccup_test.clj new file mode 100644 index 00000000..08f5ce5c --- /dev/null +++ b/test/territory_bro/ui/hiccup_test.clj @@ -0,0 +1,28 @@ +;; Copyright © 2015-2024 Esko Luontola +;; This software is released under the Apache License 2.0. +;; The license text is at http://www.apache.org/licenses/LICENSE-2.0 + +(ns territory-bro.ui.hiccup-test + (:require [clojure.test :refer :all] + [territory-bro.ui.hiccup :as h]) + (:import (hiccup.util RawString))) + +(deftest html-test + (is (instance? RawString (h/html))) + (is (= "" (str (h/html)))) + + (testing "produces HTML 5" + (testing "at compile time" + (is (= "

foo

" (str (h/html [:p {:flag true} "foo"])))) + (is (= "
" (str (h/html [:br]))))) + + (testing "at runtime" + (is (= "

foo

" (str (h/html (identity [:p {:flag true} "foo"]))))) + (is (= "
" (str (h/html (identity [:br]))))))) + + (testing "escapes text" + (testing "at compile time" + (is (= "

<script>

" (str (h/html [:p "