Skip to content

Instantly share code, notes, and snippets.

@RomanTurner
Last active January 30, 2023 15:12
Show Gist options
  • Save RomanTurner/159ba3b21c6f81b8d76c9fff31389eb9 to your computer and use it in GitHub Desktop.
Save RomanTurner/159ba3b21c6f81b8d76c9fff31389eb9 to your computer and use it in GitHub Desktop.
Tag helpers for ViteRuby using the Phlex component gem.
module ViteRephlex
module PhlexHelpers
class OohBabyILikeItRaw < Phlex::HTML
def initialize(tag:)
@tag = tag
end
def template
@tag.is_a?(Array) ? @tag.each { |t| unsafe_raw(t) } : unsafe_raw(@tag)
end
end
class Script < Phlex::HTML
def initialize(src:, type: "module", async: false, crossorigin: false)
@src, @type, @async, @crossorigin = src, type, async, crossorigin
end
def template
script(src: @src, type: @type, async: @async, crossorigin: @crossorigin)
end
end
class Link < Phlex::HTML
def initialize(rel: "stylesheet", href: "main.css", **options)
@rel, @href, @options = rel, href, options
end
def template
link(rel: @rel, href: @href, **@options)
end
end
#end of PhlexHelpers
end
# Public: Renders a script tag for vite/client to enable HMR in development.
def vite_client_tag
return unless src = vite_manifest.vite_client_src
PhlexHelpers::Script.new(src: src)
end
# Public: Renders a script tag to enable HMR with React Refresh.
def vite_react_refresh
tag = vite_manifest.react_refresh_preamble
PhlexHelpers::OohBabyILikeItRaw.new(tag) if tag
end
# Public: Resolves the path for the specified Vite asset.
# Example:
# <%= vite_asset_path 'calendar.css' %> # => "/vite/assets/calendar-1016838bab065ae1e122.css"
def vite_asset_path(name, **options)
"/vite/assets/" + vite_manifest.path_for(name, **options)
end
# Public: Renders a ViteRephlex::PhlexHelpers::Script component.
# When called it will render a <script> tag for the specified Vite entrypoints.
def vite_javascript_tag(
*names,
type: "module",
asset_type: :javascript,
skip_preload_tags: false,
skip_style_tags: false,
crossorigin: "anonymous",
**options
)
entries = vite_manifest.resolve_entries(*names, type: asset_type)
tags =
entries
.fetch(:scripts)
.map do |src|
PhlexHelpers::Script.new(
src: src,
crossorigin: crossorigin,
type: type
).call
end
unless skip_preload_tags
tags.concat(
vite_preload_tag(*entries.fetch(:imports), crossorigin: crossorigin)
)
end
unless skip_style_tags
tags.concat(vite_js_stylesheet_tag(*entries.fetch(:stylesheets)))
end
PhlexHelpers::OohBabyILikeItRaw.new(tag: tags)
end
# Public: Renders a ViteRephlex::PhlexHelpers::Script component.
# When called it will render a <script> tag for the specified Vite entrypoints.
def vite_typescript_tag(*names, **options)
vite_javascript_tag(*names, asset_type: :typescript, **options)
end
# Public: Renders a ViteRephlex::PhlexHelpers::Link component.
# When called it will render a <link> tag for the specified Vite entrypoints.
def vite_stylesheet_tag(*names, **options)
tags =
names.map do |name|
PhlexHelpers::Link.new(
href: vite_asset_path(name, type: :stylesheet),
**options
).call
end
PhlexHelpers::OohBabyILikeItRaw.new(tag: tags)
end
private
# Needed to go around the javascript + stylesheet tag in one.
def vite_js_stylesheet_tag(*names, **options)
tags =
names.map do |name|
PhlexHelpers::Link.new(
href: vite_asset_path(name, type: :stylesheet),
**options
).call
end
end
# Internal: Returns the current manifest loaded by Vite Ruby.
def vite_manifest
ViteRuby.instance.manifest
end
# Internal: Renders a modulepreload ViteRephlex::PhlexHelpers::Link component.
# When called it will return a moduleprelode <link> tag.
def vite_preload_tag(*sources, crossorigin:)
sources.map do |source|
href = asset_path(source)
try(:request).try(
:send_early_hints,
"Link" =>
%(<#{href}>; rel=modulepreload; as=script; crossorigin=#{crossorigin})
)
PhlexHelpers::Link.new(
rel: "modulepreload",
href: href,
crossorigin: crossorigin,
as: "script"
).call
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment