Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uncheckedHsx + customHsx #2010

Merged
merged 29 commits into from
Nov 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e05de32
add new module
kodeFant Oct 26, 2024
595f778
Fix uncheckedHSX quasiquoter
kodeFant Oct 26, 2024
0644e5b
Make sure closing tags are checked
kodeFant Oct 26, 2024
4a3553a
Fix it so it works
kodeFant Oct 26, 2024
6f6480a
remove UncheckedHSX and attept to restore QQ and Parser modules
kodeFant Oct 31, 2024
ef158e4
reset some unecessary whitespace stuff
kodeFant Oct 31, 2024
ba5bc56
remove unecessary whitespace
kodeFant Oct 31, 2024
d5b95f5
unchecked HSX working, with tests on the parser
kodeFant Oct 31, 2024
f54430b
Add customHsx + tests
kodeFant Oct 31, 2024
e85bb98
fix comment
kodeFant Oct 31, 2024
23bfdf7
fix comment
kodeFant Oct 31, 2024
b0ebf79
Update ihp-hsx/IHP/HSX/Parser.hs
kodeFant Oct 31, 2024
7e45f8a
Update ihp-hsx/IHP/HSX/QQ.hs
kodeFant Oct 31, 2024
ed49673
remove newtypes and use 'HsxSettings' directly
kodeFant Oct 31, 2024
3a7bd62
Fix Github resolve bug
kodeFant Oct 31, 2024
6eb7c17
Aesthethic nitpick
kodeFant Oct 31, 2024
83c3d2f
use customHsx to call hsx and uncheckedHsx
kodeFant Nov 1, 2024
0ad0581
Move CustomHsxCases to Test.HSX namespace
kodeFant Nov 1, 2024
a64d6ef
Fix import
kodeFant Nov 1, 2024
2ff2692
Fix module comment
kodeFant Nov 1, 2024
0185bea
For now, move CustomHsxCases back so the tests are working again
kodeFant Nov 1, 2024
0864979
Add documentation
kodeFant Nov 1, 2024
1de5cb8
Minor doc fix
kodeFant Nov 1, 2024
610ae2f
Formulate solution to QuasiQuoter shortcomings
kodeFant Nov 1, 2024
502aa2d
typo fix
kodeFant Nov 1, 2024
baafd7c
Add use-case example
kodeFant Nov 1, 2024
035ee28
Simplify langauge
kodeFant Nov 1, 2024
e7522e3
Improve examples a bit
kodeFant Nov 1, 2024
5c14759
Add spread example
kodeFant Nov 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions Guide/hsx.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,93 @@ The underlying HTML library blaze currently does not support an empty HTML attri

If you use HTML entities, such as ` ` for a non-breaking space, you will notice they appear exactly like that. To output directly (i.e. unescaped) use the method `preEscapedToMarkup` from `Text.Blaze.Html5`.

### Custom HSX and Unchecked HSX

HSX provides two additional QuasiQuoters beyond the standard `[hsx|...|]` for increased flexibility: `uncheckedHsx` and `customHsx`.

#### Using `uncheckedHsx`

`uncheckedHsx` provides a quick way to bypass HSX's strict tag and attribute name checking.

It will still check for valid HTML, but it will accept any tag and attribute names.


```haskell
[uncheckedHsx|
<my-custom-element custom-attribute="value">
Content
</my-custom-element>
|]
```

While convenient for rapid development, use it with caution as you lose the benefits of compile-time guarantees for your markup.
mpscholten marked this conversation as resolved.
Show resolved Hide resolved

#### Using `customHsx`

`customHsx` allows you to extend the default HSX with additional whitelisted tag names and attribute names while maintaining the same strict compile-time checking of the default `hsx`.

This makes it easier to use custom elements that often also contain special attributes, and javascript libraries, for example `_hyperscript`, that use the `_` as an attribute name.


To use `customHsx`, you need to create it in a separate module due to Template Haskell restrictions. Here's how to set it up:

1. First, create a new module for your custom HSX (e.g., `Application.Helper.CustomHsx`):

```haskell
module Application.Helper.CustomHsx where

import IHP.Prelude
import IHP.HSX.QQ (customHsx)
import IHP.HSX.Parser
import Language.Haskell.TH.Quote
import qualified Data.Set as Set

myHsx :: QuasiQuoter
myHsx = customHsx
(HsxSettings
{ checkMarkup = True
, additionalTagNames = Set.fromList ["book", "title", "name"]
, additionalAttributeNames = Set.fromList ["_", "custom-attribute"]
}
)
```

Configuration options for `HsxSettings`:
- `checkMarkup`: Boolean to enable/disable markup checking
- `additionalTagNames`: Set of additional allowed tag names
- `additionalAttributeNames`: Set of additional allowed attribute names

2. Make it available in your views by adding it to your view helpers module:

```haskell
module Application.Helper.View (
module Application.Helper.View,
module Application.Helper.CustomHsx -- Add this line
) where

import IHP.ViewPrelude
import Application.Helper.CustomHsx (myHsx) -- Add this line
```

