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

mix lvn.setup #167

Merged
merged 1 commit into from
Apr 16, 2024
Merged
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
85 changes: 50 additions & 35 deletions lib/live_view_native/component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -121,54 +121,69 @@ defmodule LiveViewNative.Component do

declarative_opts = Keyword.drop(opts, [:as, :format, :root])

case LiveViewNative.fetch_plugin(format) do
component_ast = quote do
import Phoenix.LiveView.Helpers
import Kernel, except: [def: 2, defp: 2]
import Phoenix.Component, except: [
embed_templates: 1, embed_templates: 2,
sigitl_H: 2,

async_result: 1,
dynamic_tag: 1,
focus_wrap: 1,
form: 1,
inputs_for: 1,
intersperse: 1,
link: 1,
live_component: 1,
live_file_input: 1,
live_img_preview: 1,
live_title: 1
]
import Phoenix.Component.Declarative
require Phoenix.Template

for {prefix_match, value} <- Phoenix.Component.Declarative.__setup__(__MODULE__, unquote(declarative_opts)) do
@doc false
def __global__?(prefix_match), do: value
end

import LiveViewNative.Renderer, only: [
delegate_to_target: 1,
delegate_to_target: 2,
embed_templates: 1,
embed_templates: 2
]

def __native_opts__, do: @native_opts
end

plugin_component_ast = case LiveViewNative.fetch_plugin(format) do
{:ok, plugin} ->
quote do
import Phoenix.LiveView.Helpers
import Kernel, except: [def: 2, defp: 2]
import Phoenix.Component, except: [
embed_templates: 1, embed_templates: 2,
sigitl_H: 2,

async_result: 1,
dynamic_tag: 1,
focus_wrap: 1,
form: 1,
inputs_for: 1,
intersperse: 1,
link: 1,
live_component: 1,
live_file_input: 1,
live_img_preview: 1,
live_title: 1
]
import Phoenix.Component.Declarative
require Phoenix.Template

for {prefix_match, value} <- Phoenix.Component.Declarative.__setup__(__MODULE__, unquote(declarative_opts)) do
@doc false
def __global__?(prefix_match), do: value
end

use unquote(plugin.component)
import LiveViewNative.Renderer, only: [
delegate_to_target: 1,
delegate_to_target: 2,
embed_templates: 1,
embed_templates: 2
]

if (unquote(opts[:as])) do
@before_compile LiveViewNative.Renderer
end

@before_compile LiveViewNative.Component
end

:error ->
IO.warn("tried to load LiveViewNative plugin for format #{inspect(format)} but none was found")
if is_nil(format) do
quote do
import LiveViewNative.Component, only: [sigil_LVN: 2]
end
else
IO.warn("tried to load LiveViewNative plugin for format #{inspect(format)} but none was found")

[]
end

[]
end

[component_ast, plugin_component_ast]
end

@doc false
Expand Down
15 changes: 12 additions & 3 deletions lib/mix/live_view_native/context.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
defmodule Mix.LiveViewNative.Context do
@moduledoc false

defstruct context_app: nil,
base_module: nil,
schema_module: nil,
Expand All @@ -16,14 +18,14 @@ defmodule Mix.LiveViewNative.Context do
|> caller.validate_args!()
|> parse_args()

ctx_app = parsed_opts[:context_app] || Mix.Phoenix.context_app()
base_module = Module.concat([Mix.Phoenix.context_base(ctx_app)])
context_app = parsed_opts[:context_app] || Mix.Phoenix.context_app()
base_module = Module.concat([Mix.Phoenix.context_base(context_app)])
native_module = Module.concat([inspect(base_module) <> "Native"])
web_module = Mix.Phoenix.web_module(base_module)
native_path = Path.join(["native", Atom.to_string(format)])

