Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ config :logger, :level, :debug
config :logger, :default_handler, false

config :phoenix_live_view, enable_expensive_runtime_checks: true

# Disable :root_tag_attribute so the majority of the tests
# are not polluted by it. It will be explicitly re-enabled for
# tests related to :root_tag_attribute functionality.
config :phoenix_live_view, :root_tag_attribute, nil
132 changes: 132 additions & 0 deletions lib/phoenix_component/macro_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,131 @@ defmodule Phoenix.Component.MacroComponent do
# LiveView's end to end tests: a macro component that performs
# [syntax highlighting at compile time](https://github.com/phoenixframework/phoenix_live_view/blob/38851d943f3280c5982d75679291dccb8c442534/test/e2e/support/colocated_live.ex#L4-L35)
# using the [Makeup](https://hexdocs.pm/makeup/Makeup.html) library.
#
# ## Root tag attributes
#
# HEEx templates support adding a root tag attributes to the rendered page.
# These can be useful for debugging or as selectors for things such as CSS.
#
# Note that root tag attributes are applied to all root tags in the given
# template, not just the outermost root tags. This means that tags at the root
# of the template itself, at the root of any component inner blocks, or at
# the root of any component slots will all have root tag attributes applied.
#
# For example, imagine the following component definition:
#
# ```elixir
# defmodule MyAppWeb.MyModule do
# slot :inner_block, required: true
# slot :named_slot, required: true
#
# def my_function(assigns) do
# ~H"""
# <section>
# <div>
# {render_slot(@inner_block)}
# </div>
# </section>
# <aside>
# <div>
# {render_slot(@named_slot)}
# </div>
# </aside>
# """
# end
# end
# ```
#
# And the following HEEx template:
#
# ```heex
# <div>
# <div>
# <.my_function>
# <p>
# <span>
# Inner Block
# </span>
# </p>
# <:named_slot>
# <p>
# <span>
# Named Slot
# </span>
# </p>
# </:named_slot>
# </.my_function>
# </div>
# </div>
# ```
#
# By setting the global `root_tag_attribute` to "phx-r", the rendered HTML would look as follows:
#
# ```html
# <div phx-r>
# <div>
# <section phx-r>
# <div>
# <p phx-r>
# <span>
# Inner Block
# </span>
# </p>
# </div>
# </section>
# <aside phx-r>
# <div>
# <p phx-r>
# <span>
# Named Slot
# </span>
# </p>
# </div>
# </aside>
# </div>
# </div>
# ```
#
# This feature works on any `~H` or `.html.heex` template. They can be enabled
# globally with the following configuration in your `config/config.exs` file:
#
# config :phoenix_live_view, root_tag_attribute: "phx-r"
#
# Changing this configuration will require `mix clean` and a full recompile.
#
# Additional root tag attributes can also be applied by MacroComponents. See the `Directives` section
# below for details.
#
# ## Directives
#
# Macro components may return directives from the module's optional `c:directives/2` callback
# which can be used to influence other elements in the template outside of the macro component at compile-time.
# Macro components which specify directives **must** be placed at the beginning of the template in which they are used.
# For example:
#
# ```elixir
# defmodule MyAppWeb.TagRootSampleComponent do
# @behaviour Phoenix.Component.MacroComponent
#
# @impl true
# def directives(_ast, _meta) do
# {:ok, [root_tag_attribute: {"phx-sample-one", "test"}, root_tag_attribute: {"phx-sample-two", "test"}]}
# end
#
# @impl true
# def transform(_ast, _meta) do
# {:ok, "", %{}}
# end
# end
# ```
# The following directives are currently supported:
#
# ### Options
#
# * `:root_tag_attribute` - `{name, value}` pair to apply as an attribute to all root tags during template compilation.
# Requires that a global `:root_tag_attribute` be configured for the application. `name` must be a compile-time string. `value` must be a
# compile-time string or `true`. May be provided multiple times to apply multiple attributes. Requires that a global `:root_tag_attribute` be configured.
# See the `Root tag attributes` section above for more details.

@type tag :: binary()
@type attribute :: {binary(), Macro.t()}
Expand All @@ -128,6 +253,13 @@ defmodule Phoenix.Component.MacroComponent do
@type tag_meta :: %{closing: :self | :void}
@type heex_ast :: {tag(), attributes(), children(), tag_meta()} | binary()
@type transform_meta :: %{env: Macro.Env.t()}
@type directives_meta :: %{env: Macro.Env.t()}
@type directive :: {:root_tag_attribute, {name :: String.t(), value :: String.t() | true}}
@type directives :: [directive]

@optional_callbacks [directives: 2]

@callback directives(heex_ast :: heex_ast(), meta :: directives_meta()) :: {:ok, directives()}

@callback transform(heex_ast :: heex_ast(), meta :: transform_meta()) ::
{:ok, heex_ast()} | {:ok, heex_ast(), data :: term()}
Expand Down
Loading
Loading