Class: PhlexKit::Generators::ComponentGenerator

Inherits:
Rails::Generators::NamedBase
  • Object
show all
Defined in:
lib/generators/phlex_kit/component/component_generator.rb

Overview

rails g phlex_kit:component button — the shadcn-style "eject": copies a component's folder (.rb parts + co-located .css + _controller.js) out of the gem and into the host app so the team owns and can edit the source. Appends its @import to application.css and wires config/application.rb (once) so the ejected copy fully shadows the gem's — Ruby, CSS, and JS alike.

Constant Summary collapse

COLLAPSE_LINE =

Marker that wire_host_app is idempotent against — presence means an earlier eject already wired config/application.rb.

%(Rails.autoloaders.main.collapse(Rails.root.join("app/components/phlex_kit/*")))

Instance Method Summary collapse

Instance Method Details

#confirmObject



68
69
70
# File 'lib/generators/phlex_kit/component/component_generator.rb', line 68

def confirm
  say_status :phlex_kit, "ejected phlex_kit/#{file_name} — your copy now shadows the gem (Ruby, CSS, JS)", :green
end

#eject_componentObject



19
20
21
22
23
24
25
# File 'lib/generators/phlex_kit/component/component_generator.rb', line 19

def eject_component
  unless File.directory?(File.join(self.class.source_root, file_name))
    say_status :error, "no PhlexKit component named '#{file_name}'", :red
    return
  end
  directory file_name, "app/components/phlex_kit/#{file_name}"
end

#wire_host_appObject

Make an ejected component fully editable, in one step. Two things are needed and neither is on by default in a host app:

1. Zeitwerk collapse — so app/components/phlex_kit/button/button.rb
 autoloads as PhlexKit::Button (not ::Button::Button). Fixes Ruby.
2. Host app/components ahead of the gem on Propshaft's asset path — so
 the ejected <name>.css and <name>_controller.js resolve to the host
 copy instead of the gem's. Without this the Ruby shadows but the CSS
 and JS silently keep serving the gem's originals.

Injected once (idempotent) right after config.load_defaults, where config.assets is available.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/generators/phlex_kit/component/component_generator.rb', line 44

def wire_host_app
  app_rb = "config/application.rb"
  unless File.exist?(app_rb)
    say_status :skip, "#{app_rb} not found — wire ejected components manually (see docs/05-PROPSHAFT-INSTALL.md)", :yellow
    return
  end
  if File.read(app_rb).include?(COLLAPSE_LINE)
    say_status :identical, "#{app_rb} already wired for ejected components", :blue
    return
  end
  inject_into_file app_rb, after: /^\s*config\.load_defaults.*\n/ do
    # Indent only non-empty lines so the blank separator carries no trailing space.
    <<~RUBY.gsub(/^(?=.)/, "    ")

      # PhlexKit: make ejected components under app/components/phlex_kit/
      # fully editable — collapse so <name>/<name>.rb autoloads as
      # PhlexKit::<Name>, and put the dir ahead of the gem on the asset path
      # so the ejected <name>.css and <name>_controller.js shadow the gem's.
      #{COLLAPSE_LINE}
      config.assets.paths.unshift(Rails.root.join("app/components"))
    RUBY
  end
end

#wire_importObject



27
28
29
30
31
32
# File 'lib/generators/phlex_kit/component/component_generator.rb', line 27

def wire_import
  css = "app/assets/stylesheets/application.css"
  line = %(@import url("phlex_kit/#{file_name}/#{file_name}.css");\n)
  return unless File.exist?(css)
  prepend_to_file(css, line) unless File.read(css).include?(line.strip)
end