Class: JsxRosetta::Backend::Phlex

Inherits:
Base
  • Object
show all
Defined in:
lib/jsx_rosetta/backend/phlex.rb

Overview

Emits a Phlex 2.x view class (one Ruby file per component) from an IR::Component. Single-file output by design — the JSX ‘<h1>…` template lives as Ruby inside `view_template`, not in a sibling .erb. When the source uses `onClick`/`onChange` etc., a sibling Stimulus controller `_controller.js` is emitted alongside (same convention as the ViewComponent backend).

Naming strategies (mutually exclusive):

default                  class FlashyHeader < Phlex::HTML
suffix: "Component"      class FlashyHeaderComponent < Phlex::HTML
namespace: "Components"  module Components
                           class FlashyHeader < Phlex::HTML

Hyphenated attributes (‘data-testid`, `aria-label`, etc.) emit as string-keyed hash entries inside a splat — `**{ “data-testid” => @x }` — since Ruby kwargs can’t carry hyphens. Snake_case-friendly attrs emit as regular keyword arguments.

Defined Under Namespace

Classes: EventDescriptor

Constant Summary collapse

DEFAULT_SLOT_NAME =
"children"
DEFAULT_SUFFIX =
"Component"
PHLEX_BASE_CLASS =
"Phlex::HTML"
VALID_IDENTIFIER =
/\A[a-z_][a-z0-9_]*\z/i
VOID_ELEMENTS =
%w[area base br col embed hr img input link meta param source track wbr].freeze

Instance Method Summary collapse

Constructor Details

#initialize(suffix: nil, namespace: nil) ⇒ Phlex

Returns a new instance of Phlex.

Raises:

  • (ArgumentError)


38
39
40
41
42
43
44
# File 'lib/jsx_rosetta/backend/phlex.rb', line 38

def initialize(suffix: nil, namespace: nil)
  super()
  raise ArgumentError, "Phlex backend: pass either suffix: or namespace:, not both" if suffix && namespace

  @suffix = suffix.is_a?(String) ? suffix : (DEFAULT_SUFFIX if suffix == true)
  @namespace = namespace
end

Instance Method Details

#emit(component) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/jsx_rosetta/backend/phlex.rb', line 46

def emit(component)
  prop_names = component.props.map(&:name)
  prop_names << component.rest_prop_name if component.rest_prop_name
  translator = ViewComponent::ExpressionTranslator.new(
    prop_names: prop_names, local_binding_names: component.local_binding_names
  )

  @stimulus_identifier = component.stimulus_methods.any? ? stimulus_identifier(component) : nil
  @lambda_methods = []
  @lambda_method_counts = {}

  files = [File.new(path: ruby_path(component), contents: render_ruby_class(component, translator))]
  if component.stimulus_methods.any?
    files << File.new(
      path: stimulus_path(component),
      contents: render_stimulus_controller_js(component)
    )
  end
  files
end