Skip to content

Commit

Permalink
got import maps working 🤠
Browse files Browse the repository at this point in the history
  • Loading branch information
SethHorsley committed Oct 16, 2024
1 parent 988fa8f commit a286edc
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 76 deletions.
10 changes: 8 additions & 2 deletions lib/generators/rbui/base_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ class BaseGenerator < defined?(Rails::Generators::Base) ? Rails::Generators::Bas

source_root File.join(__dir__, "templates")

def copy_templates
template "base_store_initializer.rb", "config/initializers/rbui.rb"
# def copy_templates
# template "base_store_initializer.rb", "config/initializers/rbui.rb"
# end

private

def using_importmap?
File.exist?(Rails.root.join("config/importmap.rb")) && File.exist?(Rails.root.join("bin/importmap"))
end
end
end
Expand Down
99 changes: 67 additions & 32 deletions lib/generators/rbui/component_generator.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module RBUI
module Generators
class ComponentGenerator < defined?(Rails::Generators::Base) ? Rails::Generators::Base : Object
class ComponentGenerator < RBUI::Generators::BaseGenerator
namespace "rbui:component"

source_root File.expand_path("../../..", __dir__)
Expand Down Expand Up @@ -34,48 +34,83 @@ def copy_component_files
def update_index_file
index_path = File.join(destination_root, "app/components/rbui/index.js")

content = File.read(index_path)
rbui_index_content = File.read(index_path)

add_controller_registration(content)
updated_rbui_index_content = add_controller_registration(rbui_index_content)

File.write(index_path, content)
File.write(index_path, updated_rbui_index_content)
end

def add_controller_registration(content)
def add_controller_registration(rbui_index_content)
valid_controllers = get_valid_controllers

rbui_index_content = update_imports(rbui_index_content, valid_controllers)
update_registrations(rbui_index_content, valid_controllers)
# Uncomment the following line if you want to update exports
# rbui_index_content = update_exports(rbui_index_content, valid_controllers)
end

def get_valid_controllers
all_js_controllers = Dir.glob(File.join(destination_path, "**", "*_controller.js"))

# Collect all valid controller information
valid_controllers = all_js_controllers.map do |controller_file|
relative_path = Pathname.new(controller_file).relative_path_from(Pathname.new(destination_path))
file_name = relative_path.basename(".js").to_s
component_name = file_name.sub(/_controller$/, "")
new_controller = "#{component_name.camelize}Controller"
new_path = "./#{relative_path.dirname}/#{file_name}"
registration_name = "rbui--#{component_name.dasherize}"

{
import: "import #{new_controller} from \"#{new_path}\";",
registration: "application.register(\"#{registration_name}\", #{new_controller});",
export: "export { default as #{new_controller} } from \"#{new_path}\";"
}
all_js_controllers.map do |controller_file|
controller_info(controller_file)
end
end

def controller_info(controller_file)
# Get the relative path from the destination path to the controller file
relative_path = Pathname.new(controller_file).relative_path_from(Pathname.new(destination_path))

# Extract the file name without the .js extension
file_name = relative_path.basename(".js").to_s

# Remove '_controller' suffix to get the component name
component_name = file_name.sub(/_controller$/, "")

# Create the new controller name by camelizing the component name and adding 'Controller'
new_controller = "#{component_name.camelize}Controller"

# Build the new import path
new_import_path = new_import_path("./#{relative_path.dirname}/#{file_name}")

# Update imports
imports = valid_controllers.map { |c| c[:import] }.sort
import_block = imports.join("\n")
content.sub!(/\/\/ Import all controller files.*?(?=\n\n)/m, "// Import all controller files\n#{import_block}")
# Create the registration name by dasherizing the component name and prefixing with 'rbui--'
registration_name = "rbui--#{component_name.dasherize}"

# Update registrations
registrations = valid_controllers.map { |c| c[:registration] }.sort
registration_block = registrations.join("\n")
content.sub!(/\/\/ Register all controllers.*?(?=\n\n)/m, "// Register all controllers\n#{registration_block}")
# Return a hash with import, registration, and export statements
{
# Import statement for importmaps
import: "import #{new_controller} from \"#{new_import_path}\";",

# Update exports
# exports = valid_controllers.map { |c| c[:export] }.sort
# export_block = exports.join("\n")
# content.sub!(/\/\/ Export all controllers.*?(?=\n\n)/m, "// Export all controllers so user of npm package can lazy load controllers\n#{export_block}")
# Registration statement for the Stimulus controller
registration: "application.register(\"#{registration_name}\", #{new_controller});",

# Export statement for the controller
export: "export { default as #{new_controller} } from \"#{new_import_path}\";"
}
end

