moduleSvelteOnRailsmoduleInstallermoduleUtilsdefself.install_npm_packagepackage_name="@csedl/svelte-on-rails@latest"puts"Installing #{package_name} via npm..."ifsystem("npm install #{package_name}")puts"#{package_name} successfully installed."elseabort"Failed to install #{package_name}. Please ensure npm is installed and try running 'npm install #{package_name}' manually."endenddefself.install_turbopkg_js=Rails.root.join("package.json")package_name="@hotwired/turbo-rails"file_content=File.exist?(pkg_js)?File.read(pkg_js):""iffile_content.match?(/#{package_name}/)puts"#{package_name} is already present in package.json, assuming that it is set up well and working."elseputs"Installing #{package_name} via npm..."ifsystem("npm install #{package_name}")puts"#{package_name} successfully installed."add_line_to_file(Rails.root.join("app","frontend","entrypoints","application.js"),"import '#{package_name}';")elseabort"Failed to install #{package_name}. Please ensure npm is installed and try running 'npm install #{package_name}' manually."endendenddefself.create_folder(folder)ifDir.exist?(folder)puts"Folder already exists: #{folder}"elseFileUtils.mkdir_p(folder)puts"Created folder: #{folder}"endenddefself.add_line_to_file(file_path,line)file_content=File.exist?(file_path)?File.read(file_path):""iffile_content.match?(/#{line}/)puts"#{line} already present in #{file_path}, nothing changed here."returnendFile.open(file_path,'a')do|file|file.puts(line)endputs"added #{line} to #{file_path}."rescueStandardError=>eputs"Error: #{e.message}"enddefself.create_file(file_path)unlessFile.exist?(file_path)FileUtils.touch(file_path)puts"Created empty file at file://#{file_path}"endenddefself.create_javascript_initializerconfig_path=Rails.root.join("app","frontend","initializers","svelte.js")ifFile.exist?(config_path)puts"Initializer already exists: file://#{config_path}"elseFile.write(config_path,<<~JAVASCRIPT)
import { initializeSvelteComponents, cleanupSvelteComponents } from '@csedl/svelte-on-rails';
const components = import.meta.glob('/javascript/components/**/*.svelte', { eager: true });
const componentsRoot = '/javascript/components';
// Initialize Svelte components
initializeSvelteComponents(componentsRoot, components, true);
// Turbo event listener for page load
document.addEventListener('turbo:load', () => {
initializeSvelteComponents(componentsRoot, components, true);
});
// Turbo event listener for cleanup before page cache
document.addEventListener('turbo:before-cache', () => {
cleanupSvelteComponents(false);
});
JAVASCRIPTputs"Created initializer file at file://#{config_path}"endenddefself.create_svelte_hello_worldfile_path=Rails.root.join("app","frontend","javascript","components","HelloWorld.svelte")ifFile.exist?(file_path)puts"Hello World file already exists: file://#{file_path}"elseFile.write(file_path,<<~HTML)
<script>
export let items
let count = 0;
function increment() {
count += 1;
}
</script>
<h1>Greetings from svelte</h1>
<button on:click={increment}>Increment: {count}</button>
<ul>
{#each items as item}
<li>{item}</li>
{/each}
</ul>
<style>
button {
background-color: darkred;
color: white;
padding: 10px;
border: none;
}
</style>
HTMLputs"Hello World file at file://#{file_path}"endenddefself.run_command(command)Dir.chdir(Rails.root)dostdout,stderr,status=Open3.capture3(command)ifstderr.present?puts"Error running command «#{command}»:"raisestderrelseputs"#{command} => Success"endendenddefself.check_file_exists(file_path)unlessFile.exist?(file_path)raise"ERROR: File not found: #{file_path}"endenddefself.check_folder_exists(folder_path)unlessFile.exist?(folder_path)raise"ERROR: Folder not found: #{folder_path}"endenddefself.check_file_not_exists(file_path)ifFile.exist?(file_path)raise"ERROR: File already exists: #{file_path}"endenddefself.check_folder_not_exists(folder_path)ifFile.exist?(folder_path)raise"ERROR: Folder already exists: #{folder_path}"endenddefself.write_templates(template_paths)existing=template_paths.select{|p|File.exist?(p)}verb='Created'ifexisting.present?beginputs"#{'Template'.pluralize(existing.length)} already exists:\n#{existing.join("\n")}.\nOverwrite? (y/n)"continue=STDIN.gets.chomp.downcase[0]enduntil['y','n'].include?(continue)ifcontinue=='n'puts"Skipping write #{'template'.pluralize(template_paths.length)}."returnendverb='Overwrote'endtemplate_paths.eachdo|p|source_path=File.expand_path('rails_template',__dir__)+'/'+pFileUtils.mkdir_p(File.dirname(p))FileUtils.cp(source_path,p)endputs"#{verb}#{'file'.pluralize(template_paths.length)}:\n#{template_paths.join("\n")}"enddefself.ask_yn(question)beginputs"#{question} (y/n)"continue=STDIN.gets.chomp.downcase[0]enduntil['y','n'].include?(continue)continue=='y'enddefself.which_root_route# Check if the root route is active (uncommented) or commented outroutes=File.read(Rails.root.join('config','routes.rb'))m=routes.match(/^\s*root\s+['"]([^'"]+)['"]/m)ifmm.to_s.match(/^\s*root\s*['"]([^'"]*)['"]/)[1]endenddefself.add_route(route)file_path='config/routes.rb'# Read the file contentcontent=File.read(file_path)# Split content into lineslines=content.lines# Find the index of Rails.application.routes.draw doind=-1lines.each_with_indexdo|line,i|ifline.match?(/^\s*Rails.application.routes.draw\s*do[\s\S]+$/)ind=iendend# Insertifind>=0lines.insert(ind+1,route)elseraise"ERROR: Could not find Rails.application.routes.draw do"end# Write the modified content back to the filebeginFile.write(file_path,lines.map{|l|l.gsub(/\n/,'')}.join("\n"))puts"Successfully inserted «root '#{route}'» into '#{file_path}'"rescue=>eraise"Error writing to #{file_path} => «#{e.message}»"endenddefself.route_exists?(target_route)# check if exists# Ensure the Rails environment is loaded# require File.expand_path("../config/environment", __dir__)# Get all routes from the Rails applicationroutes=Rails.application.routes.routes# Check if the route existsroutes.any?do|route|# Extract the path spec and remove any optional parts or constraintspath=route.path.spec.to_s# Clean up the path to match the format (remove leading/trailing slashes, etc.)cleaned_path=path.gsub(/\(.*\)/,"").gsub(/^\/|\/$/,"")# Check if the cleaned path matches the target routecleaned_path==target_routeendenddefself.remove_files(file_paths)file_paths.eachdo|f|ifFile.exist?(f)ifFile.directory?(f)Dir.delete(f)puts" • Removed directory: #{f}"elseFile.delete(f)puts" • Removed file: #{f}"endelseputs" • File/Path not found so not removed: #{f}"endendenddefself.remove_line_from_file(file_path,string_to_find)# Read the file contentcontent=File.read(file_path)# Split content into lineslines=content.linesfound_lines=[]modified_content=[]lines.eachdo|line|ifline.match(/^[\s\S]+#{string_to_find}[\s\S]+$/)found_lines.push(line)elsemodified_content.push(line)endendutils=SvelteOnRails::Installer::Utilsiffound_lines.present?&&utils.ask_yn("Remove lines\n • #{found_lines.join("\n • ")}\n from #{file_path}?")# Write the modified content back to the filebeginFile.write(file_path,modified_content.map{|l|l.gsub(/\n/,'')}.join("\n"))puts"Successfully removed #{found_lines.length}#{'line'.pluralize(found_lines.length)}."rescue=>eraise"Error writing to #{file_path} => «#{e.message}»"endelseendendendendend