3. Use it in your views:

```haskell
[myHsx|
<book>
<title custom-attribute="value">My Book</title>
<name>Author Name</name>
</book>
|]
```

The custom HSX will validate that tags and attributes are either in the default HSX whitelist or in your additional sets. This gives you the flexibility to use custom elements and attributes.

This approach is particularly useful for:
- Web Components with custom attribute names
- UI libraries with non-standard attributes
- Integration with third-party frameworks that extend HTML syntax

It's not usable for libraries with highly esoteric attributes, like Alpine.js. Even `uncheckedHsx` will throw an error because it doesn't recognize html attributes starting with `@` or has `:` in the attribute name.
kodeFant marked this conversation as resolved.
Show resolved Hide resolved

## Common HSX Patterns

Expand Down
15 changes: 8 additions & 7 deletions ihp-hsx/IHP/HSX/QQ.hs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,16 @@ uncheckedHsx = customHsx
, additionalTagNames = Set.empty
, additionalAttributeNames = Set.empty
}
)
)

customHsx :: HsxSettings -> QuasiQuoter
customHsx settings = QuasiQuoter {
quoteExp = quoteHsxExpression settings,
quotePat = error "quotePat: not defined",
quoteDec = error "quoteDec: not defined",
quoteType = error "quoteType: not defined"
}
customHsx settings =
QuasiQuoter
{ quoteExp = quoteHsxExpression settings
, quotePat = error "quotePat: not defined"
, quoteDec = error "quoteDec: not defined"
, quoteType = error "quoteType: not defined"
}

quoteHsxExpression :: HsxSettings -> String -> TH.ExpQ
quoteHsxExpression settings code = do
Expand Down
87 changes: 87 additions & 0 deletions ihp-hsx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,93 @@ The underlying HTML library blaze currently does not support an empty HTML attri

If you use HTML entities, such as `&nbsp;` for a non-breaking space, you will notice they appear exactly like that. To output directly (i.e. unescaped) use the method `preEscapedToMarkup` from `Text.Blaze.Html5`.

### Custom HSX and Unchecked HSX

HSX provides two additional QuasiQuoters beyond the standard `[hsx|...|]` for increased flexibility: `uncheckedHsx` and `customHsx`.

#### Using `uncheckedHsx`

`uncheckedHsx` provides a quick way to bypass HSX's strict tag and attribute name checking.

It will still check for valid HTML, but it will accept any tag and attribute names.


```haskell
[uncheckedHsx|
<my-custom-element custom-attribute="value">
Content
</my-custom-element>
|]
```

While convenient for rapid development, use it with caution as you lose the benefits of compile-time guarantees for your markup.

#### Using `customHsx`

`customHsx` allows you to extend the default HSX with additional whitelisted tag names and attribute names while maintaining the same strict compile-time checking of the default `hsx`.

This makes it easier to use custom elements that often also contain special attributes, and javascript libraries, for example `_hyperscript`, that use the `_` as an attribute name.


To use `customHsx`, you need to create it in a separate module due to Template Haskell restrictions. Here's how to set it up:

1. First, create a new module for your custom HSX (e.g., `Application.Helper.CustomHsx`):

```haskell
module Application.Helper.CustomHsx where

import IHP.Prelude
import IHP.HSX.QQ (customHsx)
import IHP.HSX.Parser
import Language.Haskell.TH.Quote
import qualified Data.Set as Set

myHsx :: QuasiQuoter
myHsx = customHsx
(HsxSettings
{ checkMarkup = True
, additionalTagNames = Set.fromList ["book", "title", "name"]
, additionalAttributeNames = Set.fromList ["_", "custom-attribute"]
}
)
```

Configuration options for `HsxSettings`:
- `checkMarkup`: Boolean to enable/disable markup checking
- `additionalTagNames`: Set of additional allowed tag names
- `additionalAttributeNames`: Set of additional allowed attribute names

2. Make it available in your views by adding it to your view helpers module:

```haskell
module Application.Helper.View (
module Application.Helper.View,
module Application.Helper.CustomHsx -- Add this line
) where

import IHP.ViewPrelude
import Application.Helper.CustomHsx (myHsx) -- Add this line
```

3. Use it in your views:

```haskell
[myHsx|
<book>
<title custom-attribute="value">My Book</title>
<name>Author Name</name>
</book>
|]
```

The custom HSX will validate that tags and attributes are either in the default HSX whitelist or in your additional sets. This gives you the flexibility to use custom elements and attributes.

This approach is particularly useful for:
- Web Components with custom attribute names
- UI libraries with non-standard attributes
- Integration with third-party frameworks that extend HTML syntax

It's not usable for libraries with highly esoteric attributes, like Alpine.js. Even `uncheckedHsx` will throw an error because it doesn't recognize html attributes starting with `@` or has `:` in the attribute name.

## Common HSX Patterns

Expand Down