def new_import_path(relative_path)
if using_importmap?
"rbui/#{relative_path.sub(/^\.\//, "")}"
else
relative_path
end
end

def update_imports(content, controllers)
imports = controllers.map { |c| c[:import] }.sort.join("\n")
content.sub(/\/\/ Import all controller files.*?(?=\n\n)/m, "// Import all controller files\n#{imports}")
end

def update_registrations(content, controllers)
registrations = controllers.map { |c| c[:registration] }.sort.join("\n")
content.sub(/\/\/ Register all controllers.*?(?=\n\n)/m, "// Register all controllers\n#{registrations}")
end

content
def update_exports(content, controllers)
exports = controllers.map { |c| c[:export] }.sort.join("\n")
content.sub(/\/\/ Export all controllers.*?(?=\n\n)/m, "// Export all controllers so user of npm package can lazy load controllers\n#{exports}")
end

def component
Expand Down
135 changes: 97 additions & 38 deletions lib/generators/rbui/install/install_generator.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
require "net/http"

# TODO: make ejctectec components work without the gem
module RBUI
module Generators
class InstallGenerator < defined?(Rails::Generators::Base) ? Rails::Generators::Base : Object
class InstallGenerator < RBUI::Generators::BaseGenerator
namespace "rbui:install"

if defined?(Rails::Generators::Base)
source_root File.expand_path("templates", __dir__)

def confirm_installation
return if yes?("You need tailwindcss installed. Continue? (y/n)")
say "Installation cancelled.", :red
exit
end

def add_phlex_rails
say "Checking for Phlex Rails"
if gem_installed?("phlex-rails")
Expand All @@ -15,21 +24,24 @@ def add_phlex_rails
run "bundle add phlex-rails"
end

if yes?("Do you want to run the Phlex installer? (y/n)")
say "Run Phlex install"
run "bin/rails generate phlex:install"
end
say "run phlex install"
run "bin/rails generate phlex:install"
end

def install_stuff
if yes?("Do you want to set up the dev test data? (y/n)")
# make default option no
# extend the yes func to have a default option y/(n) and also allow for enter to accedpt the default

if ENV["TEST_DATA"] == "true"
say "Do you want to set up the dev test data?"
say "Add index controller"
run "bin/rails generate controller static index --no-helper --no-assets --no-test-framework --no-jbuilder"

say "Add index view"
run "bin/rails g phlex:view Static::Index"

append_to_file "app/controllers/static_controller.rb", after: " def index" do
# remove view because phlex is removing view
"\n render Static::IndexView"
end

Expand All @@ -41,65 +53,82 @@ def install_stuff
end
end

say "Checking for Tailwind CSS"
if gem_installed?("tailwindcss-rails")
say "Tailwind CSS is already installed", :green
tailwind_config_path = Rails.root.join("config/tailwind.config.js")
if !File.exist?(tailwind_config_path)
say "Tailwind CSS is required for RBUI", :red
end

say "Add rbui initializer"
template "base_store_initializer.rb", "config/initializers/rbui.rb"

if using_importmap?
say "Using importmaps, adding tailwind-animate"
run "bin/importmap pin tailwindcss-animate"

# Remove the default pin
gsub_file "config/importmap.rb", /^pin "tailwindcss-animate".*$\n/, ""

if yes?("Do you want to run the Tailwind installer? (y/n)")
say "Run Tailwind install"
run "./bin/rails tailwindcss:install"
# Add the vendor-specific pin
append_to_file "config/importmap.rb" do
'pin "tailwindcss-animate", to: "tailwindcss-animate.js", preload: true' + "\n"
end
elsif yes?("Do you want us to install Tailwind CSS? (y/n)")
say "Adding Tailwind CSS"
run "./bin/bundle add tailwindcss-rails"

say "Run Tailwind install"
run "./bin/rails tailwindcss:install"
else
say "Not using importmaps, adding tailwind-animate via yarn"
run "yarn add tailwindcss-animate"
end

say "Add tailwind animate"
run "yarn add tailwindcss-animate"

# check if tailwind.config is in config dir or in root or ask to specify a path
say "update tailwind.config.js"
template "tailwind.config.js", "config/tailwind.config.js", force: true
template "tailwind.config.js", "config/tailwind.config.js", force: true, assigns: {using_importmap: using_importmap?}

say "Add CSS variables"
template "application.tailwind.css", "app/assets/stylesheets/application.tailwind.css", force: true
end

def pin_rbui_js
importmap_binstub = Rails.root.join("bin/importmap")
importmap_config_path = Rails.root.join("config/importmap.rb")
stimulus_path = Rails.root.join("app/javascript/application.js")
package_name = "rbui-js"

if importmap_binstub.exist?
say "Add RBUI Stimulus controllers"
# run "mkdir -p app/javascript/controllers/rbui-js"
template "index.js", "app/components/rbui/index.js"

