Svg module (#25)
* Temple.Svg - scopes update_mdn_task to the temple namespace - introduces new temple.convert mix task to convert plain HTML and SVG to Temple syntax * Rename Temple.Tags to Temple.Html * Remove hackney I'm not sure why it was even in there ¯\_(ツ)_/¯ * Update floki * Document temple.convert in README
This commit is contained in:
parent
50c3bd93eb
commit
5acd6fc079
20 changed files with 515 additions and 94 deletions
|
@ -18,6 +18,15 @@ locals_without_parens = ~w[
|
|||
area br col embed hr img input keygen param source track wbr
|
||||
text partial
|
||||
|
||||
animate animateMotion animateTransform circle clipPath
|
||||
color-profile defs desc discard ellipse feBlend
|
||||
feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight feDropShadow
|
||||
feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset
|
||||
fePointLight feSpecularLighting feSpotLight feTile feTurbulence filter foreignObject g hatch hatchpath image line linearGradient
|
||||
marker mask mesh meshgradient meshpatch meshrow metadata mpath path pattern polygon
|
||||
polyline radialGradient rect set solidcolor stop svg switch symbol text_
|
||||
textPath tspan unknown use view
|
||||
|
||||
form_for inputs_for
|
||||
checkbox color_input checkbox color_input date_input date_select datetime_local_input
|
||||
datetime_select email_input file_input hidden_input number_input password_input range_input
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
|
||||
## Master
|
||||
|
||||
- `Temple.Svg` module
|
||||
- `mix temple.convert` Mix task
|
||||
- (dev) rename `mix update_mdn_docs` to `mix temple.update_mdn_docs` and don't ship it to hex
|
||||
|
||||
### Breaking
|
||||
|
||||
- Rename Temple.Tags to Temple.Html
|
||||
|
||||
## v0.3.1
|
||||
|
||||
- `Temple.Form.phx_label`, `Temple.Form.submit`, `Temple.Link.phx_button`, `Temple.Link.phx_link` now correctly parse blocks. Before this, they would escape anything passed to the block instead of accepting it as raw HTML.
|
||||
|
|
10
README.md
10
README.md
|
@ -22,7 +22,7 @@ end
|
|||
|
||||
Using Temple is a as simple as using the DSL inside of an `temple/1` block. This returns a safe result of the form `{:safe, html_string}`.
|
||||
|
||||
See the [documentation](https://hexdocs.pm/temple/Temple.Tags.html) for more details.
|
||||
See the [documentation](https://hexdocs.pm/temple/Temple.Html.html) for more details.
|
||||
|
||||
```elixir
|
||||
use Temple
|
||||
|
@ -153,6 +153,14 @@ html lang: "en" do
|
|||
end
|
||||
```
|
||||
|
||||
### Tasks
|
||||
|
||||
#### temple.convert
|
||||
|
||||
This task can be used to convert plain HTML and SVG into Temple syntax. Input is taken from stdin or from a file and the output is sent to stdout.
|
||||
|
||||
`cat index.html | mix temple.convert > index.html.exs`
|
||||
|
||||
### Formatter
|
||||
|
||||
To include Temple's formatter configuration, add `:temple` to your `.formatter.exs`.
|
||||
|
|
22
lib/mix/tasks/temple.convert.ex
Normal file
22
lib/mix/tasks/temple.convert.ex
Normal file
|
@ -0,0 +1,22 @@
|
|||
defmodule Mix.Tasks.Temple.Convert do
|
||||
use Mix.Task
|
||||
@shortdoc "Converts HTML to Temple syntax"
|
||||
@moduledoc """
|
||||
Converts HTML to Temple syntax
|
||||
|
||||
Takes HTML from a file or from stdin and outputs temple syntax to stdout.
|
||||
"""
|
||||
|
||||
def run(args) do
|
||||
html =
|
||||
if Enum.count(args) > 0 do
|
||||
args |> List.first() |> File.read!()
|
||||
else
|
||||
IO.read(:stdio, :all)
|
||||
end
|
||||
|
||||
{:ok, result} = Temple.HtmlToTemple.parse(html)
|
||||
|
||||
IO.write(result)
|
||||
end
|
||||
end
|
48
lib/mix/tasks/temple.update_mdn_docs.ex
Normal file
48
lib/mix/tasks/temple.update_mdn_docs.ex
Normal file
|
@ -0,0 +1,48 @@
|
|||
defmodule Mix.Tasks.Temple.UpdateMdnDocs do
|
||||
use Mix.Task
|
||||
|
||||
@html_base_url "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/"
|
||||
@svg_base_url "https://developer.mozilla.org/en-US/docs/Web/SVG/Element/"
|
||||
@params "?summary&raw"
|
||||
|
||||
@shortdoc "Update the MDN documentation"
|
||||
def run(_) do
|
||||
IO.puts("Downloading HTML documentation")
|
||||
|
||||
(Temple.Html.nonvoid_elements() ++ Temple.Html.void_elements() ++ ["html"])
|
||||
|> Enum.map(
|
||||
&to_doc(to_string(&1), "./tmp/docs/html/", fn el -> base_url(:html, html_page(el)) end)
|
||||
)
|
||||
|> Enum.each(&Task.await/1)
|
||||
|
||||
IO.puts("Downloading SVG documentation")
|
||||
|
||||
Temple.Svg.elements()
|
||||
|> Enum.map(&Temple.Utils.to_valid_tag(&1))
|
||||
|> Enum.map(&to_doc(&1, "./tmp/docs/svg/", fn el -> base_url(:svg, el) end))
|
||||
|> Enum.each(&Task.await/1)
|
||||
end
|
||||
|
||||
defp to_doc(el, dir_path, url_getter) do
|
||||
Task.async(fn ->
|
||||
url = url_getter.(el)
|
||||
|
||||
{doc, 0} = System.cmd("curl", ["--silent", url])
|
||||
|
||||
File.mkdir_p!(dir_path)
|
||||
|
||||
doc = HtmlSanitizeEx.strip_tags(doc)
|
||||
|
||||
File.write!(dir_path <> el <> ".txt", doc)
|
||||
end)
|
||||
end
|
||||
|
||||
defp html_page(el) when el in ["h1", "h2", "h3", "h4", "h5", "h6"] do
|
||||
"Heading_Elements"
|
||||
end
|
||||
|
||||
defp html_page(el), do: el
|
||||
|
||||
defp base_url(:html, page), do: @html_base_url <> page <> @params
|
||||
defp base_url(:svg, page), do: @svg_base_url <> page <> @params
|
||||
end
|
|
@ -1,34 +0,0 @@
|
|||
defmodule Mix.Tasks.UpdateMdnDocs do
|
||||
use Mix.Task
|
||||
|
||||
@baseurl "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/"
|
||||
@params "?summary&raw"
|
||||
|
||||
@shortdoc "Update the MDN documentation"
|
||||
def run(_) do
|
||||
IO.puts("Downloading MDN documentation")
|
||||
|
||||
(Temple.Tags.nonvoid_elements() ++ Temple.Tags.void_elements() ++ ["html"])
|
||||
|> Enum.map(fn el ->
|
||||
Task.async(fn ->
|
||||
el = to_string(el)
|
||||
|
||||
page =
|
||||
if Enum.any?(["h1", "h2", "h3", "h4", "h5", "h6"], &(&1 == el)) do
|
||||
"Heading_Elements"
|
||||
else
|
||||
el
|
||||
end
|
||||
|
||||
{doc, 0} = System.cmd("curl", ["--silent", @baseurl <> page <> @params])
|
||||
File.mkdir_p!("./tmp/docs/")
|
||||
|
||||
path = "./tmp/docs/" <> el <> ".txt"
|
||||
doc = HtmlSanitizeEx.strip_tags(doc)
|
||||
|
||||
File.write!(path, doc)
|
||||
end)
|
||||
end)
|
||||
|> Enum.each(&Task.await/1)
|
||||
end
|
||||
end
|
|
@ -2,9 +2,6 @@ defmodule Temple do
|
|||
defmacro __using__(_) do
|
||||
quote location: :keep do
|
||||
import Temple
|
||||
import Temple.Tags
|
||||
import Temple.Form
|
||||
import Temple.Link
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -33,14 +30,18 @@ defmodule Temple do
|
|||
"""
|
||||
defmacro temple([do: block] = _block) do
|
||||
quote location: :keep do
|
||||
import Kernel, except: [div: 2]
|
||||
import Kernel, except: [div: 2, use: 1, use: 2]
|
||||
import Temple.Html
|
||||
import Temple.Svg
|
||||
import Temple.Form
|
||||
import Temple.Link
|
||||
|
||||
with {:ok, var!(buff, Temple.Tags)} <- Temple.Utils.start_buffer([]) do
|
||||
with {:ok, var!(buff, Temple.Html)} <- Temple.Utils.start_buffer([]) do
|
||||
unquote(block)
|
||||
|
||||
markup = Temple.Utils.get_buffer(var!(buff, Temple.Tags))
|
||||
markup = Temple.Utils.get_buffer(var!(buff, Temple.Html))
|
||||
|
||||
:ok = Temple.Utils.stop_buffer(var!(buff, Temple.Tags))
|
||||
:ok = Temple.Utils.stop_buffer(var!(buff, Temple.Html))
|
||||
|
||||
Temple.Utils.join_and_escape(markup)
|
||||
end
|
||||
|
@ -63,7 +64,7 @@ defmodule Temple do
|
|||
defmacro text(text) do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_buffer(
|
||||
var!(buff, Temple.Tags),
|
||||
var!(buff, Temple.Html),
|
||||
unquote(text) |> Temple.Utils.escape_content()
|
||||
)
|
||||
end
|
||||
|
@ -98,7 +99,7 @@ defmodule Temple do
|
|||
defmacro partial(partial) do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_buffer(
|
||||
var!(buff, Temple.Tags),
|
||||
var!(buff, Temple.Html),
|
||||
unquote(partial) |> Temple.Utils.from_safe()
|
||||
)
|
||||
end
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
defmodule Temple.Elements do
|
||||
@moduledoc """
|
||||
This module contains the primitives used to generate the macros in the `Temple.Tags` module.
|
||||
This module contains the primitives used to generate the macros in the `Temple.Html` and `Temple.Svg` modules.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Defines an element.
|
||||
|
||||
*Note*: Underscores are converted to hypens.
|
||||
*Note*: Underscores are converted to dashes.
|
||||
|
||||
```elixir
|
||||
defmodule MyElements do
|
||||
|
@ -26,6 +26,7 @@ defmodule Temple.Elements do
|
|||
Temple.Elements.nonvoid_element(unquote(name))
|
||||
end
|
||||
|
||||
@doc false
|
||||
defmacro unquote(name)(attrs_or_content_or_block)
|
||||
|
||||
defmacro unquote(name)([{:do, _inner}] = block) do
|
||||
|
@ -36,6 +37,7 @@ defmodule Temple.Elements do
|
|||
Temple.Elements.nonvoid_element(unquote(name), attrs_or_content)
|
||||
end
|
||||
|
||||
@doc false
|
||||
defmacro unquote(name)(attrs_or_content, block_or_attrs)
|
||||
|
||||
defmacro unquote(name)(attrs, [{:do, _inner}] = block) do
|
||||
|
@ -59,8 +61,8 @@ defmodule Temple.Elements do
|
|||
@doc false
|
||||
def nonvoid_element(el) do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote(el), [])
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Html), unquote(el), [])
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Html), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -69,16 +71,16 @@ defmodule Temple.Elements do
|
|||
|
||||
def nonvoid_element(el, [{:do, inner}]) do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote(el), [])
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Html), unquote(el), [])
|
||||
_ = unquote(inner)
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Html), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
def nonvoid_element(el, attrs_or_content) do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote(el), unquote(attrs_or_content))
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Html), unquote(el), unquote(attrs_or_content))
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Html), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -87,24 +89,24 @@ defmodule Temple.Elements do
|
|||
|
||||
def nonvoid_element(el, attrs, [{:do, inner}] = _block) do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote_splicing([el, attrs]))
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Html), unquote_splicing([el, attrs]))
|
||||
_ = unquote(inner)
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Html), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
def nonvoid_element(el, content, attrs) do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Tags), unquote_splicing([el, attrs]))
|
||||
Temple.Utils.put_open_tag(var!(buff, Temple.Html), unquote_splicing([el, attrs]))
|
||||
text unquote(content)
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Tags), unquote(el))
|
||||
Temple.Utils.put_close_tag(var!(buff, Temple.Html), unquote(el))
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def void_element(el, attrs \\ []) do
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_void_tag(var!(buff, Temple.Tags), unquote_splicing([el, attrs]))
|
||||
Temple.Utils.put_void_tag(var!(buff, Temple.Html), unquote_splicing([el, attrs]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,9 +45,9 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
var!(form) = HTML.Form.form_for(unquote_splicing([form_data, action, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), var!(form) |> HTML.Safe.to_iodata())
|
||||
Utils.put_buffer(var!(buff, Temple.Html), var!(form) |> HTML.Safe.to_iodata())
|
||||
_ = unquote(block)
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), "</form>")
|
||||
Utils.put_buffer(var!(buff, Temple.Html), "</form>")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -83,7 +83,7 @@ defmodule Temple.Form do
|
|||
{:safe, input} =
|
||||
apply(Phoenix.HTML.Form, unquote(helper), [unquote_splicing([form, field, opts])])
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -97,7 +97,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.textarea(unquote_splicing([form, field, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -108,7 +108,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.reset(unquote_splicing([value, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -119,7 +119,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.submit(do: temple(do: unquote(block)))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -127,7 +127,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.submit(unquote(value))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -138,7 +138,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.submit(unquote(opts), do: temple(do: unquote(block)))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -146,7 +146,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.submit(unquote_splicing([value, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -157,7 +157,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.label(unquote_splicing([form, field]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -169,7 +169,7 @@ defmodule Temple.Form do
|
|||
{:safe, input} =
|
||||
Phoenix.HTML.Form.label(unquote_splicing([form, field]), do: temple(do: unquote(block)))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -177,7 +177,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.label(unquote_splicing([form, field, text_or_opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -191,7 +191,7 @@ defmodule Temple.Form do
|
|||
do: temple(do: unquote(block))
|
||||
)
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -199,7 +199,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.label(unquote_splicing([form, field, text, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -211,7 +211,7 @@ defmodule Temple.Form do
|
|||
{:safe, input} =
|
||||
Phoenix.HTML.Form.radio_button(unquote_splicing([form, field, value, attrs]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -223,7 +223,7 @@ defmodule Temple.Form do
|
|||
{:safe, input} =
|
||||
Phoenix.HTML.Form.multiple_select(unquote_splicing([form, field, options, attrs]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -234,7 +234,7 @@ defmodule Temple.Form do
|
|||
quote location: :keep do
|
||||
{:safe, input} = Phoenix.HTML.Form.select(unquote_splicing([form, field, options, attrs]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), input)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -293,7 +293,7 @@ defmodule Temple.Form do
|
|||
|
||||
hidden_input
|
||||
end)
|
||||
|> Enum.each(&Utils.put_buffer(var!(buff, Temple.Tags), &1))
|
||||
|> Enum.each(&Utils.put_buffer(var!(buff, Temple.Html), &1))
|
||||
|
||||
var!(inner_form) = form
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
defmodule Temple.Tags do
|
||||
defmodule Temple.Html do
|
||||
require Temple.Elements
|
||||
|
||||
@moduledoc """
|
||||
The `Temple.Tags` module defines macros for all HTML5 compliant elements.
|
||||
The `Temple.Html` module defines macros for all HTML5 compliant elements.
|
||||
|
||||
`Temple.Tags` macros must be called inside of a `Temple.temple/1` block.
|
||||
`Temple.Html` macros must be called inside of a `Temple.temple/1` block.
|
||||
|
||||
*Note*: Only the lowest arity macros are documented. Void elements are defined as a 1-arity macro and non-void elements are defined as 0, 1, and 2-arity macros.
|
||||
|
||||
## Attributes
|
||||
|
||||
Tags accept a keyword list or a map of attributes to be emitted into the element's opening tag. Multi-word attribute keys written in snake_case (`data_url`) will be transformed into kebab-case (`data-url`).
|
||||
Html accept a keyword list or a map of attributes to be emitted into the element's opening tag. Multi-word attribute keys written in snake_case (`data_url`) will be transformed into kebab-case (`data-url`).
|
||||
|
||||
## Children
|
||||
|
||||
|
@ -88,20 +90,20 @@ defmodule Temple.Tags do
|
|||
def void_elements, do: @void_elements
|
||||
|
||||
for el <- @nonvoid_elements do
|
||||
@doc if File.exists?("./tmp/docs/#{el}.txt"), do: File.read!("./tmp/docs/#{el}.txt")
|
||||
@doc if File.exists?("./tmp/docs/html/#{el}.txt"), do: File.read!("./tmp/docs/html/#{el}.txt")
|
||||
Temple.Elements.defelement(unquote(el), :nonvoid)
|
||||
end
|
||||
|
||||
for el <- @void_elements do
|
||||
@doc if File.exists?("./tmp/docs/#{el}.txt"), do: File.read!("./tmp/docs/#{el}.txt")
|
||||
@doc if File.exists?("./tmp/docs/html/#{el}.txt"), do: File.read!("./tmp/docs/html/#{el}.txt")
|
||||
Temple.Elements.defelement(unquote(el), :void)
|
||||
end
|
||||
|
||||
@doc if File.exists?("./tmp/docs/html.txt"), do: File.read!("./tmp/docs/html.txt")
|
||||
@doc if File.exists?("./tmp/docs/html/html.txt"), do: File.read!("./tmp/docs/html/html.txt")
|
||||
defmacro html(attrs \\ [], [{:do, _inner}] = block) do
|
||||
doc_type =
|
||||
quote location: :keep do
|
||||
Temple.Utils.put_buffer(var!(buff, Temple.Tags), "<!DOCTYPE html>")
|
||||
Temple.Utils.put_buffer(var!(buff, Temple.Html), "<!DOCTYPE html>")
|
||||
end
|
||||
|
||||
[doc_type, Temple.Elements.nonvoid_element(:html, attrs, block)]
|
74
lib/temple/html_to_temple.ex
Normal file
74
lib/temple/html_to_temple.ex
Normal file
|
@ -0,0 +1,74 @@
|
|||
defmodule Temple.HtmlToTemple do
|
||||
@moduledoc false
|
||||
|
||||
@tags Temple.Html.void_elements() ++
|
||||
Temple.Html.nonvoid_elements() ++ Temple.Svg.elements() ++ [:html]
|
||||
|
||||
def parse(doc) do
|
||||
result =
|
||||
doc
|
||||
|> Floki.parse()
|
||||
|> do_parse(0)
|
||||
|
||||
{:ok, result}
|
||||
end
|
||||
|
||||
def do_parse({tag, [], []}, indent) do
|
||||
tag = tag |> find_tag
|
||||
(Temple.Utils.kebab_to_snake(tag) <> "()\n") |> pad_indent(indent)
|
||||
end
|
||||
|
||||
def do_parse({tag, attrs, []}, indent) do
|
||||
tag = tag |> find_tag
|
||||
(Temple.Utils.kebab_to_snake(tag) <> build_attrs(attrs) <> "\n") |> pad_indent(indent)
|
||||
end
|
||||
|
||||
def do_parse({tag, attrs, [""]}, indent), do: do_parse({tag, attrs, []}, indent)
|
||||
|
||||
def do_parse({tag, attrs, children}, indent) do
|
||||
tag = tag |> find_tag
|
||||
|
||||
head =
|
||||
(Temple.Utils.kebab_to_snake(tag) <> build_attrs(attrs) <> " do\n")
|
||||
|> pad_indent(indent)
|
||||
|
||||
parsed_childs =
|
||||
for child <- children do
|
||||
do_parse(child, indent + 1)
|
||||
end
|
||||
|> Enum.join("\n")
|
||||
|
||||
head <> parsed_childs <> pad_indent("end\n", indent)
|
||||
end
|
||||
|
||||
def do_parse(text, indent) when is_binary(text) do
|
||||
(~s|text "| <> text <> ~s|"\n|) |> pad_indent(indent)
|
||||
end
|
||||
|
||||
defp build_attrs([]), do: ""
|
||||
|
||||
defp build_attrs(attrs) do
|
||||
attrs =
|
||||
for {key, value} <- attrs do
|
||||
wrap_in_quotes(key) <> ~s|: "| <> value <> ~s|"|
|
||||
end
|
||||
|> Enum.join(", ")
|
||||
|
||||
" " <> attrs
|
||||
end
|
||||
|
||||
defp wrap_in_quotes(key) do
|
||||
if Regex.match?(~r/[^a-zA-Z_]/, key) do
|
||||
~s|"| <> key <> ~s|"|
|
||||
else
|
||||
key
|
||||
end
|
||||
end
|
||||
|
||||
defp pad_indent(paddable, indent) do
|
||||
String.pad_leading(paddable, 2 * indent + String.length(paddable))
|
||||
end
|
||||
|
||||
defp find_tag(tag),
|
||||
do: @tags |> Enum.find(fn x -> String.downcase(to_string(x)) == tag end)
|
||||
end
|
|
@ -15,7 +15,7 @@ defmodule Temple.Link do
|
|||
temple(do: unquote(block))
|
||||
|> HTML.Link.link(unquote(opts))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), link)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), link)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -23,7 +23,7 @@ defmodule Temple.Link do
|
|||
quote location: :keep do
|
||||
{:safe, link} = HTML.Link.link(unquote_splicing([content, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), link)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), link)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -36,7 +36,7 @@ defmodule Temple.Link do
|
|||
temple(do: unquote(block))
|
||||
|> HTML.Link.button(unquote(opts))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), link)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), link)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -44,7 +44,7 @@ defmodule Temple.Link do
|
|||
quote location: :keep do
|
||||
{:safe, link} = HTML.Link.button(unquote_splicing([content, opts]))
|
||||
|
||||
Utils.put_buffer(var!(buff, Temple.Tags), link)
|
||||
Utils.put_buffer(var!(buff, Temple.Html), link)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
29
lib/temple/svg.ex
Normal file
29
lib/temple/svg.ex
Normal file
|
@ -0,0 +1,29 @@
|
|||
defmodule Temple.Svg do
|
||||
require Temple.Elements
|
||||
|
||||
@moduledoc """
|
||||
The `Temple.Svg` module defines macros for all SVG elements.
|
||||
|
||||
Usage is the same as `Temple.Html`.
|
||||
"""
|
||||
|
||||
@elements ~w[
|
||||
animate animateMotion animateTransform circle clipPath
|
||||
color_profile defs desc discard ellipse feBlend
|
||||
feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight feDropShadow
|
||||
feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset
|
||||
fePointLight feSpecularLighting feSpotLight feTile feTurbulence filter foreignObject g hatch hatchpath image line linearGradient
|
||||
marker mask metadata mpath path pattern polygon
|
||||
polyline radialGradient rect set solidcolor stop svg switch symbol text_
|
||||
textPath tspan use view
|
||||
]a
|
||||
|
||||
@doc false
|
||||
def elements(), do: @elements
|
||||
|
||||
for el <- @elements do
|
||||
@doc if File.exists?("./tmp/docs/svg/#{Temple.Utils.to_valid_tag(el)}.txt"),
|
||||
do: File.read!("./tmp/docs/svg/#{Temple.Utils.to_valid_tag(el)}.txt")
|
||||
Temple.Elements.defelement(unquote(el), :nonvoid)
|
||||
end
|
||||
end
|
|
@ -74,7 +74,10 @@ defmodule Temple.Utils do
|
|||
end
|
||||
|
||||
defp snake_to_kebab(stringable),
|
||||
do: stringable |> to_string() |> String.replace("_", "-")
|
||||
do: stringable |> to_string() |> String.replace_trailing("_", "") |> String.replace("_", "-")
|
||||
|
||||
def kebab_to_snake(stringable),
|
||||
do: stringable |> to_string() |> String.replace("-", "_")
|
||||
|
||||
def __quote__(outer) do
|
||||
quote [location: :keep], do: unquote(outer)
|
||||
|
@ -84,4 +87,10 @@ defmodule Temple.Utils do
|
|||
block
|
||||
|> Macro.prewalk(&Temple.Utils.insert_props(&1, props, inner))
|
||||
end
|
||||
|
||||
def doc_path(:html, el), do: "./tmp/docs/html/#{el}.txt"
|
||||
def doc_path(:svg, el), do: "./tmp/docs/svg/#{el}.txt"
|
||||
|
||||
def to_valid_tag(tag),
|
||||
do: tag |> to_string |> String.replace_trailing("_", "") |> String.replace("_", "-")
|
||||
end
|
||||
|
|
6
mix.exs
6
mix.exs
|
@ -40,13 +40,14 @@ defmodule Temple.MixProject do
|
|||
maintainers: ["Mitchell Hanberg"],
|
||||
licenses: ["MIT"],
|
||||
links: %{github: "https://github.com/mhanberg/temple"},
|
||||
exclude_patterns: ["temple.update_mdn_docs.ex"],
|
||||
files: ~w(lib priv CHANGELOG.md LICENSE mix.exs README.md .formatter.exs)
|
||||
]
|
||||
end
|
||||
|
||||
defp aliases do
|
||||
[
|
||||
docs: ["update_mdn_docs", "docs"]
|
||||
docs: ["temple.update_mdn_docs", "docs"]
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -58,7 +59,8 @@ defmodule Temple.MixProject do
|
|||
{:ex_doc, "~> 0.0", only: [:dev], runtime: false},
|
||||
{:html_sanitize_ex, "~> 1.3", only: [:dev, :test], runtime: false},
|
||||
{:phoenix, "~> 1.4", optional: true},
|
||||
{:plug, "~> 1.8", optional: true}
|
||||
{:plug, "~> 1.8", optional: true},
|
||||
{:floki, "~> 0.23.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
2
mix.lock
2
mix.lock
|
@ -3,6 +3,8 @@
|
|||
"earmark": {:hex, :earmark, "1.3.5", "0db71c8290b5bc81cb0101a2a507a76dca659513984d683119ee722828b424f6", [:mix], [], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "3.2.0", "940e2598813f205223d60c78d66e514afe1db5167ed8075510a59e496619cfb5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.21.2", "caca5bc28ed7b3bdc0b662f8afe2bee1eedb5c3cf7b322feeeb7c6ebbde089d6", [:mix], [{:earmark, "~> 1.3.3 or ~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"floki": {:hex, :floki, "0.23.0", "956ab6dba828c96e732454809fb0bd8d43ce0979b75f34de6322e73d4c917829", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"},
|
||||
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
|
89
test/mix/tasks/html_to_temple_test.exs
Normal file
89
test/mix/tasks/html_to_temple_test.exs
Normal file
|
@ -0,0 +1,89 @@
|
|||
defmodule Mix.Tasks.HtmlToTempleTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test "converts html to temple syntax" do
|
||||
html = """
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta>
|
||||
<script></script>
|
||||
<link>
|
||||
</head>
|
||||
<body>
|
||||
<header class="header" data-action="do a thing">
|
||||
<nav role="navigation">
|
||||
<ul>
|
||||
<li><a href="/home">Home</a></li>
|
||||
<li><a href="/about">About</a></li>
|
||||
<li><a href="/profile">Profile</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main role="main">
|
||||
<svg>
|
||||
<path d="alksdjfalksdjfslkadfj"/>
|
||||
<linearGradient>
|
||||
<stop></stop>
|
||||
</linearGradient
|
||||
</svg>
|
||||
</main>
|
||||
|
||||
<footer></footer>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
{:ok, result} = Temple.HtmlToTemple.parse(html)
|
||||
|
||||
assert result === """
|
||||
html lang: "en" do
|
||||
head do
|
||||
meta()
|
||||
|
||||
script()
|
||||
|
||||
link()
|
||||
end
|
||||
|
||||
body do
|
||||
header class: "header", "data-action": "do a thing" do
|
||||
nav role: "navigation" do
|
||||
ul do
|
||||
li do
|
||||
a href: "/home" do
|
||||
text "Home"
|
||||
end
|
||||
end
|
||||
|
||||
li do
|
||||
a href: "/about" do
|
||||
text "About"
|
||||
end
|
||||
end
|
||||
|
||||
li do
|
||||
a href: "/profile" do
|
||||
text "Profile"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
main role: "main" do
|
||||
svg do
|
||||
path d: "alksdjfalksdjfslkadfj"
|
||||
|
||||
linearGradient do
|
||||
stop()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
footer()
|
||||
end
|
||||
end
|
||||
"""
|
||||
end
|
||||
end
|
|
@ -2,7 +2,7 @@ defmodule Temple.ElementsTest do
|
|||
use ExUnit.Case, async: true
|
||||
import Temple.Elements, only: [defelement: 2]
|
||||
import Temple, only: [temple: 1, text: 1]
|
||||
import Temple.Tags, only: [option: 2]
|
||||
import Temple.Html, only: [option: 2]
|
||||
|
||||
defelement(:my_select, :nonvoid)
|
||||
defelement(:my_input, :void)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
defmodule Temple.TagsTest do
|
||||
defmodule Temple.HtmlTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Temple
|
||||
|
||||
|
@ -22,7 +22,7 @@ defmodule Temple.TagsTest do
|
|||
assert result == ~s{<!DOCTYPE html><html class="hello"><div></div></html>}
|
||||
end
|
||||
|
||||
for tag <- Temple.Tags.nonvoid_elements() do
|
||||
for tag <- Temple.Html.nonvoid_elements() do
|
||||
test "renders a #{tag}" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
|
@ -92,7 +92,7 @@ defmodule Temple.TagsTest do
|
|||
end
|
||||
end
|
||||
|
||||
for tag <- Temple.Tags.void_elements() do
|
||||
for tag <- Temple.Html.void_elements() do
|
||||
test "renders a #{tag}" do
|
||||
{:safe, result} =
|
||||
temple do
|
150
test/temple/svg_test.exs
Normal file
150
test/temple/svg_test.exs
Normal file
|
@ -0,0 +1,150 @@
|
|||
defmodule Temple.SvgTest do
|
||||
use ExUnit.Case, async: true
|
||||
import Temple
|
||||
import Temple.Svg
|
||||
import Temple.Utils, only: [to_valid_tag: 1]
|
||||
|
||||
for tag <- Temple.Svg.elements() -- [:text_] do
|
||||
test "renders a #{tag}" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
unquote(tag)()
|
||||
end
|
||||
|
||||
assert result == ~s{<#{to_valid_tag(unquote(tag))}></#{to_valid_tag(unquote(tag))}>}
|
||||
end
|
||||
|
||||
test "renders a #{tag} with attrs" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
unquote(tag)(class: "hello")
|
||||
end
|
||||
|
||||
assert result ==
|
||||
~s{<#{to_valid_tag(unquote(tag))} class="hello"></#{to_valid_tag(unquote(tag))}>}
|
||||
end
|
||||
|
||||
test "renders a #{tag} with content" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
unquote(tag)("Hi")
|
||||
end
|
||||
|
||||
assert result == "<#{to_valid_tag(unquote(tag))}>Hi</#{to_valid_tag(unquote(tag))}>"
|
||||
end
|
||||
|
||||
test "renders a #{tag} with escaped content" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
unquote(tag)("<div>1</div>")
|
||||
end
|
||||
|
||||
assert result ==
|
||||
"<#{to_valid_tag(unquote(tag))}><div>1</div></#{
|
||||
to_valid_tag(unquote(tag))
|
||||
}>"
|
||||
end
|
||||
|
||||
test "renders a #{tag} with attrs and content" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
unquote(tag)("Hi", class: "hello")
|
||||
end
|
||||
|
||||
assert result ==
|
||||
~s{<#{to_valid_tag(unquote(tag))} class="hello">Hi</#{to_valid_tag(unquote(tag))}>}
|
||||
end
|
||||
|
||||
test "renders a #{tag} with a block" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
unquote(tag)(do: unquote(tag)())
|
||||
end
|
||||
|
||||
assert result ==
|
||||
~s{<#{to_valid_tag(unquote(tag))}><#{to_valid_tag(unquote(tag))}></#{
|
||||
to_valid_tag(unquote(tag))
|
||||
}></#{to_valid_tag(unquote(tag))}>}
|
||||
end
|
||||
|
||||
test "renders a #{tag} with attrs and a block" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
unquote(tag)(class: "hello") do
|
||||
unquote(tag)()
|
||||
end
|
||||
end
|
||||
|
||||
assert result ==
|
||||
~s{<#{to_valid_tag(unquote(tag))} class="hello"><#{to_valid_tag(unquote(tag))}></#{
|
||||
to_valid_tag(unquote(tag))
|
||||
}></#{to_valid_tag(unquote(tag))}>}
|
||||
end
|
||||
end
|
||||
|
||||
test "renders a text" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
text_()
|
||||
end
|
||||
|
||||
assert result == ~s{<text></text>}
|
||||
end
|
||||
|
||||
test "renders a text with attrs" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
text_(class: "hello")
|
||||
end
|
||||
|
||||
assert result == ~s{<text class="hello"></text>}
|
||||
end
|
||||
|
||||
test "renders a text with content" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
text_("Hi")
|
||||
end
|
||||
|
||||
assert result == "<text>Hi</text>"
|
||||
end
|
||||
|
||||
test "renders a text with escaped content" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
text_("<div>1</div>")
|
||||
end
|
||||
|
||||
assert result == "<text><div>1</div></text>"
|
||||
end
|
||||
|
||||
test "renders a text with attrs and content" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
text_("Hi", class: "hello")
|
||||
end
|
||||
|
||||
assert result == ~s{<text class="hello">Hi</text>}
|
||||
end
|
||||
|
||||
test "renders a text with a block" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
text_(do: text_())
|
||||
end
|
||||
|
||||
assert result == ~s{<text><text></text></text>}
|
||||
end
|
||||
|
||||
test "renders a text with attrs and a block" do
|
||||
{:safe, result} =
|
||||
temple do
|
||||
text_(class: "hello") do
|
||||
text_()
|
||||
end
|
||||
end
|
||||
|
||||
assert result ==
|
||||
~s{<text class="hello"><text></text></text>}
|
||||
end
|
||||
end
|
Reference in a new issue