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:
Mitchell Hanberg 2019-09-13 21:36:16 -04:00 committed by GitHub
parent 50c3bd93eb
commit 5acd6fc079
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 515 additions and 94 deletions

View file

@ -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

View file

@ -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.

View file

@ -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`.

View 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

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)]

View 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

View file

@ -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
View 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

View file

@ -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

View file

@ -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

View file

@ -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"},

View 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

View file

@ -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)

View file

@ -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
View 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))}>&lt;div&gt;1&lt;/div&gt;</#{
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>&lt;div&gt;1&lt;/div&gt;</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