if using_importmap?
gsub_file "app/components/rbui/index.js", /^import { application }.*$/ do
'import { application } from "controllers/application";'
end

append_to_file Rails.root.join("config/initializers/assets.rb") do
"Rails.application.config.assets.paths << Rails.root.join(\"app/components\")\n"
end

say "Pin #{package_name}"
append_to_file importmap_config_path do
# %(pin "rbui-js", to: "rbui-js.js"\n)
%(pin #{package_name}, to: "rbui-js.js"\n)
append_to_file Rails.root.join("config/importmap.rb") do
"pin_all_from \"app/components/rbui\", under: \"rbui\"\n"
end

run "bin/importmap pin #{package_name}"
append_to_file stimulus_path, "\nimport \"rbui\";\n"

fix_import_maps!
else
say "Add rbui-js package"
run "yarn add #{package_name}"
end

if stimulus_path.exist?
say "Add RBUI Stimulus controllers"
template "#{template_dir}/index.js", "#{destination_path}/index.js" unless File.exist?("#{destination_path}/index.js")
append_to_file stimulus_path, "\nimport \"../components/rbui\";\n"

run "yarn build"
else
say "Default Stimulus location is missing: app/javascript/controllers/index.js", :red
say " Add to your Stimulus index.js:"
say " import \"#{package_name}\""
end
end

def include_rbui
say "Add RBUI to your global component layout"
insert_into_file "app/views/application_view.rb", after: "class ApplicationView < ApplicationComponent\n" do
" include RBUI\n"
message = "Include RBUI in your global component layout?\n This allows to call RBUI.Button {\"button\"} / RBUI::Button.new {\"button\"} with Button {\"button\"} (y/n)"
if yes?(message)
say "Add RBUI to your global component layout"
insert_into_file "app/views/application_view.rb", after: "class ApplicationView < ApplicationComponent\n" do
" include RBUI\n"
end
end
end

Expand All @@ -119,6 +148,36 @@ def revoke

private

def fix_import_maps!
importmap_config_path = Rails.root.join("config/importmap.rb")

gsub_file importmap_config_path, /^pin "date-fns".*$/ do
'pin "date-fns", to: "https://ga.jspm.io/npm:[email protected]/index.mjs"'
end
run "bin/importmap pin @popperjs/[email protected]/+esm --from jsdelivr"

run "mv vendor/javascript/@popperjs--core--+esm.js vendor/javascript/stupid-popper-lib-2024.js"

append_to_file importmap_config_path do
'pin "@popperjs/core", to: "stupid-popper-lib-2024.js"'
end

uri = URI "https://ga.jspm.io/npm:[email protected]/dist/chart.min.js"
request = Net::HTTP::Get.new uri

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http|
http.request(request)
}

File.write(Rails.root.join("vendor/javascript/chart.js--auto.js"), response.body) if response.is_a?(Net::HTTPSuccess)

append_to_file Rails.root.join("app/views/layouts/application.html.erb"), before: "</body>" do
"<script>
window.process = { env: { NODE_ENV: 'production'} }
</script>"
end
end

def gem_installed?(name)
Gem::Specification.find_all_by_name(name).any?
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
RBUI.setup do |config|
# RBUI.setup do |config|
# Setting a namespace allows you to access RBUI components through this namespace.
# For example, with namespace set to "UI", you can use:
# UI::Button.new instead of RBUI::Button.new
# UI::Card.new instead of RBUI::Card.new
# This can help avoid naming conflicts and allows for cleaner, more concise code.
# If you prefer to use RBUI components directly, you can leave this unset.
config.namespace = "UI"
end
# config.namespace = "UI"
# end
4 changes: 4 additions & 0 deletions lib/generators/rbui/install/templates/tailwind.config.js.tt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ module.exports = {
},
},
plugins: [
<% if using_importmap? %>
require("../vendor/javascript/tailwindcss-animate"),
<% else %>
require("tailwindcss-animate"),
<% end %>
],
}
1 change: 1 addition & 0 deletions lib/rbui.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def self.create_namespace_module

# If you need to require generators (assuming they're needed)
if defined?(Rails::Generators)
require_relative "generators/rbui/base_generator"
require_relative "generators/rbui/install/install_generator"
require_relative "generators/rbui/component_generator"
end
2 changes: 1 addition & 1 deletion lib/rbui/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

module RBUI
class Base < Phlex::HTML
TAILWIND_MERGER = ::TailwindMerge::Merger.new.freeze
TAILWIND_MERGER = ::TailwindMerge::Merger.new.freeze unless defined?(TAILWIND_MERGER)

attr_reader :attrs

Expand Down

0 comments on commit a286edc

Please sign in to comment.