Current section

46 Versions

Jump to

Compare versions

30 files changed
+198 additions
-74 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…