Packages
surface
0.5.1
0.12.3
0.12.2
0.12.1
0.12.0
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.1
0.1.0
0.1.0-alpha.2
0.1.0-alpha.1
0.1.0-alpha.0
A component based library for Phoenix LiveView
Current section
46 Versions
Jump to
Current section
46 Versions
Compare versions
30
files changed
+197
additions
-73
deletions
| @@ -1,5 +1,15 @@ | |
| 1 1 | # Changelog |
| 2 2 | |
| 3 | + ## v0.5.1 (2021-07-13) |
| 4 | + |
| 5 | + * Add property `values` to form inputs |
| 6 | + * Handle doctype as text |
| 7 | + * Improve error message when `default_translator` is not configured for `ErrorTag` (#449) |
| 8 | + * Raise on invalid attribute/directive in `<#slot>` (#456) |
| 9 | + * Raise error on `{#case}` without `{#match}` (#443) |
| 10 | + * Raise on blocks without expression |
| 11 | + * Fix error line on missing closing tag |
| 12 | + |
| 3 13 | ## v0.5.0 (2021-06-17) |
| 4 14 | |
| 5 15 | * Add `<:slotname>` shorthand for `<#template slot="slotname">` |
| @@ -119,4 +119,4 @@ | |
| 119 119 | {<<"optional">>,false}, |
| 120 120 | {<<"repository">>,<<"hexpm">>}, |
| 121 121 | {<<"requirement">>,<<"~> 0.15">>}]]}. |
| 122 | - {<<"version">>,<<"0.5.0">>}. |
| 122 | + {<<"version">>,<<"0.5.1">>}. |
| @@ -42,7 +42,7 @@ defmodule Surface.Compiler do | |
| 42 42 | @template_directive_handlers [Surface.Directive.Let] |
| 43 43 | |
| 44 44 | @slot_directive_handlers [ |
| 45 | - Surface.Directive.SlotProps, |
| 45 | + Surface.Directive.SlotArgs, |
| 46 46 | Surface.Directive.If, |
| 47 47 | Surface.Directive.For |
| 48 48 | ] |
| @@ -126,11 +126,15 @@ defmodule Surface.Compiler do | |
| 126 126 | end |
| 127 127 | |
| 128 128 | defp is_stateful_component(module) do |
| 129 | - if Module.open?(module) do |
| 130 | - Module.get_attribute(module, :component_type, Surface.BaseComponent) in @stateful_component_types |
| 131 | - else |
| 132 | - function_exported?(module, :component_type, 0) and |
| 129 | + cond do |
| 130 | + function_exported?(module, :component_type, 0) -> |
| 133 131 | module.component_type() in @stateful_component_types |
| 132 | + |
| 133 | + Module.open?(module) -> |
| 134 | + Module.get_attribute(module, :component_type) in @stateful_component_types |
| 135 | + |
| 136 | + true -> |
| 137 | + false |
| 134 138 | end |
| 135 139 | end |
| 136 140 | |
| @@ -342,6 +346,14 @@ defmodule Surface.Compiler do | |
| 342 346 | }} |
| 343 347 | end |
| 344 348 | |
| 349 | + defp convert_node_to_ast(:block, {:block, "case", _, _, %{has_sub_blocks?: false} = node_meta}, compile_meta) do |
| 350 | + meta = Helpers.to_meta(node_meta, compile_meta) |
| 351 | + |
| 352 | + message = "no {#match} sub-block defined. A {#case} block must include at least one {#match ...} sub-block." |
| 353 | + |
| 354 | + IOHelper.compile_error(message, meta.file, meta.line) |
| 355 | + end |
| 356 | + |
| 345 357 | defp convert_node_to_ast(:block, {:block, name, attrs, children, meta}, compile_meta) do |
| 346 358 | {:ok, |
| 347 359 | %AST.Block{ |
| @@ -447,11 +459,12 @@ defmodule Surface.Compiler do | |
| 447 459 | |
| 448 460 | index = attribute_value_as_ast(attributes, "index", %Surface.AST.Literal{value: 0}, compile_meta) |
| 449 461 | |
| 450 | - with {:ok, directives, _attrs} <- |
| 462 | + with {:ok, directives, attrs} <- |
| 451 463 | collect_directives(@slot_directive_handlers, attributes, meta), |
| 452 464 | slot <- Enum.find(defined_slots, fn slot -> slot.name == name end), |
| 453 465 | slot when not is_nil(slot) <- slot do |
| 454 466 | maybe_warn_required_slot_with_default_value(slot, children, short_slot_syntax?, meta) |
| 467 | + validate_slot_attrs!(attrs) |
| 455 468 | |
| 456 469 | {:ok, |
| 457 470 | %AST.Slot{ |
| @@ -1178,4 +1191,24 @@ defmodule Surface.Compiler do | |
| 1178 1191 | value |
| 1179 1192 | end |
| 1180 1193 | end |
| 1194 | + |
| 1195 | + defp validate_slot_attrs!([{name, _, %{file: file, line: line}} | _]) when name != "name" do |
| 1196 | + type = |
| 1197 | + case name do |
| 1198 | + ":" <> _ -> "directive" |
| 1199 | + _ -> "attribute" |
| 1200 | + end |
| 1201 | + |
| 1202 | + message = """ |
| 1203 | + invalid #{type} `#{name}` for <#slot>. |
| 1204 | + |
| 1205 | + Slots only accept `name`, `:args`, `:if` and `:for`. |
| 1206 | + """ |
| 1207 | + |
| 1208 | + IOHelper.compile_error(message, file, line) |
| 1209 | + end |
| 1210 | + |
| 1211 | + defp validate_slot_attrs!(_) do |
| 1212 | + :ok |
| 1213 | + end |
| 1181 1214 | end |
| @@ -22,6 +22,11 @@ defmodule Surface.Compiler.ParseTreeTranslator do | |
| 22 22 | {{name, attributes, body, to_meta(meta)}, state} |
| 23 23 | end |
| 24 24 | |
| 25 | + def handle_block(name, [], _body, meta, _state, _context) do |
| 26 | + message = "missing expression for block {\##{name} ...}" |
| 27 | + IOHelper.compile_error(message, meta.file, meta.line) |
| 28 | + end |
| 29 | + |
| 25 30 | def handle_block(name, expr, body, meta, state, _context) do |
| 26 31 | {{:block, name, expr, body, to_meta(meta)}, state} |
| 27 32 | end |
| @@ -69,6 +74,15 @@ defmodule Surface.Compiler.ParseTreeTranslator do | |
| 69 74 | |
| 70 75 | directive = |
| 71 76 | case tag_name do |
| 77 | + "#slot" -> |
| 78 | + message = """ |
| 79 | + cannot pass dynamic attributes to <#slot>. |
| 80 | + |
| 81 | + Slots only accept `name`, `:args`, `:if` and `:for`. |
| 82 | + """ |
| 83 | + |
| 84 | + IOHelper.compile_error(message, attr_meta.file, attr_meta.line) |
| 85 | + |
| 72 86 | <<first, _::binary>> when first in ?A..?Z -> |
| 73 87 | ":props" |
| @@ -57,7 +57,7 @@ defmodule Surface.Compiler.Parser do | |
| 57 57 | end |
| 58 58 | |
| 59 59 | defp handle_token([], _buffers, %{token_stack: [{{_, _name, _attrs, meta} = node, _ctx} | _]}) do |
| 60 | - message = "expected closing node for #{format_node(node)} defined on line #{meta.line}, got EOF" |
| 60 | + message = "end of file reached without closing #{node_type(node)} for #{format_node(node)}" |
| 61 61 | raise parse_error(message, meta) |
| 62 62 | end |
| 63 63 | |
| @@ -173,6 +173,7 @@ defmodule Surface.Compiler.Parser do | |
| 173 173 | |
| 174 174 | defp handle_token([{:block_close, name, _meta} = token | rest], buffers, state) when name in @blocks do |
| 175 175 | {{:block_open, name, expr, meta}, context, state} = pop_matching_tag(state, token) |
| 176 | + meta = Map.put_new(meta, :has_sub_blocks?, false) |
| 176 177 | |
| 177 178 | # pop the current buffer and use it as children for the node |
| 178 179 | [buffer | buffers] = buffers |
| @@ -361,12 +362,14 @@ defmodule Surface.Compiler.Parser do | |
| 361 362 | end |
| 362 363 | |
| 363 364 | defp pop_matching_tag(%{token_stack: [{{_, _, _, meta} = token_open, _ctx} | _]}, token_close) do |
| 365 | + {_, _, meta_close} = token_close |
| 366 | + |
| 364 367 | message = """ |
| 365 | - expected closing node for #{format_node(token_open)} defined on line #{meta.line}, \ |
| 368 | + expected closing #{node_type(token_open)} for #{format_node(token_open)} defined on line #{meta.line}, \ |
| 366 369 | got #{format_node(token_close)}\ |
| 367 370 | """ |
| 368 371 | |
| 369 | - raise parse_error(message, meta) |
| 372 | + raise parse_error(message, meta_close) |
| 370 373 | end |
| 371 374 | |
| 372 375 | defp parent_context([{_tag, context} | _]), do: context |
| @@ -381,6 +384,11 @@ defmodule Surface.Compiler.Parser do | |
| 381 384 | } |
| 382 385 | end |
| 383 386 | |
| 387 | + defp node_type({:tag_open, _, _, _}), do: "tag" |
| 388 | + defp node_type({:tag_close, _, _}), do: "tag" |
| 389 | + defp node_type({:block_open, _, _, _}), do: "block" |
| 390 | + defp node_type({:block_close, _, _}), do: "block" |
| 391 | + |
| 384 392 | defp format_node({:tag_open, name, _attrs, _meta}), do: "<#{name}>" |
| 385 393 | defp format_node({:tag_close, name, _meta}), do: "</#{name}>" |
| 386 394 | defp format_node({:block_open, name, _attrs, _meta}), do: "{##{name}}" |
Loading more files…