digitallyinduced / ihp

🔥 The fastest way to build type safe web apps. IHP is a new batteries-included web framework optimized for longterm productivity and programmer happiness
https://ihp.digitallyinduced.com/
MIT License
4.93k stars 195 forks source link

Custom HSX #2009

Closed kodeFant closed 2 days ago

kodeFant commented 1 week ago

Hi!

Sometimes, I would like to avoid using {...[("non-standard-attribute", "value" :: Text)]} in hsx and rather be able to extend it with my own whitelist of attributes I commonly use.

When using custom elements or js libraries with non-standard attributes, it would be nice to just have a custom HSX that just adds a whitelist.

I guess we could have it as a low level function that also hsx could use.

makeCustomHsx :: HashMap.HashMap Text TH.ExpQ -> QuasiQuoter
makeCustomHsx customAttributes = QuasiQuoter
    { quoteExp = quoteHsxExpression (HashMap.union customAttributes knownAttributes)
    , quotePat = error "quotePat: not defined"
    , quoteDec = error "quoteDec: not defined"
    , quoteType = error "quoteType: not defined"
    }

-- The standard QuasiQuoter uses the standard attributes
hsx :: QuasiQuoter
hsx = makeCustomHsx HashMap.empty

And with something like this, one can enjoy compile-time type-safety with custom attributes:

module Application.Helper.CustomHSX (customHsx) where

import IHP.HSX.QQ (makeCustomHsx)
import qualified Text.Blaze.Html5.Attributes as Attributes
import qualified Data.HashMap.Strict as HashMap

customAttributes :: HashMap.HashMap Text TH.ExpQ
customAttributes = HashMap.fromList
    [ ("tooltip", [| Attributes.customAttribute "tooltip" |])
    , ("magic", [| Attributes.customAttribute "magic" |])
    -- Add your custom attributes here
    ]

customHsx :: QuasiQuoter
customHsx = makeCustomHsx customAttributes

And then we could do this:

-- Using standard HSX (unchanged)
normal = [hsx|
    <div class="normal">content</div>
|]

-- Using custom HSX with additional attributes
custom = [customHsx|
    <custom-element 
        class="normal"      -- Standard attribute still works
        tooltip="help"      -- Custom attribute
        magic="sparkle"     -- Custom attribute
    >
        content
    </custom-element>
|]

I haven't actually tested it, so it's just pseudo for now.

If interesting, I could attempt a pull request.

mpscholten commented 1 week ago

I like the idea 👍

one alternative could also be adding an [uncheckedHsx||] quasi quoter. Like hsx but without any checking of the attributes or tag names.

Your approach feels more in line with type safety, so likely we should go with makeCustomHsx :)

kodeFant commented 1 week ago

Great! If it doesn't prove to be extremely difficult, I could try to add uncheckedHsx too. Would be nice for shipping XML for example if you don't want to declare every allowed tag and attribute

mpscholten commented 1 week ago

Great, let's try that :+1:

kodeFant commented 1 week ago

The custom thing was far too complex for me to deal with right now, so I went with uncheckedHsx #2010