Phillip England
Make Writing HTML in Go a Breeze 🍃
GTML is a compiler which converts .html files into composable .go functions. Think of it like JSX for go.
Turn this:
1<div _component="Greeting">
2 <h1>Hello, $prop("name")</h1>
3</div>
Into this:
1func Greeting(name string) string {
2 var builder strings.Builder
3 builder.WriteString(`<div _component="Greeting" _id="0"><h1>Hello, `)
4 builder.WriteString(name)
5 builder.WriteString(`!</h1>`)
6 return builder.String()
7}
With go 1.22.3 or later, clone the repo and build the binary on your system.
1git clone https://github.com/phillip-england/gtml;
2cd gtml;
3go build -o gtml main.go;
Then you'll be left with a binary you can move onto your PATH.
1mv gtml ./some/dir/on/your/path
1 _____ _______ __ __ _
2 / ____|__ __| \/ | |
3 | | __ | | | \ / | |
4 | | |_ | | | | |\/| | |
5 | |__| | | | | | | | |____
6 \_____| |_| |_| |_|______|
7 ---------------------------------------
8 Make Writing HTML in Go a Breeze 🍃
9 Version 0.1.10 (2024-12-6)
10 https://github.com/phillip-england/gtml
11 ---------------------------------------
12
13Usage:
14 gtml [OPTIONS]... [INPUT DIR] [OUTPUT FILE] [PACKAGE NAME]
15
16Example:
17 gtml --watch build ./components output.go output
18
19Options:
20 --watch rebuild when source files are modified
21
In gtml, we make use of html attributes to determine a components structure. Here is a quick list of the available attributes:
When gtml is scanning .html files, it is searching for _component elements. When it finds a _component, it will generate a function in go which will output the _component's html.
🚨
_componentmay not be defined within another_component. However, you can use a_componentas aplaceholderwithin another_component
When defining a _component, you must give it a name:
1<button _component="CustomButton">Click Me!</button>
The above _component will be converted into:
1func CustomButton() string {
2 var builder strings.Builder
3 builder.WriteString(`<button _component="CustomButton" _id="0">Click Me!</button>`)
4 return builder.String()
5}
_for elements are used to iterate over a slice. The slice may be a custom type or a string slice.
_for elements require their attribute value to be structured in the following way:
1_for="ITEM OF ITEMS []TYPE"
Such as:
1<div _component="ColorList">
2 <ul _for='color of colors []string'>
3 <p>$val(color)</p>
4 </ul>
5</div>
We can also do the same with a slice of custom types:
1<div _component="GuestList">
2 <ul _for='guest of Guests []Guest'>
3 <p>$val(guest.Name)</p>
4 </ul>
5</div>
🚨 bring your own types, gtml will not autogenerate them (yet..? 🦄)
_if elements are used to render a piece of html if a condition is met.
input:
1<div _component="AdminPage">
2 <div _if="isLoggedIn">
3 <p>you are logged in!</p>
4 </div>
5</div>
_else elements are used to render a piece of html if a condition is not met.
input:
1<div _component="AdminPage">
2 <div _else="isLoggedIn">
3 <p>you are not logged in!</p>
4 </div>
5</div>
_slot elements are unique in the sense that they are not used within a _component itself, rather, they are used in it's placeholder.
We will discuss placeholders more in a bit, but for now, just know that a placeholder is what we refer to a _component as when it is being used within another component.
For example, this LoginForm uses CustomButton as a placeholder
1<form _component="LoginForm">
2 ...
3 <CustomButton></CustomButton>
4</form>
5
6<div _component="CustomButton">
7 <button>Submit</button>
8</div>
Now, imagine a scenario where you are going to use certain components on each page, like maybe each page of your website has the same navbar and footer?
That is where _slot's are useful.
For example:
1<div _component="GuestLayout">
2 <navbar>my navbar</navbar>
3 $slot("content")
4 <footer></footer>
5<div>
6
7<GuestLayout _component="HomePage">
8 <div _slot="content">
9 I will appear in the content section!
10 </div>
11</GuestLayout>
_md elements are used to render a markdown file into html. You can also provide a theme in _md-theme. Here is a list of the available themes.
gtml uses goldmark under the hood to parse .md files.
input:
1<div _component="BlogPost">
2 <div _md="/path/to/file.md" _md-theme="dracula"></div>
3</div>
In gtml, we make use of runes to manage the way data flows throughout our components. Certain runes accept string values while others expect raw values. Here is a quick list of the available runes in gtml:
$prop() is used to define a prop within our _component. A prop is a value which is usable by sibling and child elements. The value passed into $prop() will end up in the arguments of our output function.
🚨:
$prop()only accepts strings:$prop("someStr")
For example, we may define a $prop() like so:
1<div _component="RuneProp">
2 <p>Hello, $prop("name")!</p>
3</div>
Once a $prop() has been defined, it can used in elsewhere in the same component using $val(). Also, you can pipe the value of a $prop() into a child _component using $pipe()
$val() is used to access the value of prop.
🚨:
$val()only accepts raw values:$val(rawValue)
For example, here we make use of $val() to access a neighboring prop:
1<div _component="Echo">
2 <p>$prop("message")</p>
3 <p>$val(message)</p>
4</div>
$val() is also used to access the data of iteration items in _for elements.
For example:
1<div _component="GuestList">
2 <ul _for='guest of Guests []Guest'>
3 <p>$val(guest.Name)</p>
4 </ul>
5</div>
$pipe() is used in situations where you want to pipe data from a parent _component into a child placeholder.
🚨:
$pipe()only accepts raw values:$pipe(rawValue)
For example:
1<div _component="RunePipe">
2 <p>Sally is $prop("age") years old</p>
3 <Greeting age="$pipe(age)"></Greeting> <== piping in the age
4</div>
5
6<div _component="Greeting">
7 <h1>This age was piped in!</h1>
8 <p>$prop("age")</p>
9</div>
When a _component is used within another _component, we refer to it as a placeholder. placeholders enable us to mix and match components with ease.
🚨: This is not JSX and you cannot do self-closing tags like
<SomeComponent/>. All tags must consist of both an opening tag and closing tag. This can be supported in future versions.
For example, this LoginForm uses CustomButton as a placeholder
1<form _component="LoginForm">
2 ...
3 <CustomButton></CustomButton>
4</form>
5
6<div _component="CustomButton">
7 <button>Submit</button>
8</div>
You may pass data into a placeholder using it's attributes. These attributes must corrospond to the target _component's props.
🚨: when passing values into
placeholderattributes, you must refer to the attributes in kebab-casing. This will be patched in future version. For example, below we do$prop("firstName")to definefirstName, but when we pass values into the_elementwe usefirst-nameinstead.
For example:
1<div _component="NameTag">
2 <h1>$prop("firstName")</h1>
3 <p>$prop("message")</p>
4</div>
5
6<NameTag _component="PlaceholderWithAttrs" message="is the best" first-name="gtml"></NameTag>
If a placeholder needs to access a value from a parent _component, the value may be piped in using the $pipe() rune.
For example:
For example:
1<div _component="RunePipe">
2 <p>Sally is $prop("age") years old</p>
3 <Greeting age="$pipe(age)"></Greeting> <== piping in the age
4</div>
5
6<div _component="Greeting">
7 <h1>This age was piped in!</h1>
8 <p>$prop("age")</p>
9</div>
This section contains notes related to the ongoing development of gtml.