Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require Elixir v1.14+ #3519

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
116 changes: 58 additions & 58 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
include:
- elixir: 1.13.4
- elixir: 1.14.5
otp: 24.3

- elixir: 1.15.4
Expand All @@ -34,62 +34,62 @@ jobs:
runs-on: ubuntu-latest

steps:
- name: Install inotify-tools
run: |
sudo apt update
sudo apt install -y inotify-tools
- name: Checkout
uses: actions/checkout@v4

- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ matrix.elixir }}
otp-version: ${{ matrix.otp }}

- name: Restore deps and _build cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}

- name: Install dependencies
run: mix deps.get --only test

- name: Remove compiled application files
run: mix clean

- name: Compile dependencies
run: mix compile
if: ${{ !matrix.lint }}
env:
MIX_ENV: test

- name: Compile & lint dependencies
run: mix compile --warnings-as-errors
if: ${{ matrix.lint }} or ${{ matrix.elixir == 'main' }}
env:
MIX_ENV: test

- name: Check if formatted
run: mix format --check-formatted
if: ${{ matrix.lint }}
env:
MIX_ENV: test

- name: Run tests
run: mix test --cover --export-coverage default

- uses: actions/upload-artifact@v4
if: always()
with:
name: mix-test-coverage-${{ matrix.otp }}-${{ matrix.elixir }}
path: cover/default.coverdata
retention-days: 7
- name: Install inotify-tools
run: |
sudo apt update
sudo apt install -y inotify-tools
- name: Checkout
uses: actions/checkout@v4

- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ matrix.elixir }}
otp-version: ${{ matrix.otp }}

- name: Restore deps and _build cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}

- name: Install dependencies
run: mix deps.get --only test

- name: Remove compiled application files
run: mix clean

- name: Compile dependencies
run: mix compile
if: ${{ !matrix.lint }}
env:
MIX_ENV: test

- name: Compile & lint dependencies
run: mix compile --warnings-as-errors
if: ${{ matrix.lint }} or ${{ matrix.elixir == 'main' }}
env:
MIX_ENV: test

- name: Check if formatted
run: mix format --check-formatted
if: ${{ matrix.lint }}
env:
MIX_ENV: test

- name: Run tests
run: mix test --cover --export-coverage default

- uses: actions/upload-artifact@v4
if: always()
with:
name: mix-test-coverage-${{ matrix.otp }}-${{ matrix.elixir }}
path: cover/default.coverdata
retention-days: 7

npm_test:
name: npm test
Expand Down Expand Up @@ -215,7 +215,7 @@ jobs:
name: e2e-test-results
path: test/e2e/test-results/
retention-days: 7