%__MODULE__{
context_app: ctx_app,
context_app: context_app,
base_module: base_module,
schema_module: schema_module,
native_module: native_module,
Expand Down Expand Up @@ -112,4 +114,11 @@ defmodule Mix.LiveViewNative.Context do
defp put_context_app(opts, string) do
Keyword.put(opts, :context_app, String.to_atom(string))
end

defmacro compile_string(string) do
EEx.compile_string(string)
end

def last?(plugins, plugin),
do: Enum.at(plugins, -1) == plugin
end
93 changes: 39 additions & 54 deletions lib/mix/tasks/lvn.gen.ex
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
defmodule Mix.Tasks.Lvn.Gen do
alias Mix.LiveViewNative.Context
import Mix.LiveViewNative.Context, only: [
compile_string: 1,
last?: 2
]

def run(args) do
context = Context.build(args, __MODULE__)

files = files_to_be_generated(context)
if Keyword.get(context.opts, :copy, true) do
files = files_to_be_generated(context)
Context.prompt_for_conflicts(files)

Context.prompt_for_conflicts(files)
copy_new_files(context, files)
end

context
|> copy_new_files(files)
|> print_shell_instructions()
if Keyword.get(context.opts, :info, true) do
print_shell_instructions(context)
end
end

def print_shell_instructions(context) do
Expand All @@ -19,15 +26,6 @@ defmodule Mix.Tasks.Lvn.Gen do
|> print_config()
|> print_router()
|> print_endpoint()
|> print_post_instructions()
end

def last?(plugins, plugin) do
Enum.at(plugins, -1) == plugin
end

defmacro compile_string(string) do
EEx.compile_string(string)
end

def print_instructions(context) do
Expand All @@ -48,51 +46,37 @@ defmodule Mix.Tasks.Lvn.Gen do

plugins? = length(plugins) > 0

stylesheet? =
Mix.Project.deps_apps()
|> Enum.member?(:live_view_native_stylesheet)

"""
\e[93;1m# config/config.exs\e[0m

# LVN - registers each available plugin<%= unless plugins? do %>
# \e[91;1mLVN - Required\e[0m
# Registers each available plugin<%= unless plugins? do %>
\e[93;1m# Hint: if you add this config to your app populated with client plugins
# and run `mix lvn.gen` this configuration's placeholders will be populated\e[0m<% end %>
config :live_view_native, plugins: [\e[32;1m<%= if plugins? do %><%= for plugin <- plugins do %>
LiveViewNative.<%= plugin.module_suffix %><%= unless last?(plugins, plugin) do %>,<% end %><% end %><% else %>
# LiveViewNative.SwiftUI<% end %>\e[0m
]

# LVN - Each format must be registered as a mime type
# add to existing configuration if one exists as this will
# overwrite
# \e[91;1mLVN - Required\e[0m
# Each format must be registered as a mime type add to
# existing configuration if one exists as this will overwrite
config :mime, :types, %{\e[32;1m
# if you want to inspect LVN stylesheets from your browser add the `style` type
# "text/styles" => ["styles"],<%= if plugins? do %><%= for plugin <- plugins do %>
"text/<%= plugin.format %>" => ["<%= plugin.format %>"]<%= unless last?(plugins, plugin) do %>,<% end %><% end %><% else %>
# "text/swiftui" => ["swiftui"]<% end %>\e[0m
}

<%= if stylesheet? do %># LVN - Required, you must configure LiveView Native Stylesheets
# on which file path patterns class names should be extracted from
config :live_view_native_stylesheet,
content: [\e[32;1m<%= if plugins? do %><%= for plugin <- plugins do %>
<%= plugin.format %>: [
"lib/**/*<%= plugin.format %>*"
]<%= unless last?(plugins, plugin) do %>,<% end %><% end %><% else %>
# swiftui: ["lib/**/*swiftui*"]<% end %>\e[0m
],
output: "priv/static/assets"
<% end %>
# LVN - Required, you must configure Phoenix to know how
# to encode for the swiftui format
# \e[91;1mLVN - Required\e[0m
# Phoenix must know how to encode each LVN format
config :phoenix_template, :format_encoders, [\e[32;1m<%= if plugins? do %><%= for plugin <- plugins do %>
<%= plugin.format %>: Phoenix.HTML.Engine<%= unless last?(plugins, plugin) do %>,<% end %><% end %><% else %>
# swiftui: Phoenix.HTML.Engine<% end %>\e[0m
]

# LVN - Required, you must configure Phoenix so it knows
# how to compile LVN's neex templates
# \e[91;1mLVN - Required\e[0m
# Phoenix must know how to compile neex templates
config :phoenix, :template_engines, [
\e[32;1mneex: LiveViewNative.Engine\e[0m
]
Expand All @@ -118,15 +102,17 @@ defmodule Mix.Tasks.Lvn.Gen do
))

"""
\e[93;1m# lib/<%= Phoenix.Naming.underscore(context.web_module) %>/router.ex\e[0m
\e[93;1m# lib/<%= Macro.underscore(context.web_module) %>/router.ex\e[0m

# \e[91;1mLVN - Required\e[0m
# add the formats to the `accepts` plug for the pipeline used by your LiveView Native app
plug :accepts, [
"html",\e[32;1m<%= if plugins? do %><%= for plugin <- plugins do %>
"<%= plugin.format %>"<%= unless last?(plugins, plugin) do %>,<% end %><% end %><% else %>
# "swiftui"<% end %>\e[0m
]

# \e[91;1mLVN - Required\e[0m
# add the root layout for each format
plug :put_root_layout, [
html: <%= inspect(layouts[:html]) %>,\e[32;1m<%= if plugins? do %><%= for plugin <- plugins do %>
Expand All @@ -142,9 +128,10 @@ defmodule Mix.Tasks.Lvn.Gen do

def print_endpoint(context) do
"""
\e[93;1m# lib/<%= Phoenix.Naming.underscore(context.web_module) %>/endpoint.ex\e[0m
\e[93;1m# lib/<%= Macro.underscore(context.web_module) %>/endpoint.ex\e[0m

# add the LiveViewNative.LiveRealoder to your endpoint
# \e[36mLVN - Optional\e[0m
# Add the LiveViewNative.LiveRealoder to your endpoint
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
Expand All @@ -159,36 +146,28 @@ defmodule Mix.Tasks.Lvn.Gen do
context
end

def print_post_instructions(context) do
"""
After you have configured your application you should run the following:

* `mix lvn.gen.layout <plaform>`
* `mix lvn.gen.stylsheet <platform> App`
"""
|> Mix.shell().info()

context
end

def switches, do: [
context_app: :string,
web: :string
web: :string,
info: :boolean,
copy: :boolean,
live_form: :boolean
]

def validate_args!([]), do: [nil]
def validate_args!(_args) do
Mix.raise("""
mix lvn.gen does not take any arguments, only the following switches:

--no-live-form
--context-app
--web
""")
end

defp files_to_be_generated(context) do
path = Mix.Phoenix.context_app_path(context.context_app, "lib")
file = Phoenix.Naming.underscore(context.native_module) <> ".ex"
file = Macro.underscore(context.native_module) <> ".ex"

[{:eex, "app_name_native.ex", Path.join(path, file)}]
end
Expand All @@ -200,12 +179,18 @@ defmodule Mix.Tasks.Lvn.Gen do

plugins? = length(plugins) > 0

apps = Mix.Project.deps_apps()

live_form? =
Keyword.get(context.opts, :live_form, true) && Enum.member?(apps, :live_view_native_live_form)

binding = [
context: context,
plugins: plugins,
plugins?: plugins?,
last?: &last?/2,
assigns: %{
live_form?: live_form?,
gettext: true,
formats: formats(),
layouts: layouts(context.web_module)
Expand Down
Loading
Loading