Skip to content

Commit e2f9c02

Browse files
authored
allow to omit name for colocated js (#3860)
1 parent 566e10e commit e2f9c02

File tree

2 files changed

+88
-16
lines changed

2 files changed

+88
-16
lines changed

lib/phoenix_live_view/colocated_js.ex

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,14 @@ defmodule Phoenix.LiveView.ColocatedJS do
128128
The supported attributes are:
129129
130130
* `name` - The name under which the default export of the script is available when importing
131-
the manifest. This is required, even if you don't plan to access the exported values or the
132-
script does not actually export anything.
131+
the manifest. If omitted, the file will be imported for side effects only.
133132
134133
* `key` - A custom key to use for the export. This is used by `Phoenix.LiveView.ColocatedHook` to
135134
export all hooks under the named `hooks` export (`export { ... as hooks }`).
136135
For example, you could set this to `web_components` for each colocated script that defines
137136
a web component and then import all of them as `import { web_components } from "phoenix-colocated/my_app"`.
138137
Defaults to `:default`, which means the export will be available under the manifest's `default` export.
139-
This needs to be a valid JavaScript identifier.
138+
This needs to be a valid JavaScript identifier. When given, a `name` is required as well.
140139
141140
* `extension` - a custom extension to use when writing the extracted file. The default is `js`.
142141
@@ -173,8 +172,12 @@ defmodule Phoenix.LiveView.ColocatedJS do
173172
raise ArgumentError,
174173
"the name attribute of a colocated script must be a compile-time string. Got: #{Macro.to_string(name)}"
175174

175+
%{"key" => _} ->
176+
raise ArgumentError,
177+
"a name is required when a key is given"
178+
176179
_ ->
177-
raise ArgumentError, "missing required name attribute for ColocatedJS"
180+
:ok
178181
end
179182
end
180183

@@ -191,7 +194,7 @@ defmodule Phoenix.LiveView.ColocatedJS do
191194
|> maybe_put_opt(opts, "manifest", :manifest)
192195

193196
hashed_name =
194-
filename_opts.name
197+
(filename_opts.name || text_content)
195198
|> then(&:crypto.hash(:md5, &1))
196199
|> Base.encode32(case: :lower, padding: false)
197200

@@ -304,13 +307,17 @@ defmodule Phoenix.LiveView.ColocatedJS do
304307
{:default, entries} ->
305308
[
306309
acc,
307-
Enum.map(entries, fn {file, %{name: name}} ->
308-
import_name =
309-
"js_" <> Base.encode32(:crypto.hash(:md5, file), case: :lower, padding: false)
310+
Enum.map(entries, fn
311+
{file, %{name: nil}} ->
312+
~s[import "./#{Path.relative_to(file, target_dir)}";\n]
310313

311-
escaped_name = Phoenix.HTML.javascript_escape(name)
314+
{file, %{name: name}} ->
315+
import_name =
316+
"js_" <> Base.encode32(:crypto.hash(:md5, file), case: :lower, padding: false)
312317

313-
~s<import #{import_name} from "./#{Path.relative_to(file, target_dir)}"; js["#{escaped_name}"] = #{import_name};\n>
318+
escaped_name = Phoenix.HTML.javascript_escape(name)
319+
320+
~s<import #{import_name} from "./#{Path.relative_to(file, target_dir)}"; js["#{escaped_name}"] = #{import_name};\n>
314321
end)
315322
]
316323

@@ -320,13 +327,17 @@ defmodule Phoenix.LiveView.ColocatedJS do
320327
[
321328
acc,
322329
~s<const #{tmp_name} = {}; export { #{tmp_name} as #{key} };\n>,
323-
Enum.map(entries, fn {file, %{name: name}} ->
324-
import_name =
325-
"js_" <> Base.encode32(:crypto.hash(:md5, file), case: :lower, padding: false)
330+
Enum.map(entries, fn
331+
{file, %{name: nil}} ->
332+
~s[import "./#{Path.relative_to(file, target_dir)}";\n]
333+
334+
{file, %{name: name}} ->
335+
import_name =
336+
"js_" <> Base.encode32(:crypto.hash(:md5, file), case: :lower, padding: false)
326337

327-
escaped_name = Phoenix.HTML.javascript_escape(name)
338+
escaped_name = Phoenix.HTML.javascript_escape(name)
328339

329-
~s<import #{import_name} from "./#{Path.relative_to(file, target_dir)}"; #{tmp_name}["#{escaped_name}"] = #{import_name};\n>
340+
~s<import #{import_name} from "./#{Path.relative_to(file, target_dir)}"; #{tmp_name}["#{escaped_name}"] = #{import_name};\n>
330341
end)
331342
]
332343
end

test/phoenix_live_view/colocated_js_test.exs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ defmodule Phoenix.LiveView.ColocatedJSTest do
2424

2525
assert folder =
2626
Enum.find(module_folders, fn folder ->
27-
folder =~ ~r/#{inspect(__MODULE__)}\.TestComponent/
27+
folder =~ ~r/#{inspect(__MODULE__)}\.TestComponent$/
2828
end)
2929

3030
assert [script] =
@@ -132,6 +132,67 @@ defmodule Phoenix.LiveView.ColocatedJSTest do
132132
:code.purge(__MODULE__.TestComponentKey)
133133
end
134134

135+
test "nameless script is imported for side effects only" do
136+
defmodule TestComponentSideEffects do
137+
use Phoenix.Component
138+
alias Phoenix.LiveView.ColocatedJS, as: Colo
139+
140+
def fun(assigns) do
141+
~H"""
142+
<script :type={Colo}>
143+
console.log("hey!");
144+
</script>
145+
"""
146+
end
147+
end
148+
149+
assert module_folders =
150+
File.ls!(Path.join(Mix.Project.build_path(), "phoenix-colocated/phoenix_live_view"))
151+
152+
assert folder =
153+
Enum.find(module_folders, fn folder ->
154+
folder =~ ~r/#{inspect(__MODULE__)}\.TestComponentSideEffects/
155+
end)
156+
157+
assert [script] =
158+
Path.wildcard(
159+
Path.join(
160+
Mix.Project.build_path(),
161+
"phoenix-colocated/phoenix_live_view/#{folder}/*.js"
162+
)
163+
)
164+
165+
assert File.read!(script) == """
166+
167+
console.log("hey!");
168+
"""
169+
170+
# now write the manifest manually as we are in a test
171+
Phoenix.LiveView.ColocatedJS.compile()
172+
173+
relative_script_path =
174+
Path.relative_to(
175+
script,
176+
Path.join(Mix.Project.build_path(), "phoenix-colocated/phoenix_live_view/")
177+
)
178+
179+
assert manifest =
180+
File.read!(
181+
Path.join(Mix.Project.build_path(), "phoenix-colocated/phoenix_live_view/index.js")
182+
)
183+
184+
assert line =
185+
Enum.find(String.split(manifest, "\n"), fn line ->
186+
line =~ inspect(__MODULE__.TestComponentSideEffects)
187+
end)
188+
189+
assert [_match] =
190+
Regex.run(~r/import "\.\/#{Regex.escape(relative_script_path)}";/, line)
191+
after
192+
:code.delete(__MODULE__.TestComponentSideEffects)
193+
:code.purge(__MODULE__.TestComponentSideEffects)
194+
end
195+
135196
test "raises for invalid name" do
136197
assert_raise Phoenix.LiveView.Tokenizer.ParseError,
137198
~r/the name attribute of a colocated script must be a compile-time string\. Got: @foo/,

0 commit comments

Comments
 (0)