Module: RubyUIAdmin::TailwindSource

Defined in:
lib/ruby_ui_admin/tailwind_source.rb

Overview

Extracts the Tailwind class candidates used by the admin's Phlex views into a single file the host app can @source from its Tailwind build. This solves the "gem path changes per environment" problem: instead of pointing @source at the gem's .rb files (whose absolute path differs between dev/CI/deploy), the host writes the class inventory into its OWN tree (a stable, relative path) and re-runs on upgrade. It scans the same literals Tailwind would, so the generated utilities match the previous @source "<gem>/**/*.rb" approach.

Driven by rake ruby_ui_admin:tailwind_source.

Constant Summary collapse

VIEWS_GLOB =
"app/components/ruby_ui_admin/**/*.rb"

Class Method Summary collapse

Class Method Details

.class_candidatesObject

All class candidates across the engine's view files, deduped and sorted.



20
21
22
23
24
# File 'lib/ruby_ui_admin/tailwind_source.rb', line 20

def class_candidates
  Dir[RubyUIAdmin::Engine.root.join(VIEWS_GLOB)].sort
    .flat_map { |file| extract(File.read(file)) }
    .uniq.sort
end

.class_token?(token) ⇒ Boolean

Heuristic: a Tailwind utility candidate. Must start with a letter (optionally !/- for important/negative) and may carry variants/values (hover:, /10, [356px]). Requiring a letter start drops embedded SVG path coordinates (e.g. 0-7.07l-1.72), which start with a digit and would otherwise be scanned as junk — no real Tailwind utility starts with a digit.

Returns:

  • (Boolean)


46
47
48
# File 'lib/ruby_ui_admin/tailwind_source.rb', line 46

def class_token?(token)
  token.match?(%r{\A!?-?[a-z][a-z0-9:_/\[\]().,%#-]*\z})
end

.extract(ruby_source) ⇒ Object

Pulls Tailwind-ish class tokens out of a chunk of Ruby source: every double-quoted string literal (and heredoc CLASSES body) is split into tokens, with #{…} interpolations dropped so the literal fragments around them survive. Non-class words are filtered out by class_token?. Only double quotes are scanned — the views write every class: with them, and scanning single quotes would let stray apostrophes in comments span across real code.



31
32
33
34
35
36
37
38
39
40
# File 'lib/ruby_ui_admin/tailwind_source.rb', line 31

def extract(ruby_source)
  strings = ruby_source.scan(/"([^"]*)"/).flatten
  strings += ruby_source.scan(/<<~?CLASSES\n(.*?)\n\s*CLASSES/m).flatten

  strings
    .map { |string| string.gsub(/#\{[^}]*\}/, " ") }
    .flat_map { |string| string.split(/\s+/) }
    .select { |token| class_token?(token) }
    .uniq
end

.htmlObject

The scannable file body (valid HTML so any Tailwind extractor picks it up).



51
52
53
54
55
56
# File 'lib/ruby_ui_admin/tailwind_source.rb', line 51

def html
  <<~HTML
    <!-- Generated by `rake ruby_ui_admin:tailwind_source`. Do not edit — re-run after upgrading ruby_ui_admin. -->
    <div class="#{class_candidates.join(" ")}"></div>
  HTML
end

.write(dest) ⇒ Object

Writes the inventory to dest and returns the path.



59
60
61
62
63
# File 'lib/ruby_ui_admin/tailwind_source.rb', line 59

def write(dest)
  FileUtils.mkdir_p(File.dirname(dest))
  File.write(dest, html)
  dest
end