-
-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathplugin_test.exs
More file actions
281 lines (228 loc) · 8.4 KB
/
Copy pathplugin_test.exs
File metadata and controls
281 lines (228 loc) · 8.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
defmodule Volt.PluginTest do
use ExUnit.Case, async: true
defmodule UppercasePlugin do
@behaviour Volt.Plugin
@impl true
def name, do: "uppercase"
@impl true
def transform(code, _path) do
{:ok, String.upcase(code)}
end
end
defmodule VirtualPlugin do
@behaviour Volt.Plugin
@impl true
def name, do: "virtual"
@impl true
def resolve("virtual:config", _importer), do: {:ok, "virtual:config"}
def resolve(_, _), do: nil
@impl true
def load("virtual:config"), do: {:ok, "export default {debug: true};\n"}
def load(_), do: nil
end
describe "PluginRunner.transform/3" do
test "pipes code through transform hooks" do
result = Volt.PluginRunner.transform([UppercasePlugin], "hello", "test.js")
assert result == "HELLO"
end
test "skips plugins without transform" do
result = Volt.PluginRunner.transform([VirtualPlugin], "hello", "test.js")
assert result == "hello"
end
test "chains multiple transforms" do
defmodule PrefixPlugin do
@behaviour Volt.Plugin
def name, do: "prefix"
def transform(code, _path), do: {:ok, "/* volt */\n" <> code}
end
result =
Volt.PluginRunner.transform([PrefixPlugin, UppercasePlugin], "hello", "test.js")
assert result == "/* VOLT */\nHELLO"
end
end
describe "PluginRunner.define/2" do
test "collects plugin-provided defines" do
defmodule DefinePlugin do
@behaviour Volt.Plugin
def name, do: "define"
def define(mode), do: %{"import.meta.env.CUSTOM_MODE" => Jason.encode!(mode)}
end
assert Volt.PluginRunner.define([DefinePlugin], "production") == %{
"__VUE_OPTIONS_API__" => "true",
"__VUE_PROD_DEVTOOLS__" => "false",
"__VUE_PROD_HYDRATION_MISMATCH_DETAILS__" => "false",
"import.meta.env.CUSTOM_MODE" => ~s("production")
}
end
test "passes tuple options to define callbacks" do
defmodule ConfiguredDefinePlugin do
@behaviour Volt.Plugin
def name, do: "configured-define"
def define(_mode, opts), do: Keyword.fetch!(opts, :define)
end
assert Volt.PluginRunner.define(
[{ConfiguredDefinePlugin, define: %{"APP" => "true"}}],
"test"
)[
"APP"
] == "true"
end
end
describe "PluginRunner.resolve/3" do
test "resolves via plugin" do
assert {:ok, "virtual:config"} =
Volt.PluginRunner.resolve([VirtualPlugin], "virtual:config", nil)
end
test "returns nil when no plugin matches" do
assert nil == Volt.PluginRunner.resolve([VirtualPlugin], "vue", nil)
end
end
describe "PluginRunner.load/2" do
test "loads virtual module content" do
assert {:ok, code} = Volt.PluginRunner.load([VirtualPlugin], "virtual:config")
assert code =~ "debug"
end
test "returns nil for unhandled paths" do
assert nil == Volt.PluginRunner.load([VirtualPlugin], "other.js")
end
end
describe "PluginRunner.extensions/2" do
test "passes tuple options to plugins with arity-aware callbacks" do
defmodule ConfiguredExtensionsPlugin do
@behaviour Volt.Plugin
def name, do: "configured-extensions"
def extensions(:compile, opts), do: Keyword.fetch!(opts, :extensions)
def extensions(_, _opts), do: []
end
assert ".widget" in Volt.PluginRunner.extensions(
[{ConfiguredExtensionsPlugin, extensions: [".widget"]}],
:compile
)
end
test "includes built-in Vue extensions and custom plugin extensions" do
defmodule SfcPlugin do
@behaviour Volt.Plugin
def name, do: "sfc"
def extensions(:compile), do: [".sfc"]
def extensions(_), do: []
end
assert ".vue" in Volt.PluginRunner.extensions([], :compile)
assert ".svelte" in Volt.PluginRunner.extensions([], :compile)
assert ".sfc" in Volt.PluginRunner.extensions([SfcPlugin], :compile)
end
end
describe "Volt.Plugin.Svelte" do
@tag :integration
test "accepts plugin compiler options" do
assert {:ok, %{code: code, warnings: warnings}} =
Volt.Plugin.Svelte.compile("App.svelte", "<h1>Hello</h1>", [], generate: :server)
assert code =~ "svelte/internal/server"
assert is_list(warnings)
end
test "extracts imports from script blocks" do
source = """
<script module>
import config from './config'
</script>
<script lang="ts">
import Child from './Child.svelte'
import { format } from '../format'
</script>
<h1>{format(config.title)}</h1>
"""
assert {:ok, %{imports: imports, workers: []}} =
Volt.Plugin.Svelte.extract_imports("App.svelte", source, [])
assert {:static, "./config"} in imports
assert {:static, "./Child.svelte"} in imports
assert {:static, "../format"} in imports
end
end
describe "Volt.Plugin.Solid" do
@tag :integration
test "compiles Solid TSX through the Solid compiler" do
source = """
type Props = { name: string }
export function App(props: Props) {
return <h1>Hello {props.name}</h1>
}
"""
assert {:ok, %{code: code, sourcemap: sourcemap}} =
Volt.Plugin.Solid.compile("App.tsx", source, [])
assert code =~ "solid-js/web"
assert code =~ "template"
refute code =~ "jsx-runtime"
assert is_binary(sourcemap)
end
test "adds Solid compiler runtime imports during import extraction" do
source = """
import { createSignal } from 'solid-js'
const [count] = createSignal(0)
export const App = () => <button>{count()}</button>
"""
assert {:ok, %{imports: imports, workers: []}} =
Volt.Plugin.Solid.extract_imports("App.tsx", source, [])
assert {:static, "solid-js"} in imports
assert {:static, "solid-js/web"} in imports
end
@tag :integration
test "removes imports that are only used as TypeScript types" do
source = """
import { createSignal } from 'solid-js'
import { Label } from './types'
type Props = { label: Label }
export function App(props: Props) {
const [count] = createSignal(0)
return <button>{props.label.text} {count()}</button>
}
"""
assert {:ok, %{code: code}} = Volt.Plugin.Solid.compile("App.tsx", source, [])
refute code =~ "./types"
assert code =~ "solid-js"
end
test "extracts require imports and worker specifiers" do
source = """
import { render } from 'solid-js/web'
const helper = require('./helper')
const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' })
console.log(worker)
export const App = () => <button>{helper.label}</button>
"""
assert {:ok, %{imports: imports, workers: workers}} =
Volt.Plugin.Solid.extract_imports("App.tsx", source, [])
assert {:static, "./helper"} in imports
assert {:static, "solid-js/web"} in imports
assert "./worker.ts" in workers
end
@tag :integration
test "drops sourcemap when target downleveling rewrites compiled output" do
source = """
export const App = () => <button>{globalThis.value ?? 'fallback'}</button>
"""
assert {:ok, %{code: code, sourcemap: nil}} =
Volt.Plugin.Solid.compile("App.tsx", source, target: :es2019)
refute code =~ "??"
end
end
describe "Pipeline integration" do
test "plugins can compile custom file types" do
defmodule CustomCompilerPlugin do
@behaviour Volt.Plugin
def name, do: "custom-compiler"
def extensions(:compile), do: [".custom"]
def extensions(_), do: []
def compile("component.custom", source, _opts) do
{:ok,
%{code: "export default #{inspect(source)}", sourcemap: nil, css: nil, hashes: nil}}
end
def compile(_, _, _), do: nil
end
assert {:ok, %{code: ~s(export default "hello")}} =
Volt.Pipeline.compile("component.custom", "hello", plugins: [CustomCompilerPlugin])
end
test "plugins receive compiled output" do
{:ok, result} =
Volt.Pipeline.compile("app.ts", "const x: number = 42", plugins: [UppercasePlugin])
assert result.code == String.upcase(result.code)
end
end
end