- uses: actions/upload-artifact@v4
if: always()
with:
Expand Down
21 changes: 2 additions & 19 deletions lib/phoenix_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1978,20 +1978,8 @@ defmodule Phoenix.Component do
'''
@doc type: :macro
defmacro attr(name, type, opts \\ []) do
# TODO: Use Macro.expand_literals on Elixir v1.14.1+
type =
if Macro.quoted_literal?(type) do
Macro.prewalk(type, &expand_alias(&1, __CALLER__))
else
type
end

opts =
if Macro.quoted_literal?(opts) do
Macro.prewalk(opts, &expand_alias(&1, __CALLER__))
else
opts
end
type = Macro.expand_literals(type, __CALLER__)
opts = Macro.expand_literals(opts, __CALLER__)

quote bind_quoted: [name: name, type: type, opts: opts] do
Phoenix.Component.Declarative.__attr__!(
Expand All @@ -2005,11 +1993,6 @@ defmodule Phoenix.Component do
end
end

defp expand_alias({:__aliases__, _, _} = alias, env),
do: Macro.expand(alias, %{env | function: {:__attr__, 3}})

defp expand_alias(other, _env), do: other

## Components

import Kernel, except: [def: 2, defp: 2]
Expand Down
4 changes: 1 addition & 3 deletions lib/phoenix_component/declarative.ex
Original file line number Diff line number Diff line change
Expand Up @@ -612,8 +612,6 @@ defmodule Phoenix.Component.Declarative do
end
end

@after_verify_supported Version.match?(System.version(), ">= 1.14.0-dev")

@doc false
defmacro __before_compile__(env) do
attrs = pop_attrs(env)
Expand Down Expand Up @@ -711,7 +709,7 @@ defmodule Phoenix.Component.Declarative do
end

def_components_calls_ast =
if components_calls != [] and @after_verify_supported do
if components_calls != [] do
quote do
@after_verify {__MODULE__, :__phoenix_component_verify__}

Expand Down
17 changes: 2 additions & 15 deletions lib/phoenix_live_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -548,10 +548,10 @@ defmodule Phoenix.LiveView do
mod_or_mod_arg =
case mod_or_mod_arg do
{mod, arg} ->
{expand_literals(mod, __CALLER__), expand_literals(arg, __CALLER__)}
{Macro.expand_literals(mod, __CALLER__), Macro.expand_literals(arg, __CALLER__)}

mod_or_mod_arg ->
expand_literals(mod_or_mod_arg, __CALLER__)
Macro.expand_literals(mod_or_mod_arg, __CALLER__)
end

quote do
Expand All @@ -563,19 +563,6 @@ defmodule Phoenix.LiveView do
end
end

defp expand_literals(ast, env) do
if Macro.quoted_literal?(ast) do
Macro.prewalk(ast, &expand_alias(&1, env))
else
ast
end
end

defp expand_alias({:__aliases__, _, _} = alias, env),
do: Macro.expand(alias, %{env | function: {:on_mount, 4}})

defp expand_alias(other, _env), do: other

@doc """
Returns true if the socket is connected.

Expand Down
9 changes: 1 addition & 8 deletions lib/phoenix_live_view/async.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,7 @@ defmodule Phoenix.LiveView.Async do
Macro.prewalk(func, fn
{:socket, meta, _} ->
warn_socket_access(op, fn msg ->
# TODO: Remove conditional once we require Elixir v1.14+
meta =
if Version.match?(System.version(), ">= 1.14.0") do
Keyword.take(meta, [:line, :column]) ++ [line: env.line, file: env.file]
else
Macro.Env.stacktrace(env)
end

meta = Keyword.take(meta, [:line, :column]) ++ [line: env.line, file: env.file]
IO.warn(msg, meta)
end)

Expand Down
62 changes: 19 additions & 43 deletions lib/phoenix_live_view/html_formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,7 @@ defmodule Phoenix.LiveView.HTMLFormatter do
# If the opening delimiter is a single character, such as ~H"...", or the formatted code is empty,
# do not add trailing newline.
newline = if match?(<<_>>, opts[:opening_delimiter]) or formatted == [], do: [], else: ?\n

# TODO: Remove IO.iodata_to_binary/1 call on Elixir v1.14+
IO.iodata_to_binary([formatted, newline])
[formatted, newline]
end
end

Expand Down Expand Up @@ -281,51 +279,29 @@ defmodule Phoenix.LiveView.HTMLFormatter do
# {::close, :tag, "section", %{column: 1, line: 2}}
# ]
#
# EEx.tokenize/2 was introduced in Elixir 1.14.
# TODO: Remove this when we no longer support earlier versions.
@eex_expr [:start_expr, :expr, :end_expr, :middle_expr]
if Code.ensure_loaded?(EEx) && function_exported?(EEx, :tokenize, 2) do
defp tokenize(source) do
{:ok, eex_nodes} = EEx.tokenize(source)
{tokens, cont} = Enum.reduce(eex_nodes, {[], :text}, &do_tokenize(&1, &2, source))
Tokenizer.finalize(tokens, "nofile", cont, source)
end

defp do_tokenize({:text, text, meta}, {tokens, cont}, source) do
text = List.to_string(text)
meta = [line: meta.line, column: meta.column]
state = Tokenizer.init(0, "nofile", source, Phoenix.LiveView.HTMLEngine)
Tokenizer.tokenize(text, meta, tokens, cont, state)
end

defp do_tokenize({:comment, text, meta}, {tokens, cont}, _contents) do
{[{:eex_comment, List.to_string(text), meta} | tokens], cont}
end
defp tokenize(source) do
{:ok, eex_nodes} = EEx.tokenize(source)
{tokens, cont} = Enum.reduce(eex_nodes, {[], :text}, &do_tokenize(&1, &2, source))
Tokenizer.finalize(tokens, "nofile", cont, source)
end

defp do_tokenize({type, opt, expr, %{column: column, line: line}}, {tokens, cont}, _contents)
when type in @eex_expr do
meta = %{opt: opt, line: line, column: column}
{[{:eex, type, expr |> List.to_string() |> String.trim(), meta} | tokens], cont}
end
else
defp tokenize(source) do
{:ok, eex_nodes} = EEx.Tokenizer.tokenize(source, 1, 1, %{indentation: 0, trim: false})
{tokens, cont} = Enum.reduce(eex_nodes, {[], :text}, &do_tokenize(&1, &2, source))
Tokenizer.finalize(tokens, "nofile", cont, source)
end
defp do_tokenize({:text, text, meta}, {tokens, cont}, source) do
text = List.to_string(text)
meta = [line: meta.line, column: meta.column]
state = Tokenizer.init(0, "nofile", source, Phoenix.LiveView.HTMLEngine)
Tokenizer.tokenize(text, meta, tokens, cont, state)
end

defp do_tokenize({:text, line, column, text}, {tokens, cont}, source) do
text = List.to_string(text)
meta = [line: line, column: column]
state = Tokenizer.init(0, "nofile", source, Phoenix.LiveView.HTMLEngine)
Tokenizer.tokenize(text, meta, tokens, cont, state)
end
defp do_tokenize({:comment, text, meta}, {tokens, cont}, _contents) do
{[{:eex_comment, List.to_string(text), meta} | tokens], cont}
end

defp do_tokenize({type, line, column, opt, expr}, {tokens, cont}, _contents)
when type in @eex_expr do
meta = %{opt: opt, line: line, column: column}
{[{:eex, type, expr |> List.to_string() |> String.trim(), meta} | tokens], cont}
end
defp do_tokenize({type, opt, expr, %{column: column, line: line}}, {tokens, cont}, _contents)
when type in @eex_expr do
meta = %{opt: opt, line: line, column: column}
{[{:eex, type, expr |> List.to_string() |> String.trim(), meta} | tokens], cont}
end

defp do_tokenize(_node, acc, _contents) do
Expand Down
22 changes: 4 additions & 18 deletions lib/phoenix_live_view/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,9 @@ defmodule Phoenix.LiveView.Router do

"""
defmacro live(path, live_view, action \\ nil, opts \\ []) do
# TODO: Use Macro.expand_literals on Elixir v1.14.1+
live_view =
if Macro.quoted_literal?(live_view) do
Macro.prewalk(live_view, &expand_alias(&1, __CALLER__))
else
live_view
end
live_view = Macro.expand_literals(live_view, __CALLER__)
action = Macro.expand_literals(action, __CALLER__)
opts = Macro.expand_literals(opts, __CALLER__)

quote bind_quoted: binding() do
{action, router_options} =
Expand Down Expand Up @@ -235,12 +231,7 @@ defmodule Phoenix.LiveView.Router do
and be executed via `on_mount` hooks.
"""
defmacro live_session(name, opts \\ [], do: block) do
opts =
if Macro.quoted_literal?(opts) do
Macro.prewalk(opts, &expand_alias(&1, __CALLER__))
else
opts
end
opts = Macro.expand_literals(opts, __CALLER__)

quote do
unquote(__MODULE__).__live_session__(__MODULE__, unquote(opts), unquote(name))
Expand All @@ -249,11 +240,6 @@ defmodule Phoenix.LiveView.Router do
end
end

defp expand_alias({:__aliases__, _, _} = alias, env),
do: Macro.expand(alias, %{env | function: {:mount, 3}})

defp expand_alias(other, _env), do: other

@doc false
def __live_session__(module, opts, name) do
Module.register_attribute(module, :phoenix_live_sessions, accumulate: true)
Expand Down
20 changes: 7 additions & 13 deletions lib/phoenix_live_view/tag_engine.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1242,19 +1242,13 @@ defmodule Phoenix.LiveView.TagEngine do
# warn if using name="id" on an input
case Enum.find(attrs, &match?({"name", {:string, "id", _}, _}, &1)) do
{_name, _value, attr_meta} ->
# TODO: Remove conditional once we require Elixir v1.14+
meta =
if Version.match?(System.version(), ">= 1.14.0") do
[
line: attr_meta.line,
column: attr_meta.column,
file: state.file,
module: state.caller.module,
function: state.caller.function
]
else
Macro.Env.stacktrace(%{state.caller | line: attr_meta.line})
end
meta = [
line: attr_meta.line,
column: attr_meta.column,
file: state.file,
module: state.caller.module,
function: state.caller.function
]

IO.warn(
"""
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule Phoenix.LiveView.MixProject do
[
app: :phoenix_live_view,
version: @version,
elixir: "~> 1.13",
elixir: "~> 1.14.1 or ~> 1.15",
start_permanent: Mix.env() == :prod,
elixirc_paths: elixirc_paths(Mix.env()),
test_options: [docs: true],
Expand Down
Loading
Loading