islandjs-rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +16 -0
- data/LICENSE.md +22 -0
- data/README.md +754 -0
- data/exe/islandjs-rails +6 -0
- data/islandjs-rails.gemspec +55 -0
- data/lib/islandjs-rails.rb +3 -0
- data/lib/islandjs_rails/cli.rb +57 -0
- data/lib/islandjs_rails/configuration.rb +49 -0
- data/lib/islandjs_rails/core.rb +462 -0
- data/lib/islandjs_rails/core_methods.rb +609 -0
- data/lib/islandjs_rails/rails_helpers.rb +394 -0
- data/lib/islandjs_rails/railtie.rb +59 -0
- data/lib/islandjs_rails/tasks.rb +118 -0
- data/lib/islandjs_rails/vendor_manager.rb +271 -0
- data/lib/islandjs_rails/version.rb +3 -0
- data/lib/islandjs_rails.rb +142 -0
- data/lib/templates/app/controllers/islandjs_demo_controller.rb +9 -0
- data/lib/templates/app/javascript/islands/components/.gitkeep +0 -0
- data/lib/templates/app/javascript/islands/components/HelloWorld.jsx +117 -0
- data/lib/templates/app/javascript/islands/index.js +10 -0
- data/lib/templates/app/javascript/islands/utils/turbo.js +87 -0
- data/lib/templates/app/views/islandjs_demo/index.html.erb +98 -0
- data/lib/templates/app/views/islandjs_demo/react.html.erb +93 -0
- data/lib/templates/config/demo_routes.rb +3 -0
- data/lib/templates/package.json +21 -0
- data/lib/templates/webpack.config.js +49 -0
- data/package.json +12 -0
- data/yarn.lock +1890 -0
- metadata +181 -0
@@ -0,0 +1,609 @@
|
|
1
|
+
module IslandjsRails
|
2
|
+
class Core
|
3
|
+
# Additional core methods (part 2)
|
4
|
+
|
5
|
+
def build_bundle!
|
6
|
+
puts "šØ Building IslandJS webpack bundle..."
|
7
|
+
|
8
|
+
unless system('which yarn > /dev/null 2>&1')
|
9
|
+
puts "ā yarn not found, cannot build bundle"
|
10
|
+
return false
|
11
|
+
end
|
12
|
+
|
13
|
+
unless system('yarn list webpack-cli > /dev/null 2>&1')
|
14
|
+
puts "ā ļø webpack-cli not found, installing..."
|
15
|
+
system('yarn add --dev webpack-cli@^5.1.4')
|
16
|
+
end
|
17
|
+
|
18
|
+
if ENV['NODE_ENV'] == 'production' || ENV['RAILS_ENV'] == 'production'
|
19
|
+
success = system('yarn build')
|
20
|
+
else
|
21
|
+
success = system('yarn build > /dev/null 2>&1')
|
22
|
+
end
|
23
|
+
|
24
|
+
if success
|
25
|
+
puts "ā
Bundle built successfully"
|
26
|
+
return true
|
27
|
+
else
|
28
|
+
puts "ā Build failed. Check your webpack configuration."
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def offer_demo_route!
|
34
|
+
# Check if demo route already exists
|
35
|
+
if demo_route_exists?
|
36
|
+
puts "ā Demo route already exists at /islandjs/react"
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
print "\nā Would you like to create a demo route at /islandjs/react to showcase your HelloWorld component? (y/n): "
|
41
|
+
answer = STDIN.gets.chomp.downcase
|
42
|
+
|
43
|
+
if answer == 'y' || answer == 'yes'
|
44
|
+
create_demo_route!
|
45
|
+
puts "\nš Demo route created! Visit http://localhost:3000/islandjs/react to see your React component in action."
|
46
|
+
puts "š” You can remove it later by deleting the route, controller, and view manually."
|
47
|
+
else
|
48
|
+
puts "\nš” No problem! Here's how to render your HelloWorld component manually:"
|
49
|
+
puts " In any view: <%= react_component('HelloWorld') %>"
|
50
|
+
puts " Don't forget to: yarn build && rails server"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def demo_route_exists?
|
55
|
+
routes_file = File.join(Dir.pwd, 'config', 'routes.rb')
|
56
|
+
return false unless File.exist?(routes_file)
|
57
|
+
|
58
|
+
content = File.read(routes_file)
|
59
|
+
content.include?('islandjs_demo') || content.include?('islandjs/react') || content.include?("get 'islandjs'")
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_demo_route!
|
63
|
+
create_demo_controller!
|
64
|
+
create_demo_view!
|
65
|
+
add_demo_route!
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_demo_controller!
|
69
|
+
controller_dir = File.join(Dir.pwd, 'app', 'controllers')
|
70
|
+
FileUtils.mkdir_p(controller_dir)
|
71
|
+
|
72
|
+
controller_file = File.join(controller_dir, 'islandjs_demo_controller.rb')
|
73
|
+
copy_template_file('app/controllers/islandjs_demo_controller.rb', controller_file)
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_demo_view!
|
77
|
+
view_dir = File.join(Dir.pwd, 'app', 'views', 'islandjs_demo')
|
78
|
+
FileUtils.mkdir_p(view_dir)
|
79
|
+
|
80
|
+
# Copy demo view templates from gem
|
81
|
+
copy_demo_template('index.html.erb', view_dir)
|
82
|
+
copy_demo_template('react.html.erb', view_dir)
|
83
|
+
end
|
84
|
+
|
85
|
+
def copy_demo_template(template_name, destination_dir)
|
86
|
+
gem_root = File.expand_path('../../..', __FILE__)
|
87
|
+
template_path = File.join(gem_root, 'lib', 'templates', 'app', 'views', 'islandjs_demo', template_name)
|
88
|
+
destination_path = File.join(destination_dir, template_name)
|
89
|
+
|
90
|
+
if File.exist?(template_path)
|
91
|
+
FileUtils.cp(template_path, destination_path)
|
92
|
+
puts " ā Created #{template_name} at app/views/islandjs_demo/#{template_name}"
|
93
|
+
else
|
94
|
+
puts " ā ļø Template not found: #{template_path}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def copy_template_file(template_name, destination_path)
|
99
|
+
gem_root = File.expand_path('../../..', __FILE__)
|
100
|
+
template_path = File.join(gem_root, 'lib', 'templates', template_name)
|
101
|
+
|
102
|
+
if File.exist?(template_path)
|
103
|
+
FileUtils.cp(template_path, destination_path)
|
104
|
+
puts " ā Created #{File.basename(template_name)} from template"
|
105
|
+
else
|
106
|
+
puts " ā ļø Template not found: #{template_path}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_demo_routes_content(indent, has_root_route)
|
111
|
+
gem_root = File.expand_path('../../..', __FILE__)
|
112
|
+
template_path = File.join(gem_root, 'lib', 'templates', 'config', 'demo_routes.rb')
|
113
|
+
|
114
|
+
if File.exist?(template_path)
|
115
|
+
routes_content = File.read(template_path)
|
116
|
+
# Apply indentation to each line
|
117
|
+
route_lines = routes_content.lines.map { |line| "#{indent}#{line}" }.join
|
118
|
+
|
119
|
+
# Add root route if none exists
|
120
|
+
unless has_root_route
|
121
|
+
root_route = "#{indent}root 'islandjs_demo#index'\n"
|
122
|
+
route_lines = root_route + route_lines
|
123
|
+
end
|
124
|
+
|
125
|
+
route_lines
|
126
|
+
else
|
127
|
+
# Fallback to hardcoded routes if template not found
|
128
|
+
route_lines = "#{indent}# IslandJS demo routes (you can remove these)\n"
|
129
|
+
unless has_root_route
|
130
|
+
route_lines += "#{indent}root 'islandjs_demo#index'\n"
|
131
|
+
end
|
132
|
+
route_lines += "#{indent}get 'islandjs', to: 'islandjs_demo#index'\n"
|
133
|
+
route_lines += "#{indent}get 'islandjs/react', to: 'islandjs_demo#react'\n"
|
134
|
+
route_lines
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def add_demo_route!
|
139
|
+
routes_file = File.join(Dir.pwd, 'config', 'routes.rb')
|
140
|
+
return unless File.exist?(routes_file)
|
141
|
+
|
142
|
+
content = File.read(routes_file)
|
143
|
+
|
144
|
+
# Check if root route already exists
|
145
|
+
has_root_route = content.include?('root ') || content.match(/^\s*root\s/)
|
146
|
+
|
147
|
+
# Find the Rails.application.routes.draw block
|
148
|
+
if content.match(/Rails\.application\.routes\.draw do\s*$/)
|
149
|
+
# Determine indentation
|
150
|
+
indent = content.match(/^(\s*)Rails\.application\.routes\.draw do\s*$/)[1]
|
151
|
+
|
152
|
+
# Build route lines from template
|
153
|
+
route_lines = get_demo_routes_content(indent, has_root_route)
|
154
|
+
|
155
|
+
# Add the routes after the draw line
|
156
|
+
updated_content = content.sub(
|
157
|
+
/(Rails\.application\.routes\.draw do\s*$)/,
|
158
|
+
"\\1\n#{route_lines}"
|
159
|
+
)
|
160
|
+
|
161
|
+
File.write(routes_file, updated_content)
|
162
|
+
puts " ā Added demo routes to config/routes.rb:"
|
163
|
+
unless has_root_route
|
164
|
+
puts " root 'islandjs_demo#index' (set as homepage)"
|
165
|
+
end
|
166
|
+
puts " get 'islandjs', to: 'islandjs_demo#index'"
|
167
|
+
puts " get 'islandjs/react', to: 'islandjs_demo#react'"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def setup_vendor_system!
|
172
|
+
# Initialize empty vendor manifest
|
173
|
+
manifest_path = configuration.vendor_manifest_path
|
174
|
+
unless File.exist?(manifest_path)
|
175
|
+
require 'json'
|
176
|
+
initial_manifest = { 'libs' => [] }
|
177
|
+
FileUtils.mkdir_p(File.dirname(manifest_path))
|
178
|
+
File.write(manifest_path, JSON.pretty_generate(initial_manifest))
|
179
|
+
puts " ā Created vendor manifest"
|
180
|
+
end
|
181
|
+
|
182
|
+
# Generate initial empty vendor partial
|
183
|
+
vendor_manager = IslandjsRails.vendor_manager
|
184
|
+
vendor_manager.send(:regenerate_vendor_partial!)
|
185
|
+
puts " ā Generated vendor UMD partial"
|
186
|
+
end
|
187
|
+
|
188
|
+
def inject_islands_helper_into_layout!
|
189
|
+
layout_path = find_application_layout
|
190
|
+
return unless layout_path
|
191
|
+
|
192
|
+
content = File.read(layout_path)
|
193
|
+
islands_helper_line = '<%= islands %>'
|
194
|
+
vendor_render_line = '<%= render "shared/islands/vendor_umd" %>'
|
195
|
+
|
196
|
+
# Check if islands helper or vendor partial is already included
|
197
|
+
if content.include?(islands_helper_line) || content.include?('islands %>') ||
|
198
|
+
content.include?(vendor_render_line) || content.include?('render "shared/islands/vendor_umd"')
|
199
|
+
puts " ā Islands helper already included in layout"
|
200
|
+
return
|
201
|
+
end
|
202
|
+
|
203
|
+
# Try to inject after existing head content or before </head>
|
204
|
+
if content.include?('</head>')
|
205
|
+
updated_content = content.sub(
|
206
|
+
/\s*<\/head>/,
|
207
|
+
"\n #{islands_helper_line}\n </head>"
|
208
|
+
)
|
209
|
+
|
210
|
+
File.write(layout_path, updated_content)
|
211
|
+
puts " ā Added islands helper to #{File.basename(layout_path)}"
|
212
|
+
else
|
213
|
+
puts " ā ļø Could not automatically inject islands helper. Please add manually:"
|
214
|
+
puts " #{islands_helper_line}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def find_application_layout
|
219
|
+
# Look for application layout in common locations
|
220
|
+
layout_paths = [
|
221
|
+
File.join(Dir.pwd, 'app', 'views', 'layouts', 'application.html.erb'),
|
222
|
+
File.join(Dir.pwd, 'app', 'views', 'layouts', 'application.html.haml'),
|
223
|
+
File.join(Dir.pwd, 'app', 'views', 'layouts', 'application.html.slim')
|
224
|
+
]
|
225
|
+
|
226
|
+
layout_paths.find { |path| File.exist?(path) }
|
227
|
+
end
|
228
|
+
|
229
|
+
def check_node_tools!
|
230
|
+
unless system('which npm > /dev/null 2>&1')
|
231
|
+
puts "ā npm not found. Please install Node.js and npm first."
|
232
|
+
exit 1
|
233
|
+
end
|
234
|
+
|
235
|
+
unless system('which yarn > /dev/null 2>&1')
|
236
|
+
puts "ā yarn not found. Please install yarn first: npm install -g yarn"
|
237
|
+
exit 1
|
238
|
+
end
|
239
|
+
|
240
|
+
puts "ā npm and yarn are available"
|
241
|
+
end
|
242
|
+
|
243
|
+
def ensure_package_json!
|
244
|
+
if File.exist?(configuration.package_json_path)
|
245
|
+
puts "ā package.json already exists"
|
246
|
+
return
|
247
|
+
end
|
248
|
+
|
249
|
+
puts "š Creating package.json..."
|
250
|
+
|
251
|
+
# Use template and customize with current directory name
|
252
|
+
template_path = File.join(__dir__, '..', 'templates', 'package.json')
|
253
|
+
template_content = File.read(template_path)
|
254
|
+
package_json = JSON.parse(template_content)
|
255
|
+
|
256
|
+
# Customize with current directory name
|
257
|
+
package_json["name"] = File.basename(Dir.pwd)
|
258
|
+
|
259
|
+
File.write(configuration.package_json_path, JSON.pretty_generate(package_json))
|
260
|
+
puts "ā Created package.json"
|
261
|
+
end
|
262
|
+
|
263
|
+
def install_essential_dependencies!
|
264
|
+
puts "š¦ Installing essential webpack dependencies..."
|
265
|
+
puts " Installing: #{ESSENTIAL_DEPENDENCIES.join(', ')}"
|
266
|
+
|
267
|
+
missing_deps = ESSENTIAL_DEPENDENCIES.select do |dep|
|
268
|
+
package_name = dep.split('@').first
|
269
|
+
!package_installed?(package_name)
|
270
|
+
end
|
271
|
+
|
272
|
+
if missing_deps.empty?
|
273
|
+
puts "ā All essential dependencies already installed"
|
274
|
+
return
|
275
|
+
end
|
276
|
+
|
277
|
+
success = system("yarn add --dev #{missing_deps.join(' ')}")
|
278
|
+
|
279
|
+
unless success
|
280
|
+
puts "ā Failed to install dependencies"
|
281
|
+
exit 1
|
282
|
+
end
|
283
|
+
|
284
|
+
puts "ā Installed essential webpack dependencies"
|
285
|
+
end
|
286
|
+
|
287
|
+
def create_scaffolded_structure!
|
288
|
+
puts "šļø Creating scaffolded structure..."
|
289
|
+
|
290
|
+
# Copy entire JavaScript islands structure from templates
|
291
|
+
gem_root = File.expand_path('../../..', __FILE__)
|
292
|
+
template_js_dir = File.join(gem_root, 'lib', 'templates', 'app', 'javascript', 'islands')
|
293
|
+
target_js_dir = File.join(Dir.pwd, 'app', 'javascript', 'islands')
|
294
|
+
|
295
|
+
if Dir.exist?(template_js_dir)
|
296
|
+
FileUtils.mkdir_p(File.dirname(target_js_dir))
|
297
|
+
FileUtils.cp_r(template_js_dir, File.dirname(target_js_dir))
|
298
|
+
puts "ā Created JavaScript islands structure from templates"
|
299
|
+
else
|
300
|
+
puts "ā ļø Template JavaScript directory not found: #{template_js_dir}"
|
301
|
+
# Fallback: create minimal structure
|
302
|
+
FileUtils.mkdir_p(File.join(target_js_dir, 'components'))
|
303
|
+
File.write(File.join(target_js_dir, 'components', '.gitkeep'), '')
|
304
|
+
puts "ā Created minimal JavaScript structure"
|
305
|
+
end
|
306
|
+
|
307
|
+
FileUtils.mkdir_p(configuration.partials_dir)
|
308
|
+
puts "ā Created #{configuration.partials_dir}"
|
309
|
+
end
|
310
|
+
|
311
|
+
# Automatically inject islands helper into Rails layout
|
312
|
+
def inject_umd_partials_into_layout!
|
313
|
+
layout_path = File.join(Dir.pwd, 'app', 'views', 'layouts', 'application.html.erb')
|
314
|
+
|
315
|
+
unless File.exist?(layout_path)
|
316
|
+
puts "ā ļø Layout file not found: #{layout_path}"
|
317
|
+
puts " Please add manually to your layout:"
|
318
|
+
puts " <%= islands %>"
|
319
|
+
return
|
320
|
+
end
|
321
|
+
|
322
|
+
content = File.read(layout_path)
|
323
|
+
|
324
|
+
# Check if already injected (idempotent)
|
325
|
+
if content.include?('island_partials') && content.include?('island_bundle_script') || content.include?('islands')
|
326
|
+
puts "ā Islands helper already present in layout"
|
327
|
+
return
|
328
|
+
end
|
329
|
+
|
330
|
+
# Find the closing </head> tag and inject before it with proper indentation
|
331
|
+
if match = content.match(/^(\s*)<\/head>/i)
|
332
|
+
indent = match[1] # Capture the existing indentation
|
333
|
+
islands_injection = "#{indent}<!-- IslandjsRails: Auto-injected -->\n#{indent}<%= islands %>"
|
334
|
+
|
335
|
+
# Inject before </head> with proper indentation
|
336
|
+
updated_content = content.gsub(/^(\s*)<\/head>/i, "#{islands_injection}\n\\1</head>")
|
337
|
+
File.write(layout_path, updated_content)
|
338
|
+
puts "ā Auto-injected UMD helper into app/views/layouts/application.html.erb"
|
339
|
+
else
|
340
|
+
puts "ā ļø Could not find </head> tag in layout"
|
341
|
+
puts " Please add manually to your layout:"
|
342
|
+
puts " <%= islands %>"
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# Ensure node_modules is in .gitignore
|
347
|
+
def ensure_node_modules_gitignored!
|
348
|
+
gitignore_path = File.join(Dir.pwd, '.gitignore')
|
349
|
+
|
350
|
+
unless File.exist?(gitignore_path)
|
351
|
+
puts "ā ļø .gitignore not found, creating one..."
|
352
|
+
gitignore_content = <<~GITIGNORE
|
353
|
+
/node_modules
|
354
|
+
GITIGNORE
|
355
|
+
File.write(gitignore_path, gitignore_content)
|
356
|
+
puts "ā Created .gitignore with /node_modules"
|
357
|
+
return
|
358
|
+
end
|
359
|
+
|
360
|
+
content = File.read(gitignore_path)
|
361
|
+
|
362
|
+
# Check if node_modules is already ignored (various patterns)
|
363
|
+
unless content.match?(/^\/node_modules\s*$/m) ||
|
364
|
+
content.match?(/^node_modules\/?\s*$/m) ||
|
365
|
+
content.match?(/^\*\*\/node_modules\/?\s*$/m)
|
366
|
+
content += "\n# IslandJS: Node.js dependencies\n/node_modules\n"
|
367
|
+
File.write(gitignore_path, content)
|
368
|
+
puts "ā Added /node_modules to .gitignore"
|
369
|
+
else
|
370
|
+
puts "ā .gitignore already configured for IslandjsRails"
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
def install_package!(package_name, version = nil)
|
375
|
+
# Get version from package.json
|
376
|
+
actual_version = version_for(package_name)
|
377
|
+
|
378
|
+
unless actual_version
|
379
|
+
raise IslandjsRails::PackageNotFoundError, "#{package_name} not found in package.json"
|
380
|
+
end
|
381
|
+
|
382
|
+
# Try to find working UMD URL
|
383
|
+
umd_url, global_name = find_working_umd_url(package_name, actual_version)
|
384
|
+
|
385
|
+
unless umd_url
|
386
|
+
raise IslandjsRails::UmdNotFoundError, "No UMD build found for #{package_name}@#{actual_version}. This package may not provide a UMD build."
|
387
|
+
end
|
388
|
+
|
389
|
+
# Download UMD content
|
390
|
+
umd_content = download_umd_content(umd_url)
|
391
|
+
|
392
|
+
# Create partial
|
393
|
+
create_partial_file(package_name, umd_content, global_name)
|
394
|
+
end
|
395
|
+
|
396
|
+
def download_umd_content(url)
|
397
|
+
uri = URI(url)
|
398
|
+
response = Net::HTTP.get_response(uri)
|
399
|
+
|
400
|
+
if response.code == '200'
|
401
|
+
response.body
|
402
|
+
else
|
403
|
+
raise IslandjsRails::Error, "Failed to download UMD from #{url}: #{response.code}"
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
def create_partial_file(package_name, island_content, global_name = nil)
|
408
|
+
partial_path = partial_path_for(package_name)
|
409
|
+
|
410
|
+
FileUtils.mkdir_p(File.dirname(partial_path))
|
411
|
+
|
412
|
+
partial_content = generate_partial_content(package_name, island_content, global_name)
|
413
|
+
|
414
|
+
File.write(partial_path, partial_content)
|
415
|
+
puts " ā Created partial: #{File.basename(partial_path)}"
|
416
|
+
end
|
417
|
+
|
418
|
+
def generate_partial_content(package_name, island_content, global_name = nil)
|
419
|
+
safe_name = package_name.gsub(/[@\/]/, '_').gsub(/-/, '_')
|
420
|
+
global_name ||= detect_global_name(package_name)
|
421
|
+
|
422
|
+
# Base64 encode the content to completely avoid ERB parsing issues
|
423
|
+
require 'base64'
|
424
|
+
encoded_content = Base64.strict_encode64(island_content)
|
425
|
+
|
426
|
+
<<~ERB
|
427
|
+
<%# #{global_name} UMD Library %>
|
428
|
+
<%# Global: #{global_name} %>
|
429
|
+
<%# Generated by IslandjsRails %>
|
430
|
+
<script type="text/javascript">
|
431
|
+
(function() {
|
432
|
+
var script = document.createElement('script');
|
433
|
+
script.text = atob('<%= "#{encoded_content}" %>');
|
434
|
+
document.head.appendChild(script);
|
435
|
+
document.head.removeChild(script);
|
436
|
+
})();
|
437
|
+
</script>
|
438
|
+
ERB
|
439
|
+
end
|
440
|
+
|
441
|
+
def package_json
|
442
|
+
return @package_json if @package_json
|
443
|
+
return nil unless File.exist?(configuration.package_json_path)
|
444
|
+
|
445
|
+
begin
|
446
|
+
@package_json = JSON.parse(File.read(configuration.package_json_path))
|
447
|
+
rescue JSON::ParserError
|
448
|
+
nil
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def installed_packages
|
453
|
+
package_data = package_json
|
454
|
+
return [] unless package_data
|
455
|
+
|
456
|
+
dependencies = package_data.dig('dependencies') || {}
|
457
|
+
dev_dependencies = package_data.dig('devDependencies') || {}
|
458
|
+
|
459
|
+
(dependencies.keys + dev_dependencies.keys).uniq
|
460
|
+
end
|
461
|
+
|
462
|
+
def supported_package?(package_name)
|
463
|
+
true
|
464
|
+
end
|
465
|
+
|
466
|
+
def partial_path_for(package_name)
|
467
|
+
partial_name = package_name.gsub(/[@\/]/, '_').gsub(/-/, '_')
|
468
|
+
configuration.partials_dir.join("_#{partial_name}.html.erb")
|
469
|
+
end
|
470
|
+
|
471
|
+
def download_and_create_partial!(package_name)
|
472
|
+
version = version_for(package_name)
|
473
|
+
|
474
|
+
# Try to find working UMD URL
|
475
|
+
umd_url, global_name = find_working_umd_url(package_name, version)
|
476
|
+
|
477
|
+
unless umd_url
|
478
|
+
puts " ā No UMD build found for #{package_name}@#{version}"
|
479
|
+
return
|
480
|
+
end
|
481
|
+
|
482
|
+
# Download UMD content
|
483
|
+
umd_content = download_umd_content(umd_url)
|
484
|
+
|
485
|
+
# Create partial
|
486
|
+
create_partial_file(package_name, umd_content, global_name)
|
487
|
+
|
488
|
+
puts " ā Created partial: #{partial_path_for(package_name)}"
|
489
|
+
end
|
490
|
+
|
491
|
+
def add_package_via_yarn(package_name, version = nil)
|
492
|
+
package_spec = version ? "#{package_name}@#{version}" : package_name
|
493
|
+
command = "yarn add #{package_spec}"
|
494
|
+
|
495
|
+
stdout, stderr, status = Open3.capture3(command, chdir: defined?(Rails) ? Rails.root : Dir.pwd)
|
496
|
+
|
497
|
+
unless status.success?
|
498
|
+
raise YarnError, "Failed to add #{package_spec}: #{stderr}"
|
499
|
+
end
|
500
|
+
|
501
|
+
@package_json = nil
|
502
|
+
puts " ā Added to package.json: #{package_spec}"
|
503
|
+
end
|
504
|
+
|
505
|
+
def yarn_update!(package_name, version = nil)
|
506
|
+
if version
|
507
|
+
add_package_via_yarn(package_name, version)
|
508
|
+
else
|
509
|
+
command = "yarn upgrade #{package_name}"
|
510
|
+
stdout, stderr, status = Open3.capture3(command, chdir: defined?(Rails) ? Rails.root : Dir.pwd)
|
511
|
+
|
512
|
+
unless status.success?
|
513
|
+
raise YarnError, "Failed to update #{package_name}: #{stderr}"
|
514
|
+
end
|
515
|
+
|
516
|
+
@package_json = nil
|
517
|
+
puts " ā Updated in package.json: #{package_name}"
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
def remove_package_via_yarn(package_name)
|
522
|
+
command = "yarn remove #{package_name}"
|
523
|
+
|
524
|
+
stdout, stderr, status = Open3.capture3(command, chdir: defined?(Rails) ? Rails.root : Dir.pwd)
|
525
|
+
|
526
|
+
unless status.success?
|
527
|
+
raise YarnError, "Failed to remove #{package_name}: #{stderr}"
|
528
|
+
end
|
529
|
+
|
530
|
+
@package_json = nil
|
531
|
+
puts " ā Removed from package.json: #{package_name}"
|
532
|
+
end
|
533
|
+
|
534
|
+
def generate_webpack_config!
|
535
|
+
copy_template_file('webpack.config.js', configuration.webpack_config_path)
|
536
|
+
end
|
537
|
+
|
538
|
+
def url_accessible?(url)
|
539
|
+
uri = URI(url)
|
540
|
+
response = Net::HTTP.get_response(uri)
|
541
|
+
response.code == '200'
|
542
|
+
rescue => e
|
543
|
+
false
|
544
|
+
end
|
545
|
+
|
546
|
+
def has_partial?(package_name)
|
547
|
+
File.exist?(partial_path_for(package_name))
|
548
|
+
end
|
549
|
+
|
550
|
+
def get_global_name_for_package(package_name)
|
551
|
+
detect_global_name(package_name)
|
552
|
+
end
|
553
|
+
|
554
|
+
def reset_webpack_externals
|
555
|
+
webpack_config_path = configuration.webpack_config_path
|
556
|
+
return unless File.exist?(webpack_config_path)
|
557
|
+
|
558
|
+
content = File.read(webpack_config_path)
|
559
|
+
|
560
|
+
externals_block = <<~JS
|
561
|
+
externals: {
|
562
|
+
// IslandjsRails managed externals - do not edit manually
|
563
|
+
},
|
564
|
+
JS
|
565
|
+
|
566
|
+
updated_content = content.gsub(
|
567
|
+
/externals:\s*\{[^}]*\}(?:,)?/m,
|
568
|
+
externals_block.chomp
|
569
|
+
)
|
570
|
+
|
571
|
+
File.write(webpack_config_path, updated_content)
|
572
|
+
puts " ā Reset webpack externals"
|
573
|
+
end
|
574
|
+
|
575
|
+
def update_webpack_externals(package_name = nil, global_name = nil)
|
576
|
+
webpack_config_path = configuration.webpack_config_path
|
577
|
+
return unless File.exist?(webpack_config_path)
|
578
|
+
|
579
|
+
content = File.read(webpack_config_path)
|
580
|
+
|
581
|
+
externals = {}
|
582
|
+
|
583
|
+
# Get installed packages from vendor manifest instead of partials
|
584
|
+
vendor_manager = IslandjsRails.vendor_manager
|
585
|
+
manifest = vendor_manager.send(:read_manifest)
|
586
|
+
|
587
|
+
manifest['libs'].each do |lib|
|
588
|
+
pkg = lib['name']
|
589
|
+
externals[pkg] = get_global_name_for_package(pkg)
|
590
|
+
end
|
591
|
+
|
592
|
+
externals_lines = externals.map { |pkg, global| " \"#{pkg}\": \"#{global}\"" }
|
593
|
+
externals_block = <<~JS
|
594
|
+
externals: {
|
595
|
+
// IslandjsRails managed externals - do not edit manually
|
596
|
+
#{externals_lines.join(",\n")}
|
597
|
+
},
|
598
|
+
JS
|
599
|
+
|
600
|
+
updated_content = content.gsub(
|
601
|
+
/externals:\s*\{[^}]*\}(?:,)?/m,
|
602
|
+
externals_block.chomp
|
603
|
+
)
|
604
|
+
|
605
|
+
File.write(webpack_config_path, updated_content)
|
606
|
+
puts " ā Updated webpack externals"
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|