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,394 @@
|
|
1
|
+
module IslandjsRails
|
2
|
+
module RailsHelpers
|
3
|
+
# Main helper method that combines all IslandJS functionality
|
4
|
+
def islands
|
5
|
+
output = []
|
6
|
+
output << island_partials # Now uses vendor UMD partial
|
7
|
+
output << island_bundle_script
|
8
|
+
output << umd_versions_debug if Rails.env.development?
|
9
|
+
output.compact.join("\n").html_safe
|
10
|
+
end
|
11
|
+
|
12
|
+
# Render all island partials (CDN scripts for external libraries)
|
13
|
+
# Now delegates to the vendor UMD partial for better performance
|
14
|
+
def island_partials
|
15
|
+
render(partial: "shared/islands/vendor_umd").html_safe
|
16
|
+
rescue ActionView::MissingTemplate
|
17
|
+
if Rails.env.development?
|
18
|
+
"<!-- IslandJS: Vendor UMD partial missing. Run: rails islandjs:init -->".html_safe
|
19
|
+
else
|
20
|
+
"".html_safe
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Render the main IslandJS bundle script tag
|
25
|
+
def island_bundle_script
|
26
|
+
manifest_path = Rails.root.join('public', 'islands_manifest.json')
|
27
|
+
bundle_path = '/islands_bundle.js'
|
28
|
+
|
29
|
+
unless File.exist?(manifest_path)
|
30
|
+
# Fallback to direct bundle path when no manifest
|
31
|
+
return html_safe_string("<script src=\"#{bundle_path}\" defer></script>")
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
manifest = JSON.parse(File.read(manifest_path))
|
36
|
+
# Look for islands_bundle.js in manifest
|
37
|
+
bundle_file = manifest['islands_bundle.js']
|
38
|
+
|
39
|
+
if bundle_file
|
40
|
+
html_safe_string("<script src=\"#{bundle_file}\" defer></script>")
|
41
|
+
else
|
42
|
+
# Fallback to direct bundle path
|
43
|
+
html_safe_string("<script src=\"#{bundle_path}\" defer></script>")
|
44
|
+
end
|
45
|
+
rescue JSON::ParserError
|
46
|
+
# Fallback to direct bundle path on manifest parse error
|
47
|
+
html_safe_string("<script src=\"#{bundle_path}\" defer></script>")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Mount a React component with props and Turbo-compatible lifecycle
|
52
|
+
def react_component(component_name, props = {}, options = {})
|
53
|
+
# Generate component ID - use custom container_id if provided
|
54
|
+
if options[:container_id]
|
55
|
+
component_id = options[:container_id]
|
56
|
+
else
|
57
|
+
component_id = "react-#{component_name.gsub(/([A-Z])/, '-\1').downcase.gsub(/^-/, '')}-#{SecureRandom.hex(4)}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Extract options
|
61
|
+
tag_name = options[:tag] || 'div'
|
62
|
+
css_class = options[:class] || ''
|
63
|
+
namespace = options[:namespace] || 'window.islandjsRails'
|
64
|
+
|
65
|
+
# For turbo-cache compatibility, store initial state as JSON in data attribute
|
66
|
+
initial_state_json = props.to_json
|
67
|
+
|
68
|
+
# Generate data attributes from props with proper HTML escaping (keeping for backward compatibility)
|
69
|
+
data_attrs = props.map do |key, value|
|
70
|
+
# Convert both camelCase and snake_case to kebab-case
|
71
|
+
attr_name = key.to_s.gsub(/([A-Z])/, '-\1').gsub('_', '-').downcase.gsub(/^-/, '')
|
72
|
+
# Properly escape HTML entities
|
73
|
+
attr_value = if value.nil?
|
74
|
+
''
|
75
|
+
else
|
76
|
+
value.to_s.gsub('&', '&').gsub('<', '<').gsub('>', '>').gsub('"', '"')
|
77
|
+
end
|
78
|
+
"data-#{attr_name}=\"#{attr_value}\""
|
79
|
+
end.join(' ')
|
80
|
+
|
81
|
+
# Generate optional chaining syntax for custom namespaces
|
82
|
+
namespace_with_optional = if namespace != 'window.islandjsRails' && !namespace.include?('?')
|
83
|
+
namespace + '?'
|
84
|
+
else
|
85
|
+
namespace
|
86
|
+
end
|
87
|
+
|
88
|
+
# Generate the mounting script - pass container_id as the only prop for turbo-cache pattern
|
89
|
+
mount_script = generate_react_mount_script(component_name, component_id, namespace, namespace_with_optional)
|
90
|
+
|
91
|
+
# Return the container div with data-initial-state and script
|
92
|
+
data_part = data_attrs.empty? ? '' : " #{data_attrs}"
|
93
|
+
class_part = css_class.empty? ? '' : " class=\"#{css_class}\""
|
94
|
+
|
95
|
+
# Add data-initial-state for turbo-cache compatibility
|
96
|
+
initial_state_attr = " data-initial-state=\"#{initial_state_json.gsub('"', '"')}\""
|
97
|
+
|
98
|
+
container_html = "<#{tag_name} id=\"#{component_id}\"#{class_part}#{data_part}#{initial_state_attr}></#{tag_name}>"
|
99
|
+
|
100
|
+
html_safe_string("#{container_html}\n#{mount_script}")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Mount a Vue component with props and Turbo-compatible lifecycle
|
104
|
+
def vue_component(component_name, props = {}, options = {})
|
105
|
+
# Generate unique ID for this component instance
|
106
|
+
component_id = "vue-#{component_name.downcase}-#{SecureRandom.hex(4)}"
|
107
|
+
|
108
|
+
# Prepare props as JSON
|
109
|
+
props_json = props.to_json
|
110
|
+
|
111
|
+
# Extract options
|
112
|
+
tag_name = options[:tag] || 'div'
|
113
|
+
css_class = options[:class] || ''
|
114
|
+
|
115
|
+
# Generate the mounting script
|
116
|
+
mount_script = generate_vue_mount_script(component_name, component_id, props_json)
|
117
|
+
|
118
|
+
# Return the container div and script
|
119
|
+
container_html = "<#{tag_name} id=\"#{component_id}\" class=\"#{css_class}\"></#{tag_name}>"
|
120
|
+
|
121
|
+
html_safe_string("#{container_html}\n#{mount_script}")
|
122
|
+
end
|
123
|
+
|
124
|
+
# Generic island component helper
|
125
|
+
def island_component(framework, component_name, props = {}, options = {})
|
126
|
+
case framework.to_s.downcase
|
127
|
+
when 'react'
|
128
|
+
react_component(component_name, props, options)
|
129
|
+
when 'vue'
|
130
|
+
vue_component(component_name, props, options)
|
131
|
+
else
|
132
|
+
html_safe_string("<!-- Unsupported framework: #{framework} -->")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Debug helper to show available components
|
137
|
+
def island_debug
|
138
|
+
return '' unless Rails.env.development?
|
139
|
+
|
140
|
+
debug_info = {
|
141
|
+
bundle_path: find_bundle_path,
|
142
|
+
partials_count: Dir.glob(File.join(IslandjsRails.configuration.partials_dir, '*.html.erb')).count,
|
143
|
+
webpack_config_exists: File.exist?(IslandjsRails.configuration.webpack_config_path),
|
144
|
+
package_json_exists: File.exist?(IslandjsRails.configuration.package_json_path)
|
145
|
+
}
|
146
|
+
|
147
|
+
debug_html = <<~HTML
|
148
|
+
<div style="background: #f0f0f0; padding: 10px; margin: 10px 0; border: 1px solid #ccc; font-family: monospace; font-size: 12px;">
|
149
|
+
<strong>🏝️ IslandJS Debug Info:</strong><br>
|
150
|
+
Bundle Path: #{debug_info[:bundle_path] || 'Not found'}<br>
|
151
|
+
Partials: #{debug_info[:partials_count]} found<br>
|
152
|
+
Webpack Config: #{debug_info[:webpack_config_exists] ? '✓' : '✗'}<br>
|
153
|
+
Package.json: #{debug_info[:package_json_exists] ? '✓' : '✗'}
|
154
|
+
</div>
|
155
|
+
HTML
|
156
|
+
|
157
|
+
html_safe_string(debug_html)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Legacy UMD helper methods for backward compatibility with tests
|
161
|
+
def umd_versions_debug
|
162
|
+
return unless Rails.env.development?
|
163
|
+
|
164
|
+
begin
|
165
|
+
installed = IslandjsRails.core.send(:installed_packages)
|
166
|
+
supported = installed.select { |pkg| IslandjsRails.core.send(:supported_package?, pkg) }
|
167
|
+
|
168
|
+
if supported.empty?
|
169
|
+
return %(<div style="position: fixed; bottom: 10px; right: 10px; background: #666; color: #fff; padding: 5px; font-size: 10px; z-index: 9999;">UMD: No packages</div>).html_safe
|
170
|
+
end
|
171
|
+
|
172
|
+
versions = supported.map do |package_name|
|
173
|
+
begin
|
174
|
+
version = IslandjsRails.version_for(package_name)
|
175
|
+
"#{package_name}: #{version}"
|
176
|
+
rescue
|
177
|
+
"#{package_name}: error"
|
178
|
+
end
|
179
|
+
end.join(', ')
|
180
|
+
|
181
|
+
%(<div style="position: fixed; bottom: 10px; right: 10px; background: #000; color: #fff; padding: 5px; font-size: 10px; z-index: 9999;">UMD: #{versions}</div>).html_safe
|
182
|
+
rescue => e
|
183
|
+
%(<div style="position: fixed; bottom: 10px; right: 10px; background: #f00; color: #fff; padding: 5px; font-size: 10px; z-index: 9999;">UMD Error: #{e.message}</div>).html_safe
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def umd_partial_for(package_name)
|
188
|
+
# Backward compatibility: delegate to vendor UMD partial
|
189
|
+
# Individual package partials are no longer used
|
190
|
+
if Rails.env.development?
|
191
|
+
"<!-- IslandJS: umd_partial_for('#{package_name}') is deprecated. Use island_partials or render 'shared/islands/vendor_umd' instead -->".html_safe
|
192
|
+
else
|
193
|
+
# In production, silently delegate to vendor partial
|
194
|
+
render(partial: "shared/islands/vendor_umd").html_safe
|
195
|
+
end
|
196
|
+
rescue ActionView::MissingTemplate
|
197
|
+
if Rails.env.development?
|
198
|
+
"<!-- IslandJS: Vendor UMD partial missing. Run: rails islandjs:init -->".html_safe
|
199
|
+
else
|
200
|
+
"".html_safe
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def react_partials
|
205
|
+
packages = ['react', 'react-dom']
|
206
|
+
partials = packages.map { |pkg| umd_partial_for(pkg) }.compact.join("\n")
|
207
|
+
html_safe_string(partials)
|
208
|
+
end
|
209
|
+
|
210
|
+
private
|
211
|
+
|
212
|
+
# Find the bundle file path (with manifest support)
|
213
|
+
def find_bundle_path
|
214
|
+
# Try manifest first (production)
|
215
|
+
manifest_path = Rails.root.join('public', 'islands_manifest.json')
|
216
|
+
|
217
|
+
if File.exist?(manifest_path)
|
218
|
+
begin
|
219
|
+
manifest = JSON.parse(File.read(manifest_path))
|
220
|
+
# Look for islands_bundle in manifest
|
221
|
+
bundle_key = manifest.keys.find { |key| key.include?('islands_bundle') }
|
222
|
+
return "/#{manifest[bundle_key]}" if bundle_key && manifest[bundle_key]
|
223
|
+
rescue JSON::ParserError
|
224
|
+
# Fall through to direct file check
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Try direct file (development)
|
229
|
+
direct_bundle_path = Rails.root.join('public', 'islands_bundle.js')
|
230
|
+
if File.exist?(direct_bundle_path)
|
231
|
+
return '/islands_bundle.js'
|
232
|
+
end
|
233
|
+
|
234
|
+
# Bundle not found
|
235
|
+
nil
|
236
|
+
end
|
237
|
+
|
238
|
+
# Generate React component mounting script with Turbo compatibility
|
239
|
+
def generate_react_mount_script(component_name, component_id, namespace, namespace_with_optional)
|
240
|
+
<<~JAVASCRIPT
|
241
|
+
<script>
|
242
|
+
(function() {
|
243
|
+
function mount#{component_name}() {
|
244
|
+
if (typeof #{namespace_with_optional} === 'undefined' || !#{namespace_with_optional}.#{component_name}) {
|
245
|
+
console.warn('IslandJS: #{component_name} component not found. Make sure it\\'s exported in your bundle.');
|
246
|
+
return;
|
247
|
+
}
|
248
|
+
|
249
|
+
if (typeof React === 'undefined' || typeof window.ReactDOM === 'undefined') {
|
250
|
+
console.warn('IslandJS: React or ReactDOM not loaded. Install with: rails "islandjs:install[react]" and rails "islandjs:install[react-dom]"');
|
251
|
+
return;
|
252
|
+
}
|
253
|
+
|
254
|
+
const container = document.getElementById('#{component_id}');
|
255
|
+
if (!container) return;
|
256
|
+
|
257
|
+
const props = { containerId: '#{component_id}' };
|
258
|
+
const element = React.createElement(#{namespace_with_optional}.#{component_name}, props);
|
259
|
+
|
260
|
+
// Use React 18 createRoot if available, fallback to React 17 render
|
261
|
+
if (window.ReactDOM.createRoot) {
|
262
|
+
if (!container._reactRoot) {
|
263
|
+
container._reactRoot = window.ReactDOM.createRoot(container);
|
264
|
+
}
|
265
|
+
container._reactRoot.render(element);
|
266
|
+
} else {
|
267
|
+
window.ReactDOM.render(element, container);
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
function cleanup#{component_name}() {
|
272
|
+
const container = document.getElementById('#{component_id}');
|
273
|
+
if (!container) return;
|
274
|
+
|
275
|
+
// React 18 unmount
|
276
|
+
if (container._reactRoot) {
|
277
|
+
container._reactRoot.unmount();
|
278
|
+
container._reactRoot = null;
|
279
|
+
} else if (typeof window.ReactDOM !== 'undefined' && window.ReactDOM.unmountComponentAtNode) {
|
280
|
+
// React 17 unmount
|
281
|
+
window.ReactDOM.unmountComponentAtNode(container);
|
282
|
+
}
|
283
|
+
}
|
284
|
+
|
285
|
+
// Mount on page load and Turbo navigation
|
286
|
+
if (document.readyState === 'loading') {
|
287
|
+
document.addEventListener('DOMContentLoaded', mount#{component_name});
|
288
|
+
} else {
|
289
|
+
mount#{component_name}();
|
290
|
+
}
|
291
|
+
|
292
|
+
// Turbo compatibility
|
293
|
+
document.addEventListener('turbo:load', mount#{component_name});
|
294
|
+
document.addEventListener('turbo:before-cache', cleanup#{component_name});
|
295
|
+
document.addEventListener('turbo:render', mount#{component_name});
|
296
|
+
document.addEventListener('turbo:before-render', cleanup#{component_name});
|
297
|
+
|
298
|
+
// Legacy Turbolinks compatibility
|
299
|
+
document.addEventListener('turbolinks:load', mount#{component_name});
|
300
|
+
document.addEventListener('turbolinks:before-cache', cleanup#{component_name});
|
301
|
+
})();
|
302
|
+
</script>
|
303
|
+
JAVASCRIPT
|
304
|
+
end
|
305
|
+
|
306
|
+
# Generate Vue component mounting script with Turbo compatibility
|
307
|
+
def generate_vue_mount_script(component_name, component_id, props_json)
|
308
|
+
<<~JAVASCRIPT
|
309
|
+
<script>
|
310
|
+
(function() {
|
311
|
+
let vueApp = null;
|
312
|
+
|
313
|
+
function mount#{component_name}() {
|
314
|
+
if (typeof window.islandjsRails === 'undefined' || !window.islandjsRails.#{component_name}) {
|
315
|
+
console.warn('IslandJS: #{component_name} component not found. Make sure it\\'s exported in your bundle.');
|
316
|
+
return;
|
317
|
+
}
|
318
|
+
|
319
|
+
if (typeof Vue === 'undefined') {
|
320
|
+
console.warn('IslandJS: Vue not loaded. Install with: rails "islandjs:install[vue]"');
|
321
|
+
return;
|
322
|
+
}
|
323
|
+
|
324
|
+
const container = document.getElementById('#{component_id}');
|
325
|
+
if (!container) return;
|
326
|
+
|
327
|
+
const props = #{props_json};
|
328
|
+
|
329
|
+
// Vue 3 syntax
|
330
|
+
if (Vue.createApp) {
|
331
|
+
vueApp = Vue.createApp({
|
332
|
+
render() {
|
333
|
+
return Vue.h(window.islandjsRails.#{component_name}, props);
|
334
|
+
}
|
335
|
+
});
|
336
|
+
vueApp.mount('##{component_id}');
|
337
|
+
} else {
|
338
|
+
// Vue 2 syntax
|
339
|
+
vueApp = new Vue({
|
340
|
+
el: '##{component_id}',
|
341
|
+
render: function(h) {
|
342
|
+
return h(window.islandjsRails.#{component_name}, { props: props });
|
343
|
+
}
|
344
|
+
});
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
348
|
+
function unmount#{component_name}() {
|
349
|
+
if (vueApp) {
|
350
|
+
if (vueApp.unmount) {
|
351
|
+
// Vue 3
|
352
|
+
vueApp.unmount();
|
353
|
+
} else if (vueApp.$destroy) {
|
354
|
+
// Vue 2
|
355
|
+
vueApp.$destroy();
|
356
|
+
}
|
357
|
+
vueApp = null;
|
358
|
+
}
|
359
|
+
}
|
360
|
+
|
361
|
+
// Mount on page load and Turbo navigation
|
362
|
+
if (document.readyState === 'loading') {
|
363
|
+
document.addEventListener('DOMContentLoaded', mount#{component_name});
|
364
|
+
} else {
|
365
|
+
mount#{component_name}();
|
366
|
+
}
|
367
|
+
|
368
|
+
// Turbo compatibility
|
369
|
+
document.addEventListener('turbo:load', mount#{component_name});
|
370
|
+
document.addEventListener('turbo:before-cache', unmount#{component_name});
|
371
|
+
|
372
|
+
// Legacy Turbolinks compatibility
|
373
|
+
document.addEventListener('turbolinks:load', mount#{component_name});
|
374
|
+
document.addEventListener('turbolinks:before-cache', unmount#{component_name});
|
375
|
+
})();
|
376
|
+
</script>
|
377
|
+
JAVASCRIPT
|
378
|
+
end
|
379
|
+
|
380
|
+
# Cross-Rails version html_safe compatibility
|
381
|
+
def html_safe_string(string)
|
382
|
+
if string.respond_to?(:html_safe)
|
383
|
+
string.html_safe
|
384
|
+
else
|
385
|
+
string
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# Auto-include in ActionView if Rails is present
|
392
|
+
if defined?(ActionView::Base)
|
393
|
+
ActionView::Base.include IslandjsRails::RailsHelpers
|
394
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rails/railtie'
|
2
|
+
|
3
|
+
module IslandjsRails
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
railtie_name :islandjs_rails
|
6
|
+
|
7
|
+
rake_tasks do
|
8
|
+
load File.expand_path('tasks.rb', __dir__)
|
9
|
+
end
|
10
|
+
|
11
|
+
initializer 'islandjs_rails.helpers' do
|
12
|
+
ActiveSupport.on_load(:action_view) do
|
13
|
+
include IslandjsRails::RailsHelpers
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Development-only warnings and checks
|
18
|
+
initializer 'islandjs_rails.development_warnings', after: :load_config_initializers do
|
19
|
+
if Rails.env.development?
|
20
|
+
# Check for common setup issues
|
21
|
+
Rails.application.config.after_initialize do
|
22
|
+
check_development_setup
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def check_development_setup
|
30
|
+
# Check if package.json exists
|
31
|
+
unless File.exist?(Rails.root.join('package.json'))
|
32
|
+
Rails.logger.warn "IslandJS: package.json not found. Run 'rails islandjs:init' to set up."
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check if webpack config exists
|
37
|
+
unless File.exist?(Rails.root.join('webpack.config.js'))
|
38
|
+
Rails.logger.warn "IslandJS: webpack.config.js not found. Run 'rails islandjs:init' to set up."
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
# Check if yarn is available
|
43
|
+
unless system('which yarn > /dev/null 2>&1')
|
44
|
+
Rails.logger.warn "IslandJS: yarn not found. Please install yarn for package management."
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
# Check if essential webpack dependencies are installed
|
49
|
+
essential_deps = ['webpack', 'webpack-cli', '@babel/core']
|
50
|
+
missing_deps = essential_deps.select do |dep|
|
51
|
+
!system("yarn list #{dep} > /dev/null 2>&1")
|
52
|
+
end
|
53
|
+
|
54
|
+
unless missing_deps.empty?
|
55
|
+
Rails.logger.warn "IslandJS: Missing dependencies: #{missing_deps.join(', ')}. Run 'rails islandjs:init' to install."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
namespace :islandjs do
|
4
|
+
desc "Initialize IslandJS in this Rails project"
|
5
|
+
task :init => :environment do
|
6
|
+
IslandjsRails.init!
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Install a JavaScript island package"
|
10
|
+
task :install, [:package_name, :version] => :environment do |t, args|
|
11
|
+
package_name = args[:package_name]
|
12
|
+
version = args[:version]
|
13
|
+
|
14
|
+
if package_name.nil?
|
15
|
+
puts "❌ Package name is required"
|
16
|
+
puts "Usage: rails \"islandjs:install[react,18.3.1]\""
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
IslandjsRails.install!(package_name, version)
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Update a JavaScript island package"
|
24
|
+
task :update, [:package_name, :version] => :environment do |t, args|
|
25
|
+
package_name = args[:package_name]
|
26
|
+
version = args[:version]
|
27
|
+
|
28
|
+
if package_name.nil?
|
29
|
+
puts "❌ Package name is required"
|
30
|
+
puts "Usage: rails \"islandjs:update[react,18.3.1]\""
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
IslandjsRails.update!(package_name, version)
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Remove a JavaScript island package"
|
38
|
+
task :remove, [:package_name] => :environment do |t, args|
|
39
|
+
package_name = args[:package_name]
|
40
|
+
|
41
|
+
if package_name.nil?
|
42
|
+
puts "❌ Package name is required"
|
43
|
+
puts "Usage: rails \"islandjs:remove[react]\""
|
44
|
+
exit 1
|
45
|
+
end
|
46
|
+
|
47
|
+
IslandjsRails.remove!(package_name)
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Sync all JavaScript island packages with current package.json"
|
51
|
+
task :sync => :environment do
|
52
|
+
IslandjsRails.sync!
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Show status of all JavaScript island packages"
|
56
|
+
task :status => :environment do
|
57
|
+
IslandjsRails.status!
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "Clean all island partials and reset webpack externals"
|
61
|
+
task :clean => :environment do
|
62
|
+
IslandjsRails.clean!
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "Show IslandJS configuration"
|
66
|
+
task :config => :environment do
|
67
|
+
config = IslandjsRails.configuration
|
68
|
+
puts "📊 IslandjsRails Configuration"
|
69
|
+
puts "=" * 40
|
70
|
+
puts "Package.json path: #{config.package_json_path}"
|
71
|
+
puts "Partials directory: #{config.partials_dir}"
|
72
|
+
puts "Webpack config path: #{config.webpack_config_path}"
|
73
|
+
puts "Supported CDNs: #{config.supported_cdns.join(', ')}"
|
74
|
+
puts "Built-in global name overrides: #{IslandjsRails::BUILT_IN_GLOBAL_NAME_OVERRIDES.size} available"
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "Show IslandJS version"
|
78
|
+
task :version do
|
79
|
+
puts "IslandjsRails #{IslandjsRails::VERSION}"
|
80
|
+
end
|
81
|
+
|
82
|
+
namespace :vendor do
|
83
|
+
desc "Rebuild combined vendor bundle (for :external_combined mode)"
|
84
|
+
task :rebuild_combined => :environment do
|
85
|
+
IslandjsRails.vendor_manager.rebuild_combined_bundle!
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "Show vendor configuration and status"
|
89
|
+
task :status => :environment do
|
90
|
+
config = IslandjsRails.configuration
|
91
|
+
puts "📦 IslandJS Vendor Status"
|
92
|
+
puts "=" * 40
|
93
|
+
puts "Mode: #{config.vendor_script_mode}"
|
94
|
+
puts "Vendor directory: #{config.vendor_dir}"
|
95
|
+
puts "Combined basename: #{config.combined_basename}"
|
96
|
+
puts "Vendor order: #{config.vendor_order.join(', ')}"
|
97
|
+
|
98
|
+
# Show manifest info
|
99
|
+
manifest_path = config.vendor_manifest_path
|
100
|
+
if File.exist?(manifest_path)
|
101
|
+
require 'json'
|
102
|
+
manifest = JSON.parse(File.read(manifest_path))
|
103
|
+
puts "\nInstalled libraries: #{manifest['libs'].length}"
|
104
|
+
manifest['libs'].each do |lib|
|
105
|
+
puts " • #{lib['name']}@#{lib['version']} (#{lib['file']})"
|
106
|
+
end
|
107
|
+
|
108
|
+
if manifest['combined']
|
109
|
+
puts "\nCombined bundle: #{manifest['combined']['file']} (#{manifest['combined']['size_kb']}KB)"
|
110
|
+
end
|
111
|
+
else
|
112
|
+
puts "\nNo vendor manifest found"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
|