Class: Panda::Core::ModuleRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/panda/core/module_registry.rb

Overview

Module registry for Panda ecosystem components

This class maintains a registry of all Panda modules (CMS, CMS Pro, Community, etc.) and their asset paths. Each module self-registers during engine initialization.

Benefits:

  • Core doesn’t need hardcoded knowledge of other modules

  • Supports private modules (e.g., panda-community in development)

  • Single source of truth for asset compilation

  • Scales automatically to future modules

Usage:

# In module's engine.rb (after class definition):
Panda::Core::ModuleRegistry.register(
  gem_name: 'panda-cms',
  engine: 'Panda::CMS::Engine',
  paths: {
    views: 'app/views/panda/cms/**/*.erb',
    components: 'app/components/panda/cms/**/*.rb',
    stylesheets: 'app/assets/stylesheets/panda/cms/**/*.css'
  }
)

Defined Under Namespace

Classes: JavaScriptMiddleware

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.modulesHash (readonly)

Returns all registered modules

Returns:

  • (Hash)

    Module registry



50
51
52
# File 'lib/panda/core/module_registry.rb', line 50

def modules
  @modules
end

Class Method Details

.combined_importmapHash

Returns a combined importmap for all registered modules

This merges importmaps from Core and all registered modules (CMS, CMS Pro, etc.) into a single hash suitable for <script type=“importmap”> generation.

Order matters: Core imports are added first, then modules in registration order. If there are conflicts, later modules override earlier ones.

Returns:

  • (Hash)

    Combined imports hash => “/path/to/file.js”



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
# File 'lib/panda/core/module_registry.rb', line 156

def combined_importmap
  imports = {}

  # Add Panda Core imports first (if Core has an importmap)
  if defined?(Panda::Core.importmap)
    Panda::Core.importmap.instance_variable_get(:@packages).each do |name, package|
      imports[name] = package.path
    end
  end

  # Add registered modules' importmaps in registration order
  @modules.each do |gem_name, info|
    next unless engine_available?(info[:engine])

    # Get the module's importmap constant (e.g., Panda::CMS.importmap)
    module_importmap = module_importmap_for(info[:engine])
    next unless module_importmap

    module_importmap.instance_variable_get(:@packages).each do |name, package|
      imports[name] = package.path
    end
  end

  imports
end

.javascript_sourcesArray<String>

Returns JavaScript source files by introspecting importmaps

Instead of duplicating file lists, we read the importmap configuration that each engine already maintains. This provides a single source of truth.

Returns:

  • (Array<String>)

    Full paths to JavaScript source files



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
# File 'lib/panda/core/module_registry.rb', line 104

def javascript_sources
  return [] unless defined?(Rails.application&.importmap)

  sources = []

  # Detect importmap-rails version and use appropriate API
  importmap = Rails.application.importmap
  entries = if importmap.respond_to?(:packages)
    # importmap-rails 2.x - packages is a hash
    importmap.packages
  elsif importmap.respond_to?(:entries)
    # importmap-rails 1.x - entries is an array
    importmap.entries.map { |e| [e.name, e] }.to_h
  else
    {}
  end

  # Find all Panda-namespaced imports and resolve to file paths
  entries.each do |name, config|
    next unless name.to_s.match?(/^panda-/)

    path = resolve_importmap_to_path(name, config)
    sources << path if path
  end

  sources.compact.uniq
end

.register(gem_name:, engine:, paths:) ⇒ Object

Register a Panda module with its asset paths

Parameters:

  • gem_name (String)

    Gem name (e.g., ‘panda-cms’)

  • engine (String)

    Engine constant name (e.g., ‘Panda::CMS::Engine’)

  • paths (Hash)

    Asset path patterns relative to engine root

Options Hash (paths:):

  • :views (String)

    View template paths

  • :components (String)

    ViewComponent paths

  • :stylesheets (String)

    Stylesheet paths (optional)



40
41
42
43
44
45
# File 'lib/panda/core/module_registry.rb', line 40

def register(gem_name:, engine:, paths:)
  @modules[gem_name] = {
    engine: engine,
    paths: paths
  }
end

.registered?(gem_name) ⇒ Boolean

Check if a specific module is registered

Parameters:

  • gem_name (String)

    Gem name to check

Returns:

  • (Boolean)

    True if module is registered



143
144
145
# File 'lib/panda/core/module_registry.rb', line 143

def registered?(gem_name)
  @modules.key?(gem_name)
end

.registered_modulesArray<String>

Returns registered module names

Returns:

  • (Array<String>)

    List of registered gem names



135
136
137
# File 'lib/panda/core/module_registry.rb', line 135

def registered_modules
  @modules.keys
end

.tailwind_content_pathsArray<String>

Returns content paths for Tailwind CSS scanning

Tailwind needs to scan all files that might contain utility classes:

  • Views (ERB templates)

  • Components (ViewComponent classes)

  • JavaScript (Stimulus controllers, etc.)

Returns:

  • (Array<String>)

    Full paths for Tailwind –content flags



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
# File 'lib/panda/core/module_registry.rb', line 60

def tailwind_content_paths
  paths = []

  # Core's own content (always included)
  core_root = Panda::Core::Engine.root
  paths << "#{core_root}/app/views/panda/core/**/*.erb"
  paths << "#{core_root}/app/components/panda/core/**/*.rb"

  # Registered modules (only if engine is loaded)
  @modules.each do |gem_name, info|
    next unless engine_available?(info[:engine])

    root = engine_root(info[:engine])
    next unless root

    # Add configured path types
    paths << "#{root}/#{info[:paths][:views]}" if info[:paths][:views]
    paths << "#{root}/#{info[:paths][:components]}" if info[:paths][:components]

    # For Tailwind scanning, we also need to scan JavaScript for utility classes
    # Check if module has JavaScript (via importmap or direct paths)
    js_root = root.join("app/javascript")
    if js_root.directory?
      paths << "#{js_root}/**/*.js"
    end
  end

  # Host application Panda overrides
  # Applications can override any Panda views/components
  if defined?(Rails.root)
    paths << "#{Rails.root}/app/views/panda/**/*.erb"
    paths << "#{Rails.root}/app/components/panda/**/*.rb"
    paths << "#{Rails.root}/app/javascript/panda/**/*.js"
  end

  paths.compact
end